diff --git a/.github/workflows/Android.yml b/.github/workflows/Android.yml index 148b6d83b..17e5cb9bf 100644 --- a/.github/workflows/Android.yml +++ b/.github/workflows/Android.yml @@ -34,7 +34,8 @@ jobs: - name: Build debug APK run: | export ANDROID_NDK=/home/runner/android-env/android-ndk-r14b - export ANDROID_NDK_ROOT=/home/runner/android-env/android-ndk-r14b + export ANDROID_NDK_ROOT=${ANDROID_NDK} + export ANDROID_NDK_HOME=${ANDROID_NDK} echo "y" | sudo apt-get install ninja-build cd $GITHUB_WORKSPACE/platform/Android/source export ANDROID_FULL_PACKAGE='true' diff --git a/.github/workflows/iOS.yml b/.github/workflows/iOS.yml index 920868795..45795e5b0 100644 --- a/.github/workflows/iOS.yml +++ b/.github/workflows/iOS.yml @@ -12,17 +12,10 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2 - - name: build external + - name: build SDK run: | git config --global user.email "you@example.com" git config --global user.name "Your Name" . setup.env . build_player.sh build_iOS - - - name: build SDK - run: | - cd platform/Apple/demo/iOS - ./genxcodeproj.sh - cd SDK - xcodebuild -scheme ALL_BUILD ONLY_ACTIVE_ARCH=NO -configuration MinSizeRel -sdk iphoneos VALID_ARCHS="armv7 arm64" diff --git a/.github/workflows/macOS.yml b/.github/workflows/macOS.yml index abb0bbb97..d5a3ce33e 100644 --- a/.github/workflows/macOS.yml +++ b/.github/workflows/macOS.yml @@ -26,7 +26,7 @@ jobs: ./Genxcodeproj.sh cd CicadaDemo xcodebuild -workspace CicadaDemo.xcworkspace -scheme install ONLY_ACTIVE_ARCH=NO -configuration Release - xcodebuild -workspace CicadaDemo.xcworkspace -scheme CicadaDemo ONLY_ACTIVE_ARCH=NO -configuration Release + xcodebuild -workspace CicadaDemo.xcworkspace -scheme CicadaDemo ONLY_ACTIVE_ARCH=NO -configuration Release VALID_ARCHS="x86_64" - name: build tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 2026beb06..ac54af849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,214 @@ +# [](https://github.com/alibaba/CicadaPlayer/compare/v0.2.1...v) (2020-10-29) + + + +## [0.2.1](https://github.com/alibaba/CicadaPlayer/compare/d44002c85b0f45eaa4c497d534b91dfa39aa1ac0...v0.2.1) (2020-10-10) + + +### Bug Fixes + +* **activedecoder:** use AF_PKT_FLAG_KEY to detect a key frame ([cd6df81](https://github.com/alibaba/CicadaPlayer/commit/cd6df81baee431edf06a6c51615a625f97233dac)) +* **android:** change UPDATE_CURRENT_POSITION value not zero , will conflict with post message ([cb9389d](https://github.com/alibaba/CicadaPlayer/commit/cb9389d1263ff5ee8fe90af82cc05bd636b521e2)) +* **AppleVideoToolBox:** fix err in backgroud recoverage ([2129643](https://github.com/alibaba/CicadaPlayer/commit/2129643c8169d6895b20848af94259f0c81adae7)) +* **audioTrackRender:** flush audioTrack before overflow ([a1c7682](https://github.com/alibaba/CicadaPlayer/commit/a1c7682c2c3a4acf31984f3c4391f310bd0523c7)) +* **audioTrackRender:** make thread safe ([93d4fa7](https://github.com/alibaba/CicadaPlayer/commit/93d4fa7337be3242de33715604d4b4e88787a10f)) +* **avformatdemuxer:** reset the avio status after interrupt ([29e3857](https://github.com/alibaba/CicadaPlayer/commit/29e385748b4d4c8e798078e516c3b7b2686ef986)) +* **avformatdemuxer:** try to get start_time when invalid ([80f2436](https://github.com/alibaba/CicadaPlayer/commit/80f2436dff8ea5ace6ede587d8ccbe91433bc6c3)) +* **cache:** add cache result callback. ([3ec4d21](https://github.com/alibaba/CicadaPlayer/commit/3ec4d216a1fa7bf1d7629360f1bb8a06448e388b)) +* **cache:** delete tmp file when stop ([35b6674](https://github.com/alibaba/CicadaPlayer/commit/35b6674b2a9361fe2ae34775d0bbeb46195b9a39)) +* **cache:** pass StreamMeta vector pointer ([31813cc](https://github.com/alibaba/CicadaPlayer/commit/31813cc8a11d9fc51c109ddee29ec994e4f89544)) +* **cache:** stop cache when seek in cache ([fecf86a](https://github.com/alibaba/CicadaPlayer/commit/fecf86aede52961fc9f79f3643b84b31e1faa5b4)) +* **cacheModule:** fix memleak ([962e7ac](https://github.com/alibaba/CicadaPlayer/commit/962e7acb2e50123e89cf8327ee3e2e52aa9b8f41)) +* **curl_data_source:** set file size to 0 when file size unknown ([75879e7](https://github.com/alibaba/CicadaPlayer/commit/75879e74352f0fb7ec7a97de24a294981b54e39d)) +* **decoder:** hardware decoder not support if format is yuv422 ([6fecf02](https://github.com/alibaba/CicadaPlayer/commit/6fecf0270d29255258d4eaf905ef32c90ee7905e)) +* **demuxer:** avoid access null mCtx->pb ([71f6d80](https://github.com/alibaba/CicadaPlayer/commit/71f6d80825f2d1b7f5f22a42bbd4726b125e8e21)) +* **demuxer:** Prevent StreamMeta being nullptr ([036f137](https://github.com/alibaba/CicadaPlayer/commit/036f13747787a8e1c4c025589780f171bc3d7837)) +* **ffmpeg_utils:** use dar in stream first ([d02a118](https://github.com/alibaba/CicadaPlayer/commit/d02a118e7da641334f022a9b9c754b16ffc447e1)) +* **filteraudiorender:** calculate the out frame nb_samples ([ba126a9](https://github.com/alibaba/CicadaPlayer/commit/ba126a9d5bf5bbcd3b928d4f959d2088bb95edf1)) +* **frameCb:** cache depends on this callback. need find another way ([09637b0](https://github.com/alibaba/CicadaPlayer/commit/09637b0538084b3e468d36529d83992346e60655)) +* **GLRender:** call glUseProgram when program changed ([6020c0e](https://github.com/alibaba/CicadaPlayer/commit/6020c0eca7a67c535ab047a6100a710cd9444f60)) +* **GLRender:** drop a frame when view is null ([6b3125a](https://github.com/alibaba/CicadaPlayer/commit/6b3125a541f12636aac7eb3957894d7dab0fef7e)) +* **GLRender:** null point crash ([589c07c](https://github.com/alibaba/CicadaPlayer/commit/589c07c1ad93cdf3ca8c492b951b1ef1e4cd9cf6)) +* **hlsstream:** don't send error when seek pos= duration ([e996d96](https://github.com/alibaba/CicadaPlayer/commit/e996d96cb801f8dab02aff449f6ffe041f8efbb6)) +* **hlsstream:** start form the right index when live switching ([5293f19](https://github.com/alibaba/CicadaPlayer/commit/5293f19336412239f47fc2ae049ed4a8e0c5b1e7)) +* **ios:** use UIApplicationStateBackground to detact bg status ([cab40e5](https://github.com/alibaba/CicadaPlayer/commit/cab40e523b29d49875e0e45d4d7a04348f3db2b1)) +* **jni:** delete local ref ([01cc6e0](https://github.com/alibaba/CicadaPlayer/commit/01cc6e07baa64cebb74c168e797570bdaaa739f0)) +* **mediacodec:** delete jni local reference ([5c86ed6](https://github.com/alibaba/CicadaPlayer/commit/5c86ed69fb84ab8815d39b81f71a45fa5df4240e)) +* **mediacodec:** reset width and height to videoInfo ([400049c](https://github.com/alibaba/CicadaPlayer/commit/400049c53ea2effa41a401a3ad3b58db59064d10)) +* **mediaplayer:** clear message after flush video render ([e911189](https://github.com/alibaba/CicadaPlayer/commit/e911189940b61103c0e6a1faf0caafa996a99ad8)) +* **mediaplayer:** fix miss out start message ([8ce5215](https://github.com/alibaba/CicadaPlayer/commit/8ce521505f1cec361a9d0ec32f5250d5d4e12bf2)) +* **OESRender:** gl background be set to black ([fcda864](https://github.com/alibaba/CicadaPlayer/commit/fcda864ac36c78c2c4df49932fcc33ccc4f592fb)) +* **player:** don't wait for video frame if in background ([4bda5ba](https://github.com/alibaba/CicadaPlayer/commit/4bda5ba18ed535d34314fab9198b8ccb0020c48a)) +* **render:** block surface changed before render finished ([8c68833](https://github.com/alibaba/CicadaPlayer/commit/8c688333b6bd72664c726b8a279e4b3d9d5e7c3c)) +* **render:** change int to float to avoid precision loss ([526f199](https://github.com/alibaba/CicadaPlayer/commit/526f199bedf47d5d0920ff6ba58442cb16dfac3c)) +* **render:** make sure video render be paused in background ([1802dd5](https://github.com/alibaba/CicadaPlayer/commit/1802dd563cad20018d15b0d9a71dceab2e7220fe)) +* **render:** update video background color if changed when pause ([0becd1e](https://github.com/alibaba/CicadaPlayer/commit/0becd1e3d1a8f6e3921a3affaeb06c72d85412d3)) +* **SdlAFAudioRender:** calculate the frame duration ([7c48f5e](https://github.com/alibaba/CicadaPlayer/commit/7c48f5e5d60b5c429d2f11bc6beebe53bc41d931)) +* **subtitleDemuxer:** set current to begin at first get subtitle packet ([cde8f98](https://github.com/alibaba/CicadaPlayer/commit/cde8f98e6db4b60e2927840cfa660c44b8474a15)) +* **subTitlePlayer:** wait the adding finished on exit ([0783c6a](https://github.com/alibaba/CicadaPlayer/commit/0783c6a542c4af55b041db92b3acd37511f5fa84)) +* **supermediaplayer:** fix pos error on seek ([2f76533](https://github.com/alibaba/CicadaPlayer/commit/2f76533b61a92b556f20a4c4ca21a6760e930778)) +* **supermediaplayer:** fix pos jump when accurate seek ([a991eb3](https://github.com/alibaba/CicadaPlayer/commit/a991eb3e9414a0bfdf5e7130c9f0721c964450db)) +* **supermediaplayer:** fix report error logic ([ae1b38e](https://github.com/alibaba/CicadaPlayer/commit/ae1b38e983ccb343d2fe4c24c865f3521339ad03)) +* **supermediaplayer:** fix wrong deltaTimeTmp increase logic ([5f8cfb6](https://github.com/alibaba/CicadaPlayer/commit/5f8cfb6fda9ffa50f27ee8a3d53590bcf14be58a)) +* **supermediaplayer:** restart the audio render on create ([c9be856](https://github.com/alibaba/CicadaPlayer/commit/c9be856ceda00fbc79e23e1823147e1a36513636)) +* **supermediaplayer:** update position when seek end ([4a72906](https://github.com/alibaba/CicadaPlayer/commit/4a72906531e01792e427ecd904ef2d2aaf39d653)) +* **supermediaplayer:** use position to seek subplayer ([73ec407](https://github.com/alibaba/CicadaPlayer/commit/73ec4072bc81a4ddb04e303c24891e5137d4ce09)) +* **superMediaPlayer:** add lock when call GetProperty ([647c08b](https://github.com/alibaba/CicadaPlayer/commit/647c08b7176bf4d8a3a8fc8ce614a09923e3a864)) +* check audio EOS work around ([357c0a7](https://github.com/alibaba/CicadaPlayer/commit/357c0a78a41b5a8299ecd76391140e2551f5b6df)) +* **ActiveDecoder:** drop logic effective hevc only ([5372c05](https://github.com/alibaba/CicadaPlayer/commit/5372c056a7aca73d3e37a339d9cc1115094f1c90)) +* **ActiveDecoder:** drop the CORRUPT packet ([d40fc47](https://github.com/alibaba/CicadaPlayer/commit/d40fc4737bb013eef4e8f9f6587d6fb61c7ef232)) +* **AppleVideoToolBox:** fix dead lock when bg ([c822566](https://github.com/alibaba/CicadaPlayer/commit/c82256695174bc2f6ecfecd8042d7fd580001069)) +* **AppleVideoToolBox:** fix reorder hevc pts ([80887fd](https://github.com/alibaba/CicadaPlayer/commit/80887fda64561fc98dddfa60246ba4c363c25878)) +* **av1:** disable av1 build ([8d571db](https://github.com/alibaba/CicadaPlayer/commit/8d571db19eed995a0315869615b252ad364fec9a)) +* **avcodecDecoder:** update pts on decode ([b68426b](https://github.com/alibaba/CicadaPlayer/commit/b68426b97c04a5c1e8856a862211422203d6f079)) +* **avFormatDemuxer:** can play concat ([0cfc4f6](https://github.com/alibaba/CicadaPlayer/commit/0cfc4f6730815a3d0613e7906dd6393a671dbd03)) +* **cache:** reset StreamMeta type after get. ([c27561c](https://github.com/alibaba/CicadaPlayer/commit/c27561cca92b4e6f7ce83b8b8f56f0d346bc9bc4)) +* **CheaterAudioRende:** change reset to set 0 to mClock ([f1b6371](https://github.com/alibaba/CicadaPlayer/commit/f1b637197d3a8a5b955db1995c66be935518f988)) +* **cicadaPlayer:** fix on error crash in travis env ([da92089](https://github.com/alibaba/CicadaPlayer/commit/da9208969d2299ee44d8692380680cae47dd07c6)) +* **curl:** fix hls play crash ([5271362](https://github.com/alibaba/CicadaPlayer/commit/5271362c20f12951ed56a3054043b7ba7521e8cf)) +* **data_source:** update CURLConnection file size ([747646e](https://github.com/alibaba/CicadaPlayer/commit/747646e7ddf124741cf79b91293acae222893392)) +* **demo:** fix config source ([f2f74d5](https://github.com/alibaba/CicadaPlayer/commit/f2f74d5195d29a455997345d14654b013cab9374)) +* **demuxer:** reduce cpu usage when need retry ([1c89c09](https://github.com/alibaba/CicadaPlayer/commit/1c89c092a1786c862c7efcdefa56433d72523ec2)) +* **drop:** compare position to seekPos ([d43a225](https://github.com/alibaba/CicadaPlayer/commit/d43a2250936fab31ec2b129f672fafd023259de6)), closes [#24957385](https://github.com/alibaba/CicadaPlayer/issues/24957385) +* **ffmpeg_utils:** set the fps meta to steam ([68afe48](https://github.com/alibaba/CicadaPlayer/commit/68afe4837347f1f1b434d7bfe9a1791416c8fe04)) +* **filterAudioRender:** use volatile to avoid out-of-order execution ([f57bb58](https://github.com/alibaba/CicadaPlayer/commit/f57bb5869a848968ec6dc2415583040218a081e9)) +* **GLRender:** remove old program when create OES surface ([46b5bd5](https://github.com/alibaba/CicadaPlayer/commit/46b5bd598e83007a54e3dc980574435b568f6be3)) +* **HLSManager:** first seek video or mix steam ([ed5063f](https://github.com/alibaba/CicadaPlayer/commit/ed5063fcac39545f62d1bd6c8d755a24ce4a3361)) +* **HLSManager:** fix logic of reading use index ([999299b](https://github.com/alibaba/CicadaPlayer/commit/999299b32d59cd460f87d72c140526e67d1bfd12)) +* **HlsParser:** fix get stream type error ([31e8172](https://github.com/alibaba/CicadaPlayer/commit/31e81723251d9ea7edfb36f632cb7849d49497b2)) +* **HlsParser:** support EXTXMEDIA without uri ([68da2ac](https://github.com/alibaba/CicadaPlayer/commit/68da2acd03deb7c43a1b9e2fc7b6e378e7b58afd)) +* **HLSSampleAesDecrypter:** return on find startcode error ([9c5a187](https://github.com/alibaba/CicadaPlayer/commit/9c5a187086ed904d3262a9ae72106843aa4b66e6)) +* **HLSStream:** fix crash when webvtt no X-TIMESTAMP-MAP ([ed5e311](https://github.com/alibaba/CicadaPlayer/commit/ed5e311336a634393189e3e8585e5cd1363f2a6f)) +* **HLSStream:** get the ringht steam meta opened ([91355f2](https://github.com/alibaba/CicadaPlayer/commit/91355f22b923694a2393ec366b9f5665dfb98487)) +* **HLSStream:** improve subtitle seek ([7cc6587](https://github.com/alibaba/CicadaPlayer/commit/7cc6587b669c91fe3c5d7a708498de62e9d108bc)) +* **HLSStream:** reopen demuxer on read after stop ([f4a636b](https://github.com/alibaba/CicadaPlayer/commit/f4a636bb672fbee172348a390a16786216356347)) +* **HLSStream:** reset mInitSegPtr in upDateInitSection ([5839fac](https://github.com/alibaba/CicadaPlayer/commit/5839fac11954ff4eeb36bf056ae8903f153ab62f)) +* **media_packet_queue:** fix get key frame logic ([3318748](https://github.com/alibaba/CicadaPlayer/commit/33187482c9608849a3a97bd81efbc78d5f2acf2e)) +* **MediaCodec:** get right width and height ([ca1dd84](https://github.com/alibaba/CicadaPlayer/commit/ca1dd8414e70c64636ac0928da94634bca918cf5)) +* **mediaplayer:** description length over 256 when getOption ([5cc1b00](https://github.com/alibaba/CicadaPlayer/commit/5cc1b00670cc8b3a1192b55819135f574d424081)) +* **mediaPlayer:** avoid video jump when catch up ([f0c4e75](https://github.com/alibaba/CicadaPlayer/commit/f0c4e7530c0bba551d0dda8d284abc9c0b67d7a6)) +* **mediaPlayer:** use the right id in SwitchStreamAligned ([e3005be](https://github.com/alibaba/CicadaPlayer/commit/e3005be273b9e34915cfec1db499c9d3edaa1849)) +* **mediaPlayer:** use the right id in switchVideoStream ([e99f30e](https://github.com/alibaba/CicadaPlayer/commit/e99f30e58c0d6008ef119044498fcbf8c5664df6)) +* **muxer:** add rotate meta to video stream ([0d3db42](https://github.com/alibaba/CicadaPlayer/commit/0d3db4286afc4c483ca251f98d1a179ad33bc8e0)) +* **player_msg_control:** add lock ([a26ff3a](https://github.com/alibaba/CicadaPlayer/commit/a26ff3a3aadfe1c9a98cb20d17f2def4d06fc882)) +* **render:** don't need redraw when nothing be changed ([0f16185](https://github.com/alibaba/CicadaPlayer/commit/0f161859ceb8e6165d463f61b5c55c032efa01a5)) +* **SuperMediaPlayer:** close the right video stream ([4487e82](https://github.com/alibaba/CicadaPlayer/commit/4487e8226385936e4c0ea90af1eb48909e9d7de2)) +* **SuperMediaPlayer:** fix crash in setup av path ([cce5cd0](https://github.com/alibaba/CicadaPlayer/commit/cce5cd042ef6cd06289f72ed94b26191ec8cc209)) +* **SuperMediaPlayer:** update duration on ReadPacket ([871c3a6](https://github.com/alibaba/CicadaPlayer/commit/871c3a69825d3c9adc48ca8326b54a6dcb4ee4e0)) +* **utils:** init ffmpeg once ([f823d27](https://github.com/alibaba/CicadaPlayer/commit/f823d27254a63c17ab687c6889e45ce53028f3cd)) +* **Render:** dont call updateTexImg() when frame not available, fix blank screen ([0007970](https://github.com/alibaba/CicadaPlayer/commit/000797039aad677483ea71f2b2f7e127581ce6b0)) +* **Render:** render frame when window not changed if player pause ([adf3758](https://github.com/alibaba/CicadaPlayer/commit/adf375808ed93664d397b0b24743dee8cfa1f69c)) +* **supermediaplayer:** use sync video pts to detect pts revert ([627b11c](https://github.com/alibaba/CicadaPlayer/commit/627b11c0c22bdcede1f2b709ab93372f4ff7792e)) +* **vsync:** do not use android vsync because of compatibility issue ([4cacc83](https://github.com/alibaba/CicadaPlayer/commit/4cacc83ba5a6b89d9666f2753819494967655955)) +* **yuvRender:** uvWidth may not right in some iOS simulators. ([e4f89ca](https://github.com/alibaba/CicadaPlayer/commit/e4f89ca5a41d739a4132b3d09d7de6fc311d2f2e)) + + +### Features + +* **AESEncrypt:** add avAESEncrypt ([ea5dbfd](https://github.com/alibaba/CicadaPlayer/commit/ea5dbfd49ed012cbc9fcccabed63e603f94f187a)) +* **afpacket:** support isProtected api ([a390893](https://github.com/alibaba/CicadaPlayer/commit/a39089313ae64e47f3a2c9c63f6b44e7480d078b)) +* **android:** add maxAccurateDelta api ([31ba7ec](https://github.com/alibaba/CicadaPlayer/commit/31ba7ecedff5b5cdb926e62d5f6f119a94de6502)) +* **android:** add exo external player support ([150381d](https://github.com/alibaba/CicadaPlayer/commit/150381d3d559bf3f9c43bd76fa1c14285fa603b1)) +* **android:** add global setting ([50a8a55](https://github.com/alibaba/CicadaPlayer/commit/50a8a55841a05669e988d05b3a138968c2026fc6)) +* **android:** add probe by name ([95c2ad9](https://github.com/alibaba/CicadaPlayer/commit/95c2ad9e9f05b0a26dd4d8c0a74d0c44673d1ec2)) +* **android:** add set hevc black list api ([d5823c4](https://github.com/alibaba/CicadaPlayer/commit/d5823c4a6bac7b0d6d9cee16ac81c9e79429dec9)) +* **android:** add setFastStart api ([f2d85e9](https://github.com/alibaba/CicadaPlayer/commit/f2d85e98087771cbcfdbc92058b194e24e6bbf70)) +* **android:** add setIpResolveType api ([c8f9a73](https://github.com/alibaba/CicadaPlayer/commit/c8f9a7396db512519a75f3221a808fc053925844)) +* **android:** add setVideoBackground api ([d84fa99](https://github.com/alibaba/CicadaPlayer/commit/d84fa9902c1970745ebe6a74afd9e0bfc16ba4f8)) +* **android:** create player by name ([07c2456](https://github.com/alibaba/CicadaPlayer/commit/07c24569515e40890a38c60ef47679c95b44aae9)) +* **anlystic:** support set external collector ([b664e13](https://github.com/alibaba/CicadaPlayer/commit/b664e131f21802f5d5f7a3514d3946a45465e4be)) +* **Apple:** add interface for CicadaAudioSessionDelegate ([0d8b537](https://github.com/alibaba/CicadaPlayer/commit/0d8b537bf133366dccad2347bd762b7133340802)) +* **Apple:** support set CicadaRenderDelegate ([839f3a0](https://github.com/alibaba/CicadaPlayer/commit/839f3a08edb4269e914f00ce37d74fd933077ebe)) +* **AudioRender:** support set CicadaAudioSessionDelegate ([bcbaae7](https://github.com/alibaba/CicadaPlayer/commit/bcbaae7b934354b28dbe88ca5eff225cbc290504)) +* **avafpacket:** support AVAFPacket to PBAFFrame ([7686348](https://github.com/alibaba/CicadaPlayer/commit/76863482d019eb1bf136af2a3f69cbbeaa16d357)) +* **build:** support build AV1 with source code ([756b399](https://github.com/alibaba/CicadaPlayer/commit/756b399f1ccd8755b3214cc4777b06e28349d2f2)) +* **build_tools:** add build version print in shared lib ([d14e394](https://github.com/alibaba/CicadaPlayer/commit/d14e394f353917f1a1a0f1a039b4476c6d031af2)) +* **build_tools:** support native openssl ([a0e6e16](https://github.com/alibaba/CicadaPlayer/commit/a0e6e1642c36f3e3ecc4385f33d3802877477f92)) +* **cicadaconfig:** add liveStartIndex config ([b9f1569](https://github.com/alibaba/CicadaPlayer/commit/b9f15695c667717785a10004dfa41ee35abdbb7f)) +* **cmdline:** add percentage seek ([8c5628f](https://github.com/alibaba/CicadaPlayer/commit/8c5628feaed9627a18c2495867ab83ab5928c7ba)) +* **cmdline:** add syncPlayer example ([8a26c6b](https://github.com/alibaba/CicadaPlayer/commit/8a26c6ba8551edc29d643e25e5b33e36e5fb73f5)) +* **cmdline:** support set native window and sdl window to sdl render ([b68e37b](https://github.com/alibaba/CicadaPlayer/commit/b68e37b39d7ed69139913953b61eee6b65f43655)) +* **codec:** support dav1d with set external lib ([1c50acf](https://github.com/alibaba/CicadaPlayer/commit/1c50acffc7c724fc7dfd8d19c4782d2e7df35189)) +* **codec:** support vp8 vp9 opus ([70041a1](https://github.com/alibaba/CicadaPlayer/commit/70041a152f63168a880e6c8ebd46f0b1db52a493)) +* **content:** support android content protocol ([92805b1](https://github.com/alibaba/CicadaPlayer/commit/92805b13d155b4ece3263dc2481c7ab2df303ab7)) +* **curl:** support http post ([d988bad](https://github.com/alibaba/CicadaPlayer/commit/d988bad81b8051d00f704d3bf48f3227bc0866cf)) +* **curl_data_source:** supprot muti connect ([0b2496a](https://github.com/alibaba/CicadaPlayer/commit/0b2496ac5060d6319ae9d1b1c7e5f9a6719c4c19)) +* **data_source:** add api to set ipv4/6 ([d7c927a](https://github.com/alibaba/CicadaPlayer/commit/d7c927a512668bdbf808079a7b7e2444e87f72d6)) +* **data_source:** support open with range ([428f79b](https://github.com/alibaba/CicadaPlayer/commit/428f79b92e00d2f0f7de6cccbc57fa72f9f473bf)) +* **dca:** add DCA direct component access ([2e948b8](https://github.com/alibaba/CicadaPlayer/commit/2e948b8fffb8fcb6f6d68f20cd044fe032aca716)) +* **decrypto:** add decrypto interface ([970750f](https://github.com/alibaba/CicadaPlayer/commit/970750f6171c5c0b2928535318257088e2314858)) +* **demo:** add sync player ([6177474](https://github.com/alibaba/CicadaPlayer/commit/6177474381404b2d4a5bd7eb973039f40b369322)) +* **Demo:** add sample code of CicadaAudioSessionDelegate ([02cf374](https://github.com/alibaba/CicadaPlayer/commit/02cf3741e4646eb9efa589c8bfe73105ec7c02c8)) +* **demuxer:** player will callback the drm media data when DRMMagicKey is Valide ([7e33121](https://github.com/alibaba/CicadaPlayer/commit/7e3312176731a07e0b2c49a6b253cd0cfbe293ee)) +* **external:** add flvdec aliyun extend audio codec id in ffmpeg ([e441e32](https://github.com/alibaba/CicadaPlayer/commit/e441e3206fc3010d67795bc0590b6cb156a81533)) +* **external:** flv support hevc ([6180d52](https://github.com/alibaba/CicadaPlayer/commit/6180d5232eb5dcbd78b4c60267039dcdaaaa3a64)) +* **ffmpeg:** enable dash demuxer ([9a7c5a6](https://github.com/alibaba/CicadaPlayer/commit/9a7c5a641b8d7fec319d646464abe2c7b6d44994)) +* **framework:** add communication tools ([f84c186](https://github.com/alibaba/CicadaPlayer/commit/f84c186882c0d425609857776e32c852ba267a12)) +* **framework:** add ffmpeg video filter ([470d557](https://github.com/alibaba/CicadaPlayer/commit/470d5577c69f52e7262d8865bec2f699566da35a)) +* **hls:** parse first dts on start and seek ([8bc4386](https://github.com/alibaba/CicadaPlayer/commit/8bc438678c623649266da9f3edb11a7563f96b5e)) +* **HlsParser:** support EXT-X-BYTERANGE tag ([a232ae7](https://github.com/alibaba/CicadaPlayer/commit/a232ae73e73ae4e8d8e88f8de83c20b02f245762)) +* **HLSStream:** reuse the demuxer meta ([fa37a8f](https://github.com/alibaba/CicadaPlayer/commit/fa37a8f4ea86d568a50de9d8271785557ecc0c6e)) +* **HLSStream:** support hls id3 pts ([3195e0d](https://github.com/alibaba/CicadaPlayer/commit/3195e0df76250b4c7d9ec5fd68acfcc3c5041c84)) +* **HLSStream:** support parse webvtt pts ([4b1b7bc](https://github.com/alibaba/CicadaPlayer/commit/4b1b7bc4c73b384da8a0e0e93dbd86881d087367)) +* **HLSStream:** support seg with range ([ac345e9](https://github.com/alibaba/CicadaPlayer/commit/ac345e9a669d37e93686db1b0c99ff0dc76b5dd1)) +* **iafpacket:** add setProtected api ([8ff9bf0](https://github.com/alibaba/CicadaPlayer/commit/8ff9bf089b51515a0f6cf5938bedc28becb2ee47)) +* **iOS:** add decoder recover size warning event ([b598575](https://github.com/alibaba/CicadaPlayer/commit/b598575f32a7f01155d2433d430ab6cb528ffc58)) +* **iOS:** add getOption interface ([f42a468](https://github.com/alibaba/CicadaPlayer/commit/f42a4688ef19b25ce6b96cfb34a3419b0ad8044d)) +* **iOS:** add interface to sync player ([d02f881](https://github.com/alibaba/CicadaPlayer/commit/d02f881d389c27f8e300e81d77fec70b1afbe9b1)) +* **iOS:** add maxAccurateDelta api ([1bdfe3a](https://github.com/alibaba/CicadaPlayer/commit/1bdfe3a98c66a96c39ee3177987ae6a76bd503ca)) +* **iOS:** add select subtitle error event ([d8c0a60](https://github.com/alibaba/CicadaPlayer/commit/d8c0a605be0fa3e4a95ddb441ca8a4ccca856cb9)) +* **iOS:** add SetDefaultBandWidth interface ([bb41c9a](https://github.com/alibaba/CicadaPlayer/commit/bb41c9a6ee0376588effcf3f214a4acd9fcb9a87)) +* **iOS:** add setDNSResolve interface ([35d4f06](https://github.com/alibaba/CicadaPlayer/commit/35d4f06d11213cd25c47a5eb2fae340e7e15b2d9)) +* **iOS:** add VideoRendered delegate ([9d71f4c](https://github.com/alibaba/CicadaPlayer/commit/9d71f4c673560363436b6843f35bd3d0921f87ae)) +* **mac:** add sync player ([c854037](https://github.com/alibaba/CicadaPlayer/commit/c8540372df5cab31123dfe497a2641b0ee438282)) +* **mediaplayer:** add api to set fast start mode ([02c495f](https://github.com/alibaba/CicadaPlayer/commit/02c495f06fb337d07db76b3d45c32bca7a58f0eb)) +* **mediaplayer:** add dummy externalPlayer impl for jni and apple ([d7ce718](https://github.com/alibaba/CicadaPlayer/commit/d7ce718e04a787e0761fb22c2b9f2cc09ec5936f)) +* **mediaplayer:** add InvokeComponent api ([79278d1](https://github.com/alibaba/CicadaPlayer/commit/79278d1bd95ed71942b7c4c44e8977d7811426cd)) +* **mediaplayer:** add liveStartIndex config ([431495e](https://github.com/alibaba/CicadaPlayer/commit/431495e839707ecf9bc7d664e4699dc5407dba85)) +* **mediaplayer:** add liveStartIndex option ([bfb3a31](https://github.com/alibaba/CicadaPlayer/commit/bfb3a318ef6e0f6d4237d2cf32ff36e0f9902f27)) +* **mediaplayer:** add set streamTypes api ([8b69ab3](https://github.com/alibaba/CicadaPlayer/commit/8b69ab308da7ece62467f3449b56e6883e303068)) +* **mediaplayer:** add setClockRefer api ([96837a7](https://github.com/alibaba/CicadaPlayer/commit/96837a72fb25436070bf42594f687e02ee0f4491)) +* add a event to notify video recover size too large ([8713fc5](https://github.com/alibaba/CicadaPlayer/commit/8713fc5d4c6d5ff0323456149cd7975bfe7254ed)), closes [#118](https://github.com/alibaba/CicadaPlayer/issues/118) +* add videoRendered callback ([6c5768b](https://github.com/alibaba/CicadaPlayer/commit/6c5768b6a38e76e1326e5d324f0d2988d3277727)) +* clear all ips under the host when remove ip is empty ([67c1aba](https://github.com/alibaba/CicadaPlayer/commit/67c1abaa49ef8f17ba701c20a31ddbfa4e94826a)) +* **mediaplayer:** add maxAccurateSeekDelta option ([ec31796](https://github.com/alibaba/CicadaPlayer/commit/ec317963bb5a9e371cf982f064acc4db7e17cfa6)) +* **mediaplayer:** add OnRenderFrame callback ([2d46589](https://github.com/alibaba/CicadaPlayer/commit/2d465896cd0ab74bdeb761dc01c15212defb8bcf)) +* **mediaplayer:** add pixelBufferOutputFormat config for apple platform ([1048145](https://github.com/alibaba/CicadaPlayer/commit/10481459f8c237a1490bfa4235f0af0a0abee803)) +* **mediaplayer:** add SetIPResolveType api ([126e368](https://github.com/alibaba/CicadaPlayer/commit/126e36859763698348739ebffb5f5bc05ae64d16)) +* **mediaplayer:** get bLowLatency from demuxer ([65da9a7](https://github.com/alibaba/CicadaPlayer/commit/65da9a724100b3476586d0c88f87b019fc1b5afe)) +* **mediaplayer:** imple mediaPlayerDCA ([bd9dae7](https://github.com/alibaba/CicadaPlayer/commit/bd9dae7a7c5982377e1b6644a371d322b225cd82)) +* **mediaplayer:** not return buffer when secret playback ([b26f337](https://github.com/alibaba/CicadaPlayer/commit/b26f337515ec88cabeb9de979d7d09baef400a9c)) +* **mediaplayer:** support not render video when frame callback is set ([5983d82](https://github.com/alibaba/CicadaPlayer/commit/5983d822a06de1aeb21bfe63589fe9969de51916)) +* **mediaplayer:** use Prototype ([0bfd9b9](https://github.com/alibaba/CicadaPlayer/commit/0bfd9b93a677b66b0f5932feff3915881f95d644)) +* **platform:** add DCA event api ([1913f6d](https://github.com/alibaba/CicadaPlayer/commit/1913f6d5da92479d3b5d62f35ca498f7a452e9e7)) +* **player:** set player pointer ([4c7ffd8](https://github.com/alibaba/CicadaPlayer/commit/4c7ffd8c566a89edd670da5af1a4f531ffb938e2)) +* **render:** add audio rendering callback, return audio data that has processed ([6ba6696](https://github.com/alibaba/CicadaPlayer/commit/6ba6696cff7e6d91584fda3e7300242377fa7925)) +* **Render:** add yuv422 support ([fe4c440](https://github.com/alibaba/CicadaPlayer/commit/fe4c440b88fb8e41f2759b3e334b03627056c63a)) +* **supermediaplayer:** support buffer in demuxer ([94b08d2](https://github.com/alibaba/CicadaPlayer/commit/94b08d20e20b05443d39b6502e33e9b2f3c9706b)) +* add android VideoRenderCallbac api ([3dcc7cc](https://github.com/alibaba/CicadaPlayer/commit/3dcc7cc2d465e76d243d94b2f47bf043695fae78)) +* add setBackgroundColor ([3bdb7ef](https://github.com/alibaba/CicadaPlayer/commit/3bdb7ef4e23379dfbb472a92cbdd7f36dc485ccf)) +* hls support fmp4 ([4e6f79d](https://github.com/alibaba/CicadaPlayer/commit/4e6f79ddc811aa6457c399b1b22038750e2a1420)) +* set clear screen color ([12cb58b](https://github.com/alibaba/CicadaPlayer/commit/12cb58b4c3f343c9d95ca7c6445989c15d9d2762)) +* support Cheater render ([58c19b4](https://github.com/alibaba/CicadaPlayer/commit/58c19b4dede537b2582825b1d02f3573c03a0574)) + + +### Performance Improvements + +* **filteraudiorender:** fix busy loop ([cd53dd3](https://github.com/alibaba/CicadaPlayer/commit/cd53dd3eecb0d17b3f571ddcd155bbb74772f469)) +* update unique_lock to lock_guard ([2fe006d](https://github.com/alibaba/CicadaPlayer/commit/2fe006df1bb2e715f75f4aecb88177195f8f1c45)) +* **ActiveDecoder:** speedup lock usage ([bc72c04](https://github.com/alibaba/CicadaPlayer/commit/bc72c04799da34271bde8ee4125542460c27a6c1)) +* **AppleVideoToolBox:** speedup recovering ([70d89b9](https://github.com/alibaba/CicadaPlayer/commit/70d89b97bddbf5613f62b4c56cf5e9ee99817712)) + + +### Reverts + +* Revert "improvement(demuxer): make GetStreamMeta thread safe" ([b1177d2](https://github.com/alibaba/CicadaPlayer/commit/b1177d2adc02a906fd7afdd2e7d9038b341936f6)) +* Revert "refactor(player): use getAudioPlayTimeStamp to check delay" ([d1a831c](https://github.com/alibaba/CicadaPlayer/commit/d1a831cb8c0976dc250efb8217e1f18631bcd63a)) +* Revert "refactor(framework): add codec id for packet" ([4e02b04](https://github.com/alibaba/CicadaPlayer/commit/4e02b04183fc592217b114053aa836c63447e0d9)) +* Revert "fix: update duration when prepare" ([38d3d85](https://github.com/alibaba/CicadaPlayer/commit/38d3d85e508e6e72433e8220c1e86a9e1a52959a)) + + + # [](/compare/v0.1.0...v) (2020-03-04) diff --git a/README.md b/README.md index 26f21b02d..774297bf5 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,9 @@ export CURL_GIT=https://gitee.com/mirrors/curl.git - [2. compile Android](doc/compile_Android.md) - [3. compile_Linux](doc/compile_Linux.md) - [4. compile_Windows (cross compile)](doc/compile_Windows.md) -- [5. compile_MacOS](doc/compile_mac.md) -- 6.webAssembly coming soon +- [5.compile_Windows(msvc)](doc/compile_Windows_msvc.md) +- [6. compile_MacOS](doc/compile_mac.md) +- 7.webAssembly coming soon ## How to use diff --git a/build_player.sh b/build_player.sh index ac86ed898..6df6cf08a 100644 --- a/build_player.sh +++ b/build_player.sh @@ -101,8 +101,8 @@ function build_Android(){ mkdir -p output/armeabi-v7a/ mkdir -p output/arm64-v8a/ - cp platform/Android/source/premierlibrary/build/intermediates/cmake/corePlayer/release/obj/armeabi-v7a/*.so output/armeabi-v7a/ - cp platform/Android/source/premierlibrary/build/intermediates/cmake/corePlayer/release/obj/arm64-v8a/*.so output/arm64-v8a/ + cp platform/Android/source/premierlibrary/build/intermediates/cmake/corePlayerRelease/obj/armeabi-v7a/*.so output/armeabi-v7a/ + cp platform/Android/source/premierlibrary/build/intermediates/cmake/corePlayerRelease/obj/arm64-v8a/*.so output/arm64-v8a/ cp external/install/ffmpeg/Android/armeabi-v7a/libalivcffmpeg.so output/armeabi-v7a/ cp external/install/ffmpeg/Android/arm64-v8a/libalivcffmpeg.so output/arm64-v8a/ @@ -171,16 +171,10 @@ function build_iOS_new(){ DEMO_SOURCE_DIR_IOS=${TOP_DIR}/platform/Apple/demo/iOS cd ${DEMO_SOURCE_DIR_IOS} ./genxcodeproj.sh - if [[ -n "$MTL" ]];then - packet_iOS - fi + packet_iOS } function build_iOS(){ - - if [[ -n "$MTL" ]];then - export HOMEBREW_NO_AUTO_UPDATE=true - fi check_brew if [[ -n "$MTL" ]];then @@ -204,11 +198,9 @@ function build_iOS(){ fi build_iOS_new - if [[ -n "$MTL" ]];then - if [[ ! -f "${TOP_DIR}/output/CicadaPlayerSDK/SDK/ARM_SIMULATOR/CicadaPlayerSDK.framework/CicadaPlayerSDK" ]]; then - echo "CicadaPlayerSDK.framework build failed" - return 1 - fi + if [[ ! -f "${TOP_DIR}/output/CicadaPlayerSDK/SDK/ARM_SIMULATOR/CicadaPlayerSDK.framework/CicadaPlayerSDK" ]]; then + echo "CicadaPlayerSDK.framework build failed" + return 1 fi } diff --git a/build_tools/build_curl.sh b/build_tools/build_curl.sh index f8862644e..38e6fb2ab 100755 --- a/build_tools/build_curl.sh +++ b/build_tools/build_curl.sh @@ -82,7 +82,8 @@ function build_curl(){ --without-libidn2 \ --without-librtmp \ --without-brotli \ - --without-libidn" + --without-libidn \ + --without-nghttp2" local build_dir="${CWD}/build/curl/$1/$2" local install_dir="${CWD}/install/curl/$1/$2" diff --git a/build_tools/build_iOS.sh b/build_tools/build_iOS.sh index 7f27e88f1..6615c427d 100755 --- a/build_tools/build_iOS.sh +++ b/build_tools/build_iOS.sh @@ -46,29 +46,32 @@ function build_fat_libs(){ } function create_cmake_config(){ - echo "cmake_minimum_required(VERSION 3.6)" >> $CONFIG_FILE - echo "set(LIB_NAME ${LIB_NAME})" >> $CONFIG_FILE - echo "find_library(SECURITY Security)" >> $CONFIG_FILE + echo "cmake_minimum_required(VERSION 3.14)" >> "$CONFIG_FILE" + echo "set(LIB_NAME ${LIB_NAME})" >> "$CONFIG_FILE" +# echo "find_library(SECURITY Security)" >> "$CONFIG_FILE" # echo "find_library(LIBXML2 Xml2)" >> $CONFIG_FILE - echo "find_library(AUDIO_TOOL_BOX AudioToolbox)" >> $CONFIG_FILE +# echo "find_library(AUDIO_TOOL_BOX AudioToolbox)" >> "$CONFIG_FILE" # echo "find_library(VIDEO_TOOL_BOX VideoToolbox)" >> $CONFIG_FILE # echo "find_library(COREMEDIA CoreMedia)" >> $CONFIG_FILE # echo "find_library(COREVIDEO CoreVideo)" >> $CONFIG_FILE # echo "find_library(COREFOUNDATION CoreFoundation)" >> $CONFIG_FILE - echo -n "set(SRC_LIBRARIES ${SRC_LIBRARIES}" >> $CONFIG_FILE - if [[ "${SSL_USE_NATIVE}" == "TRUE" ]];then - echo -n ' ${SECURITY}' >> $CONFIG_FILE - fi + echo -n "set(SRC_LIBRARIES ${SRC_LIBRARIES}" >> "$CONFIG_FILE" +# if [[ "${SSL_USE_NATIVE}" == "TRUE" ]];then +# echo -n ' ${SECURITY}' >> "$CONFIG_FILE" +# fi if [[ "${XML_USE_NATIVE}" == "TRUE" ]];then - echo -n ' xml2' >> $CONFIG_FILE + echo -n ' xml2' >> "$CONFIG_FILE" fi - echo -n ' ${AUDIO_TOOL_BOX}' >> $CONFIG_FILE +# echo -n ' ${AUDIO_TOOL_BOX}' >> "$CONFIG_FILE" # echo -n ' ${VIDEO_TOOL_BOX}' >> $CONFIG_FILE # echo -n ' ${COREMEDIA}' >> $CONFIG_FILE # echo -n ' ${COREVIDEO}' >> $CONFIG_FILE # echo -n ' ${COREFOUNDATION}' >> $CONFIG_FILE - echo ")" >> $CONFIG_FILE - echo "set(SRC_LIBRARIES_DIR ${SRC_LIBRARIES_DIR})" >>$CONFIG_FILE + echo ")" >> "$CONFIG_FILE" + echo "set(SRC_LIBRARIES_DIR ${SRC_LIBRARIES_DIR})" >>"$CONFIG_FILE" + echo -n "set(LINK_FRAMEWORKS " >> "$CONFIG_FILE" + echo -n '"-framework Security -framework AudioToolbox"' >> "$CONFIG_FILE" + echo ")" >> "$CONFIG_FILE" } #build to ffmpeg function build_shared_framework(){ @@ -115,22 +118,22 @@ function build_shared_framework(){ cp ${BUILD_TOOLS_DIR}/src/build_version.cpp ./ sh ${BUILD_TOOLS_DIR}/gen_build_version.sh > version.h - rm -rf Xcode/ - mkdir -p Xcode/OS - cd Xcode/OS - cmake -DCMAKE_TOOLCHAIN_FILE=${BUILD_TOOLS_DIR}/iOS/iOS.cmake \ - -DIOS_PLATFORM=OS ../../ \ - -G Xcode - xcodebuild -destination generic/platform=iOS -configuration MinSizeRel -target ALL_BUILD - - cd - - mkdir -p Xcode/SIMULATOR64 - cd Xcode/SIMULATOR64 - cmake -DCMAKE_TOOLCHAIN_FILE=${BUILD_TOOLS_DIR}/iOS/iOS.cmake \ - -DIOS_PLATFORM=SIMULATOR64 ../../ \ - -G Xcode - - xcodebuild -destination "platform=iOS Simulator" -configuration MinSizeRel -target ALL_BUILD +# rm -rf Xcode/ +# mkdir -p Xcode/OS +# cd Xcode/OS +# cmake -DCMAKE_TOOLCHAIN_FILE=${BUILD_TOOLS_DIR}/iOS/iOS.cmake \ +# -DIOS_PLATFORM=OS ../../ \ +# -G Xcode +# xcodebuild -destination generic/platform=iOS -configuration MinSizeRel -target ALL_BUILD +# +# cd - +# mkdir -p Xcode/SIMULATOR64 +# cd Xcode/SIMULATOR64 +# cmake -DCMAKE_TOOLCHAIN_FILE=${BUILD_TOOLS_DIR}/iOS/iOS.cmake \ +# -DIOS_PLATFORM=SIMULATOR64 ../../ \ +# -G Xcode +# +# xcodebuild -destination "platform=iOS Simulator" -configuration MinSizeRel -target ALL_BUILD return; } diff --git a/build_tools/iOS/CMakeLists.txt b/build_tools/iOS/CMakeLists.txt index a1d2b99dc..92959f63b 100644 --- a/build_tools/iOS/CMakeLists.txt +++ b/build_tools/iOS/CMakeLists.txt @@ -38,6 +38,7 @@ set_target_properties(${LIB_NAME} PROPERTIES # MACOSX_FRAMEWORK_INFO_PLIST Info.plist # PUBLIC_HEADER dynamicFramework.h # XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer" + XCODE_ATTRIBUTE_EXCLUDED_ARCHS[sdk=iphonesimulator*] arm64 XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS YES XCODE_ATTRIBUTE_ENABLE_BITCODE "YES" XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE bitcode @@ -69,3 +70,4 @@ endif () # set_xcode_property(${LIB_NAME} BITCODE_GENERATION_MODE bitcode) #endif () target_link_libraries(${LIB_NAME} PRIVATE ${LIBRARYS}) +target_link_libraries(${LIB_NAME} PUBLIC "${LINK_FRAMEWORKS}") diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt index 5257317a5..a8472e283 100644 --- a/cmdline/CMakeLists.txt +++ b/cmdline/CMakeLists.txt @@ -66,10 +66,12 @@ endif () target_link_libraries(cicadaPlayer PRIVATE framework_filter framework_utils + framework_drm avfilter avformat avcodec swresample + swscale avutil ${FRAMEWORK_LIBS}) if (NOT MSVC) @@ -101,11 +103,15 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") endif () if (APPLE) + find_library(AVFOUNDATION AVFoundation) + find_library(QUARTZCORE QuartzCore) target_link_libraries( cicadaPlayer PUBLIC iconv bz2 ${FRAMEWORK_LIBS} + ${AVFOUNDATION} + ${QUARTZCORE} ) elseif (MSVC) else () diff --git a/cmdline/cicadaPlayer.cpp b/cmdline/cicadaPlayer.cpp index 6c5d0aea6..7a2cc1bb4 100644 --- a/cmdline/cicadaPlayer.cpp +++ b/cmdline/cicadaPlayer.cpp @@ -11,9 +11,10 @@ using namespace Cicada; using namespace std; +#include "NetWorkEventReceiver.h" #include "SDLEventReceiver.h" #include "cicadaEventListener.h" -#include "NetWorkEventReceiver.h" +#include #include @@ -50,6 +51,11 @@ static void onPrepared(void *userData) cont->player->Start(); } +static void currentDownLoadSpeed(int64_t speed, void *userData) +{ + AF_LOGD("current speed is %f kbps\n", (float) speed / 1024); +} + static void onEvent(int64_t errorCode, const void *errorMsg, void *userData) { auto *cont = static_cast(userData); @@ -72,7 +78,7 @@ static void onEvent(int64_t errorCode, const void *errorMsg, void *userData) msg.deleteItem("content"); msg.addValue("content", "hi"); msg.addValue("cmd", 0); - cont->player->InvokeComponent(msg.printJSON()); + cont->player->InvokeComponent(msg.printJSON().c_str()); } break; } @@ -130,6 +136,7 @@ int main(int argc, char *argv[]) pListener.EventCallback = onEvent; pListener.ErrorCallback = onError; pListener.Prepared = onPrepared; + pListener.CurrentDownLoadSpeed = currentDownLoadSpeed; cicadaEventListener eListener(player.get()); #ifdef ENABLE_SDL SDLEventReceiver receiver(eListener); diff --git a/cmdline/example/CMakeLists.txt b/cmdline/example/CMakeLists.txt index 15373d33d..19ad3b0c4 100644 --- a/cmdline/example/CMakeLists.txt +++ b/cmdline/example/CMakeLists.txt @@ -50,6 +50,7 @@ endif () target_link_libraries(syncPlayer PRIVATE framework_filter framework_utils + framework_drm avfilter avformat avcodec diff --git a/cmdline/example/syncPlayer.cpp b/cmdline/example/syncPlayer.cpp index f7ea43e5b..9110660fe 100644 --- a/cmdline/example/syncPlayer.cpp +++ b/cmdline/example/syncPlayer.cpp @@ -1,5 +1,6 @@ #include #include +#include #include using namespace Cicada; diff --git a/external/build_external.sh b/external/build_external.sh index 537fc8481..6c612d757 100755 --- a/external/build_external.sh +++ b/external/build_external.sh @@ -199,6 +199,11 @@ if [[ -f "${CICADA_FFMPEG_CONFIG_FILE}" ]]; then cp "${CICADA_FFMPEG_CONFIG_FILE}" ./ fi +if [[ -f "${CICADA_GIT_SOURCE_LIST_FILE}" ]]; then + rm player_git_source_list.sh + cp "${CICADA_GIT_SOURCE_LIST_FILE}" ./ +fi + mkdir external cd external load_source diff --git a/external/player_ffmpeg_config.sh b/external/player_ffmpeg_config.sh index ef7c2df71..d7908ee1b 100644 --- a/external/player_ffmpeg_config.sh +++ b/external/player_ffmpeg_config.sh @@ -13,7 +13,7 @@ function dav1d_decoder_prebuilt() { } ffmpeg_config_add_decoders aac aac_latm h264 hevc mpeg4 mp3 mp3adu mp3float mp3on4float mp3adufloat mp3on4 pcm_s16le ac3_at eac3_at -ffmpeg_config_add_demuxers flv aac live_flv webvtt mov mp3 mpegts matroska h264 +ffmpeg_config_add_demuxers flv aac live_flv webvtt srt mov mp3 mpegts matroska h264 ac3 eac3 ffmpeg_config_add_muxers mp4 adts mpegts ffmpeg_config_add_parsers aac h264 hevc aac_latm ac3 ffmpeg_config_add_bsfs aac_adtstoasc h264_mp4toannexb hevc_mp4toannexb extract_extradata @@ -21,8 +21,8 @@ ffmpeg_config_add_protocols file crypto rtmp ffmpeg_config_add_filters atempo aresample aformat volume -ffmpeg_config_add_user "--enable-libxml2" -ffmpeg_config_add_demuxers dash +#ffmpeg_config_add_user "--enable-libxml2" +#ffmpeg_config_add_demuxers dash #for ffmpeg concat demuxer ffmpeg_config_add_demuxers concat diff --git a/external/player_git_source_list.sh b/external/player_git_source_list.sh index f643ce017..38c6c301f 100755 --- a/external/player_git_source_list.sh +++ b/external/player_git_source_list.sh @@ -37,10 +37,10 @@ fi CURL_BRANCH="curl-7_63_0" clone_git $CURL_GIT "$CURL_BRANCH" -if [[ -z "${LIBXML2_GIT}" ]];then - LIBXML2_GIT="https://github.com/GNOME/libxml2.git" -fi -LIBXML2_BRANCH="v2.9.9" +#if [[ -z "${LIBXML2_GIT}" ]];then +# LIBXML2_GIT="https://github.com/GNOME/libxml2.git" +#fi +#LIBXML2_BRANCH="v2.9.9" clone_git ${LIBXML2_GIT} "$LIBXML2_BRANCH" if [[ -z "${DAV1D_EXTERNAL_DIR}" ]];then diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index 0a2096354..9b74106e4 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -43,11 +43,17 @@ if ($ENV{VERSION}) endif () include(module_config.cmake) +message("CICADA_FRAMEWORK_MODULE_CONFIG_FILE is $ENV{CICADA_FRAMEWORK_MODULE_CONFIG_FILE}") +if(DEFINED ENV{CICADA_FRAMEWORK_MODULE_CONFIG_FILE}) + include("$ENV{CICADA_FRAMEWORK_MODULE_CONFIG_FILE}" OPTIONAL) +endif() + add_subdirectory(demuxer) add_subdirectory(codec) add_subdirectory(utils) add_subdirectory(filter) add_subdirectory(render) +add_subdirectory(drm) if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") else () add_subdirectory(communication) diff --git a/framework/Linux.cmake b/framework/Linux.cmake index 1baea8fcc..a5955056f 100644 --- a/framework/Linux.cmake +++ b/framework/Linux.cmake @@ -22,7 +22,7 @@ set(COMMON_INC_DIR ${COMMON_INC_DIR} ${PROJECT_SOURCE_DIR}) -set(FFMPEG_SOURCE_DIR ${TOPDIR}/external/external/ffmpeg/) +set(FFMPEG_SOURCE_DIR ${INSTALL_DIR}/../external/ffmpeg/) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=return-type") diff --git a/framework/base/media/AVAFPacket.cpp b/framework/base/media/AVAFPacket.cpp index b093bacc9..0711e0e2b 100644 --- a/framework/base/media/AVAFPacket.cpp +++ b/framework/base/media/AVAFPacket.cpp @@ -8,7 +8,6 @@ #include "utils/ffmpeg_utils.h" #ifdef __APPLE__ #include "PBAFFrame.h" -#include "avFrame2pixelBuffer.h" #endif using namespace std; @@ -60,6 +59,10 @@ AVAFPacket::AVAFPacket(AVPacket **pkt, bool isProtected) : mIsProtected(isProtec AVAFPacket::~AVAFPacket() { + if (mAVEncryptionInfo != nullptr) { + av_encryption_info_free(mAVEncryptionInfo); + } + av_packet_free(&mpkt); } @@ -68,7 +71,7 @@ uint8_t *AVAFPacket::getData() return mpkt->data; } -unique_ptr AVAFPacket::clone() +unique_ptr AVAFPacket::clone() const { return unique_ptr(new AVAFPacket(mpkt, mIsProtected)); } @@ -99,6 +102,93 @@ AVAFPacket::operator AVPacket *() return mpkt; } +bool AVAFPacket::getEncryptionInfo(IAFPacket::EncryptionInfo *dst) +{ + if(mAVEncryptionInfo == nullptr) { + + int encryption_info_size; + const uint8_t *new_encryption_info = av_packet_get_side_data(mpkt, AV_PKT_DATA_ENCRYPTION_INFO, &encryption_info_size); + + if (encryption_info_size <= 0 || new_encryption_info == nullptr) { + return false; + } + + mAVEncryptionInfo = av_encryption_info_get_side_data(new_encryption_info, encryption_info_size); + } + + if (mAVEncryptionInfo == nullptr) { + return false; + } + + if (mAVEncryptionInfo->scheme == MKBETAG('c', 'e', 'n', 'c')) { + dst->scheme = "cenc"; + } else if (mAVEncryptionInfo->scheme == MKBETAG('c', 'e', 'n', 's')) { + dst->scheme = "cens"; + } else if (mAVEncryptionInfo->scheme == MKBETAG('c', 'b', 'c', '1')) { + dst->scheme = "cbc1"; + } else if (mAVEncryptionInfo->scheme == MKBETAG('c', 'b', 'c', 's')) { + dst->scheme = "cbcs"; + } + + dst->crypt_byte_block = mAVEncryptionInfo->crypt_byte_block; + dst->skip_byte_block = mAVEncryptionInfo->skip_byte_block; + dst->subsample_count = mAVEncryptionInfo->subsample_count; + + dst->key_id = mAVEncryptionInfo->key_id; + dst->key_id_size = mAVEncryptionInfo->key_id_size; + + dst->iv = mAVEncryptionInfo->iv; + dst->iv_size = mAVEncryptionInfo->iv_size; + + if (mAVEncryptionInfo->subsample_count > 0) { + dst->subsample_count = mAVEncryptionInfo->subsample_count; + for(int i = 0; i < mAVEncryptionInfo->subsample_count; i++) { + SubsampleEncryptionInfo subInfo{}; + subInfo.bytes_of_protected_data = mAVEncryptionInfo->subsamples[i].bytes_of_protected_data; + subInfo.bytes_of_clear_data = mAVEncryptionInfo->subsamples[i].bytes_of_clear_data; + dst->subsamples.push_back(subInfo); + } + } else { + dst->subsample_count = 1; + SubsampleEncryptionInfo subInfo{}; + subInfo.bytes_of_protected_data = getSize(); + subInfo.bytes_of_clear_data = 0; + dst->subsamples.push_back(subInfo); + } + + return true; +} + + +AVAFFrame::AVAFFrame(const IAFFrame::AFFrameInfo &info, const uint8_t **data, const int *lineSize, int lineNums, IAFFrame::FrameType type) + : mType(type) +{ + AVFrame *avFrame = av_frame_alloc(); + if (type == FrameType::FrameTypeAudio) { + audioInfo aInfo = info.audio; + avFrame->channels = aInfo.channels; + avFrame->sample_rate = aInfo.sample_rate; + avFrame->format = aInfo.format; + int sampleSize = av_get_bytes_per_sample((enum AVSampleFormat)(avFrame->format)); + avFrame->nb_samples = (int) (lineSize[0] / (avFrame->channels * sampleSize)); + } else if (type == FrameType::FrameTypeVideo) { + videoInfo vInfo = info.video; + avFrame->width = vInfo.width; + avFrame->height = vInfo.height; + avFrame->format = vInfo.format; + } + + av_frame_get_buffer(avFrame, 32); + av_frame_make_writable(avFrame); + for (int i = 0; i < lineNums; i++) { + uint8_t *frameSamples = avFrame->data[i]; + memcpy(frameSamples, data[i], lineSize[i]); + } + + mAvFrame = avFrame; + copyInfo(); +} + AVAFFrame::AVAFFrame(AVFrame **frame, IAFFrame::FrameType type) : mType(type) { assert(*frame != nullptr); @@ -201,15 +291,3 @@ void AVAFFrame::updateInfo() { copyInfo(); } -#ifdef __APPLE__ -AVAFFrame::operator PBAFFrame *() -{ - CVPixelBufferRef pixelBuffer = avFrame2pixelBuffer(mAvFrame); - if (pixelBuffer) { - auto* frame = new PBAFFrame(pixelBuffer, mInfo.pts, mInfo.duration); - CVPixelBufferRelease(pixelBuffer); - return frame; - } - return nullptr; -} -#endif diff --git a/framework/base/media/AVAFPacket.h b/framework/base/media/AVAFPacket.h index 62a98853c..367ffdabf 100644 --- a/framework/base/media/AVAFPacket.h +++ b/framework/base/media/AVAFPacket.h @@ -6,10 +6,12 @@ #define FRAMEWORK_AVPACKET_H #include "base/media/IAFPacket.h" +#include extern "C" { #include -}; +#include +} #ifdef __APPLE__ class PBAFFrame; @@ -39,7 +41,7 @@ class AVAFPacket : public IAFPacket { mIsProtected = true; } - std::unique_ptr clone() override; + std::unique_ptr clone() const override; int64_t getSize() override; @@ -47,16 +49,36 @@ class AVAFPacket : public IAFPacket { explicit operator AVPacket *(); + void setMagicKey(const std::string &key) override + { + if (mMagicKey.empty()) { + mMagicKey = key; + } + } + + std::string getMagicKey() override + { + return mMagicKey; + } + + bool getEncryptionInfo(EncryptionInfo* dst) override; + private: AVPacket *mpkt{nullptr}; bool mIsProtected; + std::string mMagicKey{}; + AVEncryptionInfo *mAVEncryptionInfo{nullptr}; void copyInfo(); }; -class AVAFFrame : public IAFFrame { +class CICADA_CPLUS_EXTERN AVAFFrame : public IAFFrame { public: + + explicit AVAFFrame(const AFFrameInfo &info, const uint8_t **data, const int *lineSize, int lineNums, + IAFFrame::FrameType type = FrameTypeUnknown); + explicit AVAFFrame(AVFrame *frame, FrameType type = FrameTypeUnknown); explicit AVAFFrame(AVFrame **frame, FrameType type = FrameTypeUnknown); @@ -75,10 +97,6 @@ class AVAFFrame : public IAFFrame { explicit operator AVFrame *() const; -#ifdef __APPLE__ - explicit operator PBAFFrame *(); -#endif - void updateInfo(); @@ -100,13 +118,12 @@ static inline AVFrame *getAVFrame(IAFFrame *frame) static inline AVPacket *getAVPacket(IAFPacket *packet) { - auto * avafPacket = dynamic_cast(packet); + auto *avafPacket = dynamic_cast(packet); if (avafPacket) { return static_cast(*(avafPacket)); } return nullptr; - } -#endif //FRAMEWORK_AVPACKET_H +#endif//FRAMEWORK_AVPACKET_H diff --git a/framework/base/media/IAFPacket.h b/framework/base/media/IAFPacket.h index 4b020a00c..1ddc9791a 100644 --- a/framework/base/media/IAFPacket.h +++ b/framework/base/media/IAFPacket.h @@ -9,14 +9,14 @@ #include //#include #include +#include #include -#include #include - +#include extern "C" { //#include }; - +#include struct AVRational; @@ -48,7 +48,34 @@ class CICADA_CPLUS_EXTERN IAFPacket { ~packetInfo() { delete[](extra_data); + } + }; + + struct SubsampleEncryptionInfo { + unsigned int bytes_of_clear_data{0}; + unsigned int bytes_of_protected_data{0}; + }; + + struct EncryptionInfo { + std::string scheme{}; + + uint32_t crypt_byte_block{0}; + uint32_t skip_byte_block{0}; + + uint8_t *key_id{nullptr}; + uint32_t key_id_size{0}; + + uint8_t *iv{nullptr}; + uint32_t iv_size{0}; + + std::list subsamples{}; + uint32_t subsample_count{0}; + ~EncryptionInfo() + { + if (!subsamples.empty()) { + subsamples.clear(); + } } }; @@ -58,7 +85,7 @@ class CICADA_CPLUS_EXTERN IAFPacket { virtual ~IAFPacket() = default; - virtual std::unique_ptr clone() = 0; + virtual std::unique_ptr clone() const = 0; //TODO renturn const uint8_t, now for framework use virtual uint8_t *getData() = 0; @@ -68,7 +95,7 @@ class CICADA_CPLUS_EXTERN IAFPacket { virtual void setDiscard(bool discard) { mbDiscard = discard; - }; + } virtual bool getDiscard() { @@ -93,6 +120,20 @@ class CICADA_CPLUS_EXTERN IAFPacket { } } + virtual std::string getMagicKey() + { + return ""; + } + + virtual void setMagicKey(const std::string & key) + { + + } + + virtual bool getEncryptionInfo(EncryptionInfo *dst) + { + return false; + } protected: packetInfo mInfo{}; @@ -122,6 +163,16 @@ class CICADA_CPLUS_EXTERN IAFFrame { int colorRange; int colorSpace; + + bool operator==(const videoInfo &info) const + { + return this->width == info.width && this->height == info.height && this->format == info.format; + } + + bool operator!=(const videoInfo &info) const + { + return !operator==(info); + } }; struct audioInfo { int nb_samples; @@ -150,6 +201,7 @@ class CICADA_CPLUS_EXTERN IAFFrame { int64_t pkt_dts; int64_t duration; bool key; + int64_t timePosition; union { videoInfo video; audioInfo audio; @@ -170,16 +222,32 @@ class CICADA_CPLUS_EXTERN IAFFrame { virtual void setDiscard(bool discard) { + mbDiscard = discard; + } + virtual bool getDiscard() + { + return mbDiscard; } AFFrameInfo &getInfo(); + void setProtect(bool protect) + { + mbProtected = protect; + } + + bool isProtected() const + { + return mbProtected; + } + void dump(); protected: AFFrameInfo mInfo{}; - + bool mbDiscard{false}; + bool mbProtected{false}; }; diff --git a/framework/base/media/avFrame2pixelBuffer.c b/framework/base/media/avFrame2pixelBuffer.c deleted file mode 100644 index f0578319a..000000000 --- a/framework/base/media/avFrame2pixelBuffer.c +++ /dev/null @@ -1,290 +0,0 @@ -// -// Created by moqi on 2020/6/5. -// - -#include "avFrame2pixelBuffer.h" -#include -static int copy_avframe_to_pixel_buffer(const AVFrame *frame, CVPixelBufferRef cv_img, const size_t *plane_strides, - const size_t *plane_rows) -{ - int i, j; - size_t plane_count; - int status; - int rows; - int src_stride; - int dst_stride; - uint8_t *src_addr; - uint8_t *dst_addr; - size_t copy_bytes; - - status = CVPixelBufferLockBaseAddress(cv_img, 0); - if (status) { - AF_LOGE("Error: Could not lock base address of CVPixelBuffer: %d.\n", status); - } - - if (CVPixelBufferIsPlanar(cv_img)) { - plane_count = CVPixelBufferGetPlaneCount(cv_img); - for (i = 0; frame->data[i]; i++) { - if (i == plane_count) { - CVPixelBufferUnlockBaseAddress(cv_img, 0); - av_log(NULL, AV_LOG_ERROR, "Error: different number of planes in AVFrame and CVPixelBuffer.\n"); - - return AVERROR_EXTERNAL; - } - - dst_addr = (uint8_t *) CVPixelBufferGetBaseAddressOfPlane(cv_img, i); - src_addr = (uint8_t *) frame->data[i]; - dst_stride = CVPixelBufferGetBytesPerRowOfPlane(cv_img, i); - src_stride = plane_strides[i]; - rows = plane_rows[i]; - - if (dst_stride == src_stride) { - memcpy(dst_addr, src_addr, src_stride * rows); - } else { - copy_bytes = dst_stride < src_stride ? dst_stride : src_stride; - - for (j = 0; j < rows; j++) { - memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes); - } - } - } - } else { - if (frame->data[1]) { - CVPixelBufferUnlockBaseAddress(cv_img, 0); - av_log(NULL, AV_LOG_ERROR, "Error: different number of planes in AVFrame and non-planar CVPixelBuffer.\n"); - - return AVERROR_EXTERNAL; - } - - dst_addr = (uint8_t *) CVPixelBufferGetBaseAddress(cv_img); - src_addr = (uint8_t *) frame->data[0]; - dst_stride = CVPixelBufferGetBytesPerRow(cv_img); - src_stride = plane_strides[0]; - rows = plane_rows[0]; - - if (dst_stride == src_stride) { - memcpy(dst_addr, src_addr, src_stride * rows); - } else { - copy_bytes = dst_stride < src_stride ? dst_stride : src_stride; - - for (j = 0; j < rows; j++) { - memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes); - } - } - } - - status = CVPixelBufferUnlockBaseAddress(cv_img, 0); - if (status) { - AF_LOGE("Error: Could not unlock CVPixelBuffer base address: %d.\n", status); - return AVERROR_EXTERNAL; - } - - return 0; -} - -static int get_cv_pixel_format(enum AVPixelFormat fmt, enum AVColorRange range, int *av_pixel_format, int *range_guessed) -{ - if (range_guessed) *range_guessed = range != AVCOL_RANGE_MPEG && range != AVCOL_RANGE_JPEG; - - //MPEG range is used when no range is set - if (fmt == AV_PIX_FMT_NV12) { - *av_pixel_format = range == AVCOL_RANGE_JPEG ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange - : kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; - } else if (fmt == AV_PIX_FMT_YUV420P) { - *av_pixel_format = range == AVCOL_RANGE_JPEG ? kCVPixelFormatType_420YpCbCr8PlanarFullRange : kCVPixelFormatType_420YpCbCr8Planar; - } else if (fmt == AV_PIX_FMT_P010LE) { - *av_pixel_format = range == AVCOL_RANGE_JPEG ? kCVPixelFormatType_420YpCbCr10BiPlanarFullRange - : kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange; - *av_pixel_format = kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange; - } else { - return AVERROR(EINVAL); - } - - return 0; -} - -static int get_cv_pixel_info(const AVFrame *frame, int *color, int *plane_count, size_t *widths, size_t *heights, size_t *strides, - size_t *contiguous_buf_size) -{ - int av_format = frame->format; - int av_color_range = frame->color_range; - int i; - int range_guessed; - int status; - - status = get_cv_pixel_format(av_format, av_color_range, color, &range_guessed); - if (status) { - AF_LOGE("Could not get pixel format for color format"); - return AVERROR(EINVAL); - } - - switch (av_format) { - case AV_PIX_FMT_NV12: - *plane_count = 2; - - widths[0] = frame->width; - heights[0] = frame->height; - strides[0] = frame ? frame->linesize[0] : frame->width; - - widths[1] = (frame->width + 1) / 2; - heights[1] = (frame->height + 1) / 2; - strides[1] = frame ? frame->linesize[1] : (frame->width + 1) & -2; - break; - - case AV_PIX_FMT_YUV420P: - *plane_count = 3; - - widths[0] = frame->width; - heights[0] = frame->height; - strides[0] = frame ? frame->linesize[0] : frame->width; - - widths[1] = (frame->width + 1) / 2; - heights[1] = (frame->height + 1) / 2; - strides[1] = frame ? frame->linesize[1] : (frame->width + 1) / 2; - - widths[2] = (frame->width + 1) / 2; - heights[2] = (frame->height + 1) / 2; - strides[2] = frame ? frame->linesize[2] : (frame->width + 1) / 2; - break; - - case AV_PIX_FMT_P010LE: - *plane_count = 2; - widths[0] = frame->width; - heights[0] = frame->height; - strides[0] = frame ? frame->linesize[0] : (frame->width * 2 + 63) & -64; - - widths[1] = (frame->width + 1) / 2; - heights[1] = (frame->height + 1) / 2; - strides[1] = frame ? frame->linesize[1] : ((frame->width + 1) / 2 + 63) & -64; - break; - - default: - av_log(NULL, AV_LOG_ERROR, "Could not get frame format info for color %d range %d.\n", av_format, av_color_range); - - return AVERROR(EINVAL); - } - - *contiguous_buf_size = 0; - for (i = 0; i < *plane_count; i++) { - if (i < *plane_count - 1 && frame->data[i] + strides[i] * heights[i] != frame->data[i + 1]) { - *contiguous_buf_size = 0; - break; - } - - *contiguous_buf_size += strides[i] * heights[i]; - } - - return 0; -} - -static AVFrame *yuv420p2nv12(AVFrame *frame) -{ - int x, y; - AVFrame *outFrame = av_frame_alloc(); - outFrame->format = AV_PIX_FMT_NV12; - outFrame->width = frame->width; - outFrame->height = frame->height; - - int ret = av_frame_get_buffer(outFrame, 32); - if (ret < 0) { - av_frame_free(&outFrame); - return NULL; - } - ret = av_frame_make_writable(outFrame); - if (ret < 0) { - av_frame_free(&outFrame); - return NULL; - } - if (frame->linesize[0] == frame->width) { - memcpy(outFrame->data[0], frame->data[0], outFrame->width * frame->height); - } else { - for (y = 0; y < outFrame->height; ++y) { - for (x = 0; x < outFrame->width; ++x) { - outFrame->data[0][y * outFrame->linesize[0] + x] = frame->data[0][y * frame->linesize[0] + x]; - } - } - } - - for (y = 0; y < outFrame->height / 2; ++y) { - for (x = 0; x < outFrame->width / 2; ++x) { - outFrame->data[1][y * outFrame->linesize[1] + 2 * x] = frame->data[1][y * frame->linesize[1] + x]; - outFrame->data[1][y * outFrame->linesize[1] + 2 * x + 1] = frame->data[2][y * frame->linesize[2] + x]; - } - } - - return outFrame; -} - -CVPixelBufferRef avFrame2pixelBuffer(AVFrame *frame) -{ - int plane_count; - int color; - size_t widths[AV_NUM_DATA_POINTERS]; - size_t heights[AV_NUM_DATA_POINTERS]; - size_t strides[AV_NUM_DATA_POINTERS]; - int status; - size_t contiguous_buf_size; - CVPixelBufferPoolRef pix_buf_pool; - memset(widths, 0, sizeof(widths)); - memset(heights, 0, sizeof(heights)); - memset(strides, 0, sizeof(strides)); - status = get_cv_pixel_info(frame, &color, &plane_count, widths, heights, strides, &contiguous_buf_size); - if (status) { - AF_LOGE("Error: Cannot convert format %d color_range %d: %d\n", frame->format, frame->color_range, status); - - return NULL; - } - - CVPixelBufferRef pixelBuffer; - OSType pixelFormat; - if (frame->color_range == AVCOL_RANGE_MPEG) { - pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; - } else - pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; - - CFMutableDictionaryRef buffer_attributes; - CFMutableDictionaryRef io_surface_properties; - CFNumberRef cv_pix_fmt; - CFNumberRef w; - CFNumberRef h; - w = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &frame->width); - h = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &frame->height); - - cv_pix_fmt = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pixelFormat); - - buffer_attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - io_surface_properties = - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - CFDictionarySetValue(buffer_attributes, kCVPixelBufferPixelFormatTypeKey, cv_pix_fmt); - CFDictionarySetValue(buffer_attributes, kCVPixelBufferIOSurfacePropertiesKey, io_surface_properties); - CFDictionarySetValue(buffer_attributes, kCVPixelBufferWidthKey, w); - CFDictionarySetValue(buffer_attributes, kCVPixelBufferHeightKey, h); -#if TARGET_OS_IPHONE - CFDictionarySetValue(buffer_attributes, kCVPixelBufferOpenGLESCompatibilityKey, kCFBooleanTrue); -#else - CFDictionarySetValue(buffer_attributes, kCVPixelBufferIOSurfaceOpenGLTextureCompatibilityKey, kCFBooleanTrue); -#endif - - - status = CVPixelBufferCreate(kCFAllocatorDefault, frame->width, frame->height, pixelFormat, buffer_attributes, &pixelBuffer); - CFRelease(io_surface_properties); - CFRelease(buffer_attributes); - CFRelease(w); - CFRelease(h); - - if (status) { - AF_LOGE("Could not create pixel buffer from pool: %d.\n", status); - return NULL; - } - - AVFrame *nv12Frame = yuv420p2nv12(frame); - - status = copy_avframe_to_pixel_buffer(nv12Frame, pixelBuffer, strides, heights); - av_frame_free(&nv12Frame); - if (status) { - CFRelease(pixelBuffer); - pixelBuffer = NULL; - } - return pixelBuffer; -} \ No newline at end of file diff --git a/framework/base/media/avFrame2pixelBuffer.h b/framework/base/media/avFrame2pixelBuffer.h deleted file mode 100644 index 6a68fa6e4..000000000 --- a/framework/base/media/avFrame2pixelBuffer.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by moqi on 2020/6/5. -// - -#ifndef CICADAMEDIA_AVFRAME2PIXELBUFFER_H -#define CICADAMEDIA_AVFRAME2PIXELBUFFER_H - -#include -#ifdef __cplusplus -extern "C" { -#endif -#include -CVPixelBufferRef avFrame2pixelBuffer(AVFrame *frame); - -#ifdef __cplusplus -}; -#endif - -#endif//CICADAMEDIA_AVFRAME2PIXELBUFFER_H diff --git a/framework/base/media/subTitlePacket.cpp b/framework/base/media/subTitlePacket.cpp index 7c94e5cf3..50e832b72 100644 --- a/framework/base/media/subTitlePacket.cpp +++ b/framework/base/media/subTitlePacket.cpp @@ -32,7 +32,7 @@ uint8_t *subTitlePacket::getData() return mpBuffer; } -std::unique_ptr subTitlePacket::clone() +std::unique_ptr subTitlePacket::clone() const { return std::unique_ptr(new subTitlePacket(mpBuffer, mSize, mInfo.pts, mInfo.duration)); } diff --git a/framework/base/media/subTitlePacket.h b/framework/base/media/subTitlePacket.h index 1f1944610..a908726ea 100644 --- a/framework/base/media/subTitlePacket.h +++ b/framework/base/media/subTitlePacket.h @@ -14,7 +14,7 @@ class subTitlePacket : public IAFPacket { ~subTitlePacket() override; - std::unique_ptr clone() override; + std::unique_ptr clone() const override; uint8_t *getData() override; diff --git a/framework/base/options.cpp b/framework/base/options.cpp index 75cfa43e7..12687ab0c 100644 --- a/framework/base/options.cpp +++ b/framework/base/options.cpp @@ -38,3 +38,7 @@ void Cicada::options::reset() { mDict.clear(); } + +std::map Cicada::options::getOptions() { + return mDict; +} diff --git a/framework/base/options.h b/framework/base/options.h index d08357b09..634fe7a3e 100644 --- a/framework/base/options.h +++ b/framework/base/options.h @@ -25,6 +25,8 @@ namespace Cicada { void reset(); + std::map getOptions(); + private: std::map mDict; }; diff --git a/framework/cacheModule/CacheManager.cpp b/framework/cacheModule/CacheManager.cpp index 2392b2a23..faede0a51 100644 --- a/framework/cacheModule/CacheManager.cpp +++ b/framework/cacheModule/CacheManager.cpp @@ -5,9 +5,10 @@ #include "CacheManager.h" #include +#include +#include #include #include -#include CacheManager::CacheManager() { @@ -69,7 +70,7 @@ string CacheManager::init() return mSourceUrl; } -void CacheManager::sendMediaFrame(const unique_ptr &frame, StreamType type) +void CacheManager::sendMediaFrame(const IAFPacket *frame, StreamType type) { if (!mNeedProcessFrame) { return; diff --git a/framework/cacheModule/CacheManager.h b/framework/cacheModule/CacheManager.h index 3c23b0495..ba153a6c8 100644 --- a/framework/cacheModule/CacheManager.h +++ b/framework/cacheModule/CacheManager.h @@ -53,7 +53,7 @@ class CacheManager { void setCacheSuccessCallback(function resultCallback); - void sendMediaFrame(const std::unique_ptr &frame, StreamType type); + void sendMediaFrame(const IAFPacket *frame, StreamType type); private: std::mutex mStopMutex{}; diff --git a/framework/cacheModule/CacheModule.cpp b/framework/cacheModule/CacheModule.cpp index f979548b2..f329eca16 100644 --- a/framework/cacheModule/CacheModule.cpp +++ b/framework/cacheModule/CacheModule.cpp @@ -196,7 +196,7 @@ CacheRet CacheModule::checkCanCache() } -void CacheModule::addFrame(const unique_ptr &frame, StreamType type) +void CacheModule::addFrame(const IAFPacket *frame, StreamType type) { std::unique_lock lock(mReumxerMutex); diff --git a/framework/cacheModule/CacheModule.h b/framework/cacheModule/CacheModule.h index 3e0601abc..4adfbac5b 100644 --- a/framework/cacheModule/CacheModule.h +++ b/framework/cacheModule/CacheModule.h @@ -60,7 +60,7 @@ class CacheModule { bool isMediaInfoSet(); - void addFrame(const unique_ptr& frame, StreamType type); + void addFrame(const IAFPacket *frame, StreamType type); void setErrorCallback(function callback); diff --git a/framework/cacheModule/cache/CacheFileRemuxer.cpp b/framework/cacheModule/cache/CacheFileRemuxer.cpp index 0f408a2bd..99f7d1e2c 100644 --- a/framework/cacheModule/cache/CacheFileRemuxer.cpp +++ b/framework/cacheModule/cache/CacheFileRemuxer.cpp @@ -41,7 +41,7 @@ CacheFileRemuxer::~CacheFileRemuxer() mFrameInfoQueue.clear(); } -void CacheFileRemuxer::addFrame(const unique_ptr &frame, StreamType type) +void CacheFileRemuxer::addFrame(const IAFPacket *frame, StreamType type) { if (frame == nullptr) { mFrameEof = true; diff --git a/framework/cacheModule/cache/CacheFileRemuxer.h b/framework/cacheModule/cache/CacheFileRemuxer.h index 227a95643..6d7c8687f 100644 --- a/framework/cacheModule/cache/CacheFileRemuxer.h +++ b/framework/cacheModule/cache/CacheFileRemuxer.h @@ -36,7 +36,7 @@ class CacheFileRemuxer { ~CacheFileRemuxer(); - void addFrame(const unique_ptr &frame, StreamType type); + void addFrame(const IAFPacket *frame, StreamType type); bool prepare(); diff --git a/framework/codec/ActiveDecoder.cpp b/framework/codec/ActiveDecoder.cpp index 904b47a30..b60d2519b 100644 --- a/framework/codec/ActiveDecoder.cpp +++ b/framework/codec/ActiveDecoder.cpp @@ -5,20 +5,28 @@ #include "ActiveDecoder.h" #include "utils/timer.h" +#include //TODO: can set -#define MAX_INPUT_SIZE 16 +#define MAX_OUTPUT_SIZE 16 + +/* + * input queue will hold all the packets in holding queue, so the input queue must + * big enough for now + */ +#define MAX_INPUT_SIZE (20 * 30) + +// TODO: use mHoldingQueue and mInputQueue in decode func, so we can decrease the MAX_INPUT_SIZE using namespace std; -ActiveDecoder::ActiveDecoder() : mInputQueue(MAX_INPUT_SIZE), mOutputQueue(MAX_INPUT_SIZE) +ActiveDecoder::ActiveDecoder() : mInputQueue(MAX_INPUT_SIZE), mOutputQueue(MAX_OUTPUT_SIZE) { - mFlags = 0; } -int ActiveDecoder::open(const Stream_meta *meta, void *voutObsr, uint64_t flags) +int ActiveDecoder::open(const Stream_meta *meta, void *voutObsr, uint64_t flags , const Cicada::DrmInfo *drmInfo) { - int ret = init_decoder(meta, voutObsr, flags); + int ret = init_decoder(meta, voutObsr, flags , drmInfo); if (ret < 0) { close(); @@ -159,7 +167,7 @@ bool ActiveDecoder::needDrop(IAFPacket *packet) keyPts = packet->getInfo().pts; return false; } - } else if (packet->getInfo().flags) { + } else if (packet->getInfo().flags & AF_PKT_FLAG_KEY) { keyPts = INT64_MIN;// get the next key frame, stop to check } @@ -168,7 +176,7 @@ bool ActiveDecoder::needDrop(IAFPacket *packet) return false; } - if (packet->getInfo().pts < keyPts) {// after get the key frame, check the wrong frame use pts + if (packet->getInfo().pts != INT64_MIN && packet->getInfo().pts < keyPts) {// after get the key frame, check the wrong frame use pts AF_LOGW("key pts is %lld,pts is %lld\n", keyPts, packet->getInfo().pts); AF_LOGW("drop a error frame\n"); return true; @@ -219,7 +227,7 @@ int ActiveDecoder::thread_send_packet(unique_ptr &packet) unique_lock uMutex(mMutex); if (bHolding) { - if (packet->getInfo().flags) { + if (packet->getInfo().flags & AF_PKT_FLAG_KEY) { while (!mHoldingQueue.empty()) { mHoldingQueue.pop(); } @@ -234,7 +242,7 @@ int ActiveDecoder::thread_send_packet(unique_ptr &packet) // AF_LOGD("mInputQueue.size() is %d\n",mInputQueue.size()); - if ((mInputQueue.size() >= MAX_INPUT_SIZE) || (mOutputQueue.size() >= maxOutQueueSize)) { + if ((mInputQueue.size() >= maxInQueueSize) || (mOutputQueue.size() >= maxOutQueueSize)) { // TODO: wait for timeOut us status |= STATUS_RETRY_IN; } else { @@ -311,9 +319,14 @@ ActiveDecoder::~ActiveDecoder() void ActiveDecoder::flush() { #if AF_HAVE_PTHREAD - bool running = mDecodeThread->getStatus() == afThread::THREAD_STATUS_RUNNING; + bool running = false; + if (mDecodeThread) { + running = mDecodeThread->getStatus() == afThread::THREAD_STATUS_RUNNING; + } mRunning = false; - mDecodeThread->pause(); + if (mDecodeThread) { + mDecodeThread->pause(); + } while (!mInputQueue.empty()) { delete mInputQueue.front(); @@ -335,7 +348,9 @@ void ActiveDecoder::flush() bInputEOS = false; bDecoderEOS = false; bSendEOS2Decoder = false; - mRunning = true; + if (running) { + mRunning = true; + } bNeedKeyFrame = true; #if AF_HAVE_PTHREAD @@ -346,7 +361,7 @@ void ActiveDecoder::flush() #endif } -void ActiveDecoder::preClose() +void ActiveDecoder::prePause() { #if AF_HAVE_PTHREAD { @@ -362,6 +377,29 @@ void ActiveDecoder::preClose() #endif } +void ActiveDecoder::pause(bool pause) +{ +#if AF_HAVE_PTHREAD + if (pause) { + { + std::unique_lock locker(mSleepMutex); + mRunning = false; + } + mSleepCondition.notify_one(); + + if (mDecodeThread) { + mDecodeThread->pause(); + } + return; + } else { + mRunning = true; + if (mDecodeThread) { + mDecodeThread->start(); + } + } +#endif +} + #if AF_HAVE_PTHREAD int ActiveDecoder::extract_decoder() @@ -407,7 +445,9 @@ int ActiveDecoder::holdOn(bool hold) if (hold) { #if AF_HAVE_PTHREAD mRunning = false; - mDecodeThread->pause(); + if (mDecodeThread) { + mDecodeThread->pause(); + } #endif while (!mInputQueue.empty()) { mInputQueue.front()->setDiscard(true); @@ -423,6 +463,9 @@ int ActiveDecoder::holdOn(bool hold) AF_LOGD("mHoldingQueue size is %d\n", mHoldingQueue.size()); int64_t pts = 0; + if (mInputQueue.write_available() < mHoldingQueue.size()) { + AF_LOGW("mHoldingQueue is too big(%lld), please increase the input queue size\n", mHoldingQueue.size()); + } while (!mHoldingQueue.empty()) { mHoldingQueue.front()->setDiscard(true); @@ -441,7 +484,9 @@ int ActiveDecoder::holdOn(bool hold) #if AF_HAVE_PTHREAD mRunning = true; #endif - mDecodeThread->start(); + if (mDecodeThread) { + mDecodeThread->start(); + } return 0; } @@ -450,5 +495,9 @@ int ActiveDecoder::getRecoverQueueSize() unique_lock uMutex(mMutex); return int(mHoldingQueue.size() + get_decoder_recover_size()); } +uint32_t ActiveDecoder::getInputPaddingSize() +{ + return mInputQueue.size(); +} #endif diff --git a/framework/codec/ActiveDecoder.h b/framework/codec/ActiveDecoder.h index 7f27ed656..4f49c85fd 100644 --- a/framework/codec/ActiveDecoder.h +++ b/framework/codec/ActiveDecoder.h @@ -17,15 +17,17 @@ #include #include #include +#include +#include -class ActiveDecoder : public Cicada::IDecoder { +class CICADA_CPLUS_EXTERN ActiveDecoder : public Cicada::IDecoder { public: ActiveDecoder(); virtual ~ActiveDecoder() override; - int open(const Stream_meta *meta, void *voutObsr, uint64_t flags) override; + int open(const Stream_meta *meta, void *voutObsr, uint64_t flags, const Cicada::DrmInfo *drmInfo) override; void close() override; @@ -33,21 +35,26 @@ class ActiveDecoder : public Cicada::IDecoder { int getFrame(std::unique_ptr &frame, uint64_t timeOut) override; + + void pause(bool pause) override; + void flush() override; - void preClose() override; + void prePause() override; int holdOn(bool hold) override; int getRecoverQueueSize() override; + uint32_t getInputPaddingSize() override; + private: virtual int enqueue_decoder(std::unique_ptr &pPacket) = 0; virtual int dequeue_decoder(std::unique_ptr &pFrame) = 0; - virtual int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags) = 0; + virtual int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags, const Cicada::DrmInfo *drmInfo) = 0; virtual void close_decoder() = 0; @@ -89,10 +96,11 @@ class ActiveDecoder : public Cicada::IDecoder { Cicada::SpscQueue mInputQueue; Cicada::SpscQueue mOutputQueue; int maxOutQueueSize = 10; + int maxInQueueSize = 16; std::mutex mMutex{}; std::mutex mSleepMutex{}; #endif - bool bHolding = false; + std::atomic_bool bHolding{false}; std::queue> mHoldingQueue{}; enum AFCodecID mCodecId{AF_CODEC_ID_NONE}; }; diff --git a/framework/codec/Android/jni/JEncryptionInfo.cpp b/framework/codec/Android/jni/JEncryptionInfo.cpp new file mode 100644 index 000000000..db9446927 --- /dev/null +++ b/framework/codec/Android/jni/JEncryptionInfo.cpp @@ -0,0 +1,102 @@ +// +// Created by SuperMan on 2020/10/19. +// + +#include +#include +#include +#include +#include "JEncryptionInfo.h" + +jclass jEncryptionInfo_class = nullptr; +jmethodID jEncryptionInfo_init = nullptr; +jfieldID jEncryptionInfo_crypt_byte_block = nullptr; +jfieldID jEncryptionInfo_skip_byte_block = nullptr; +jmethodID jEncryptionInfo_setScheme = nullptr; +jmethodID jEncryptionInfo_setKeyId = nullptr; +jmethodID jEncryptionInfo_setIv = nullptr; +jmethodID jEncryptionInfo_setSubsamples = nullptr; + +jclass jSubsampleEncryptionInfo_class = nullptr; +jmethodID jSubsampleEncryptionInfo_init = nullptr; +jfieldID jSubsampleEncryptionInfo_bytes_of_clear_data = nullptr; +jfieldID jSubsampleEncryptionInfo_bytes_of_protected_data = nullptr; + +void JEncryptionInfo::init(JNIEnv *env) { + if (jEncryptionInfo_class == nullptr) { + FindClass infoClass(env, "com/cicada/player/utils/media/EncryptionInfo"); + jEncryptionInfo_class = (jclass) (env->NewGlobalRef(infoClass.getClass())); + + jEncryptionInfo_init = env->GetMethodID(jEncryptionInfo_class, "", "()V"); + jEncryptionInfo_setScheme = env->GetMethodID(jEncryptionInfo_class, "setScheme", + "(Ljava/lang/String;)V"); + jEncryptionInfo_crypt_byte_block = env->GetFieldID(jEncryptionInfo_class, + "crypt_byte_block", "I"); + jEncryptionInfo_skip_byte_block = env->GetFieldID(jEncryptionInfo_class, "skip_byte_block", + "I"); + jEncryptionInfo_setKeyId = env->GetMethodID(jEncryptionInfo_class, "setKeyId", "([B)V"); + jEncryptionInfo_setIv = env->GetMethodID(jEncryptionInfo_class, "setIv", "([B)V"); + jEncryptionInfo_setSubsamples = env->GetMethodID(jEncryptionInfo_class, "setSubsamples", + "(Ljava/lang/Object;)V"); + } + if (jSubsampleEncryptionInfo_class == nullptr) { + FindClass infoClass(env, "com/cicada/player/utils/media/SubsampleEncryptionInfo"); + jSubsampleEncryptionInfo_class = (jclass) (env->NewGlobalRef(infoClass.getClass())); + jSubsampleEncryptionInfo_init = env->GetMethodID(jSubsampleEncryptionInfo_class, "", + "()V"); + jSubsampleEncryptionInfo_bytes_of_clear_data = env->GetFieldID( + jSubsampleEncryptionInfo_class, "bytes_of_clear_data", "I"); + jSubsampleEncryptionInfo_bytes_of_protected_data = env->GetFieldID( + jSubsampleEncryptionInfo_class, "bytes_of_protected_data", "I"); + } + + +} + +void JEncryptionInfo::unInit(JNIEnv *env) { + if (jEncryptionInfo_class != nullptr) { + env->DeleteGlobalRef(jEncryptionInfo_class); + jEncryptionInfo_class = nullptr; + } + + if (jSubsampleEncryptionInfo_class != nullptr) { + env->DeleteGlobalRef(jSubsampleEncryptionInfo_class); + jSubsampleEncryptionInfo_class = nullptr; + } + +} + +jobject JEncryptionInfo::convert(JNIEnv *env, IAFPacket::EncryptionInfo *info) { + + jobject jEncryptionInfo = env->NewObject(jEncryptionInfo_class, jEncryptionInfo_init); + NewStringUTF scheme(env, info->scheme.c_str()); + env->CallVoidMethod(jEncryptionInfo, jEncryptionInfo_setScheme, scheme.getString()); + env->SetIntField(jEncryptionInfo, jEncryptionInfo_crypt_byte_block, info->crypt_byte_block); + env->SetIntField(jEncryptionInfo, jEncryptionInfo_skip_byte_block, info->skip_byte_block); + + NewByteArray key(env, info->key_id, info->key_id_size); + env->CallVoidMethod(jEncryptionInfo, jEncryptionInfo_setKeyId, key.getArray()); + + NewByteArray iv(env, info->iv, info->iv_size); + env->CallVoidMethod(jEncryptionInfo, jEncryptionInfo_setIv, iv.getArray()); + + if (info->subsample_count > 0) { + NewLinkedList subsmaplesList(env); + for (auto &subsampeInfo : info->subsamples) { + jobject jSubSampleInfo = env->NewObject(jSubsampleEncryptionInfo_class, + jSubsampleEncryptionInfo_init); + env->SetIntField(jSubSampleInfo, jSubsampleEncryptionInfo_bytes_of_clear_data, + subsampeInfo.bytes_of_clear_data); + env->SetIntField(jSubSampleInfo, jSubsampleEncryptionInfo_bytes_of_protected_data, + subsampeInfo.bytes_of_protected_data); + + subsmaplesList.add(jSubSampleInfo); + env->DeleteLocalRef(jSubSampleInfo); + } + + jobject pJobject = subsmaplesList.getList(); + env->CallVoidMethod(jEncryptionInfo , jEncryptionInfo_setSubsamples , pJobject); + } + + return jEncryptionInfo; +} diff --git a/framework/codec/Android/jni/JEncryptionInfo.h b/framework/codec/Android/jni/JEncryptionInfo.h new file mode 100644 index 000000000..9ebde57a4 --- /dev/null +++ b/framework/codec/Android/jni/JEncryptionInfo.h @@ -0,0 +1,23 @@ +// +// Created by SuperMan on 2020/10/19. +// + +#ifndef SOURCE_JENCRYPTIONINFO_H +#define SOURCE_JENCRYPTIONINFO_H + + +#include +#include + +class JEncryptionInfo { +public: + static void init(JNIEnv* env); + + static void unInit(JNIEnv* env); + +public: + static jobject convert(JNIEnv* env, IAFPacket::EncryptionInfo* info); +}; + + +#endif //SOURCE_JENCRYPTIONINFO_H diff --git a/framework/codec/Android/jni/MediaCodec_Decoder.cpp b/framework/codec/Android/jni/MediaCodec_Decoder.cpp new file mode 100644 index 000000000..64cf9d161 --- /dev/null +++ b/framework/codec/Android/jni/MediaCodec_Decoder.cpp @@ -0,0 +1,471 @@ +// +// Created by SuperMan on 2020/10/13. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include "MediaCodec_Decoder.h" +#include "OutputBufferInfo.h" +#include "JEncryptionInfo.h" + +using namespace Cicada; + +static jclass jMediaCodecClass = nullptr; +static jmethodID jMediaCodec_init = nullptr; +static jmethodID jMediaCodec_setCodecSpecificData = nullptr; +static jmethodID jMediaCodec_setDrmInfo = nullptr; +static jmethodID jMediaCodec_setForceInsecureDecoder = nullptr; +static jmethodID jMediaCodec_configureVideo = nullptr; +static jmethodID jMediaCodec_configureAudio = nullptr; +static jmethodID jMediaCodec_start = nullptr; +static jmethodID jMediaCodec_flush = nullptr; +static jmethodID jMediaCodec_stop = nullptr; +static jmethodID jMediaCodec_release = nullptr; +static jmethodID jMediaCodec_releaseOutputBuffer = nullptr; +static jmethodID jMediaCodec_dequeueInputBufferIndex = nullptr; +static jmethodID jMediaCodec_queueInputBuffer = nullptr; +static jmethodID jMediaCodec_queueSecureInputBuffer = nullptr; +static jmethodID jMediaCodec_dequeueOutputBufferIndex = nullptr; +static jmethodID jMediaCodec_getOutputBufferInfo = nullptr; +static jmethodID jMediaCodec_getOutBuffer = nullptr; + +void MediaCodec_Decoder::init(JNIEnv *env) { + if (env == nullptr) { + return; + } + + if (jMediaCodecClass == nullptr) { + FindClass jClass(env, "com/cicada/player/utils/media/MediaCodecDecoder"); + jMediaCodecClass = static_cast(env->NewGlobalRef(jClass.getClass())); + jMediaCodec_init = env->GetMethodID(jMediaCodecClass, "", "()V"); + jMediaCodec_setCodecSpecificData = env->GetMethodID(jMediaCodecClass, + "setCodecSpecificData", "(Ljava/lang/Object;)V"); + jMediaCodec_setDrmInfo = env->GetMethodID(jMediaCodecClass, "setDrmInfo", + "(Ljava/lang/String;[B)Z"); + jMediaCodec_setForceInsecureDecoder = env->GetMethodID(jMediaCodecClass, "setForceInsecureDecoder", + "(Z)V"); + jMediaCodec_configureVideo = env->GetMethodID(jMediaCodecClass, "configureVideo", + "(Ljava/lang/String;IIILjava/lang/Object;)I"); + jMediaCodec_configureAudio = env->GetMethodID(jMediaCodecClass, "configureAudio", + "(Ljava/lang/String;III)I"); + jMediaCodec_start = env->GetMethodID(jMediaCodecClass, "start", "()I"); + jMediaCodec_flush = env->GetMethodID(jMediaCodecClass, "flush", "()I"); + jMediaCodec_stop = env->GetMethodID(jMediaCodecClass, "stop", "()I"); + jMediaCodec_release = env->GetMethodID(jMediaCodecClass, "release", "()I"); + jMediaCodec_releaseOutputBuffer = env->GetMethodID(jMediaCodecClass, "releaseOutputBuffer", + "(IZ)I"); + jMediaCodec_dequeueInputBufferIndex = env->GetMethodID(jMediaCodecClass, + "dequeueInputBufferIndex", "(J)I"); + jMediaCodec_queueInputBuffer = env->GetMethodID(jMediaCodecClass, "queueInputBuffer", + "(I[BJZ)I"); + jMediaCodec_queueSecureInputBuffer = env->GetMethodID(jMediaCodecClass, + "queueSecureInputBuffer", + "(I[BLjava/lang/Object;JZ)I"); + jMediaCodec_dequeueOutputBufferIndex = env->GetMethodID(jMediaCodecClass, + "dequeueOutputBufferIndex", "(J)I"); + jMediaCodec_getOutputBufferInfo = env->GetMethodID(jMediaCodecClass, "getOutputBufferInfo", + "(I)Ljava/lang/Object;"); + jMediaCodec_getOutBuffer = env->GetMethodID(jMediaCodecClass, "getOutBuffer", + "(I)Ljava/lang/Object;"); + } + + +} + +void MediaCodec_Decoder::unInit(JNIEnv *env) { + if (env == nullptr) { + return; + } + if (jMediaCodecClass != nullptr) { + env->DeleteGlobalRef(jMediaCodecClass); + jMediaCodecClass = nullptr; + } +} + + +MediaCodec_Decoder::MediaCodec_Decoder() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return; + } + + jobject pJobject = env->NewObject(jMediaCodecClass, jMediaCodec_init); + mMediaCodec = env->NewGlobalRef(pJobject); + env->DeleteLocalRef(pJobject); +} + +MediaCodec_Decoder::~MediaCodec_Decoder() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return; + } + + if (mMediaCodec != nullptr) { + env->DeleteGlobalRef(mMediaCodec); + } +} + +void MediaCodec_Decoder::setCodecSpecificData(std::list csds) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return; + } + + if(csds.empty()){ + return; + } + + NewHashMap csdMap(env); + + for (CodecSpecificData & data: csds) { + std::string key = data.key; + + NewStringUTF keyStr(env, key.c_str()); + NewByteArray csdData(env , data.buffer, data.len); + csdMap.put(keyStr.getString(),csdData.getArray()); + } + + env->CallVoidMethod(mMediaCodec, jMediaCodec_setCodecSpecificData, csdMap.getMap()); + +} + +int MediaCodec_Decoder::setDrmInfo(const std::string &uuid, const void *sessionId, int size) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + NewStringUTF jKeyUrl(env, uuid.c_str()); + NewByteArray jSessionId(env, sessionId, size); + bool ret = env->CallBooleanMethod(mMediaCodec, jMediaCodec_setDrmInfo, jKeyUrl.getString(), + jSessionId.getArray()); + + return ret? 0 : MC_ERROR; +} + +void MediaCodec_Decoder::setForceInsecureDecoder(bool force) +{ + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return ; + } + + env->CallVoidMethod(mMediaCodec, jMediaCodec_setForceInsecureDecoder, (jboolean)force); + +} + +int MediaCodec_Decoder::configureVideo(const std::string &mime, int width, int height, int angle, + void *surface) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + mCodecCategory = CATEGORY_VIDEO; + NewStringUTF jMime(env, mime.c_str()); + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_configureVideo, jMime.getString(), + (jint) width, (jint) height, (jint) angle, (jobject) surface); + + return ret; +} + +int MediaCodec_Decoder::configureAudio(const std::string &mime, int sampleRate, int channelCount, + int isADTS) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + mCodecCategory = CATEGORY_AUDIO; + NewStringUTF jMime(env, mime.c_str()); + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_configureAudio, jMime.getString(), + (jint) sampleRate, (jint) channelCount, (jint) isADTS); + + return ret; +} + +int MediaCodec_Decoder::start() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_start); + + return ret; +} + +int MediaCodec_Decoder::flush() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_flush); + + return ret; +} + +int MediaCodec_Decoder::stop() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_stop); + + return ret; +} + +int MediaCodec_Decoder::release() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_release); + + return ret; +} + +int MediaCodec_Decoder::dequeueInputBufferIndex(int64_t timeoutUs) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_dequeueInputBufferIndex, + (jlong) timeoutUs); + return ret; +} + +int MediaCodec_Decoder::dequeueOutputBufferIndex(int64_t timeoutUs) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_dequeueOutputBufferIndex, + (jlong) timeoutUs); + + return ret; +} + +int MediaCodec_Decoder::queueInputBuffer(int index, void *buffer, size_t size, int64_t pts, + bool isConfig) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + jbyteArray jBuffer = nullptr; + if (buffer != nullptr) { + jBuffer = env->NewByteArray(size); + env->SetByteArrayRegion(jBuffer, 0, size, (jbyte *) (buffer)); + } + + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_queueInputBuffer, (jint) index, jBuffer, + (jlong) pts, (jboolean) isConfig); + if (jBuffer != nullptr) { + env->DeleteLocalRef(jBuffer); + } + return ret; +} + +int MediaCodec_Decoder::queueSecureInputBuffer(int index, void *buffer, size_t size, + IAFPacket::EncryptionInfo *pEncryptionInfo, + int64_t pts, bool isConfig) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + jbyteArray jBuffer = nullptr; + if (buffer != nullptr) { + jBuffer = env->NewByteArray(size); + env->SetByteArrayRegion(jBuffer, 0, size, (jbyte *) (buffer)); + } + + jobject encryptionInfo = nullptr; + if (pEncryptionInfo != nullptr) { + encryptionInfo = JEncryptionInfo::convert(env, pEncryptionInfo); + } + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_queueSecureInputBuffer, (jint) index, + jBuffer, + encryptionInfo, (jlong) pts, (jboolean) isConfig); + if (jBuffer != nullptr) { + env->DeleteLocalRef(jBuffer); + } + + if(encryptionInfo != nullptr){ + env->DeleteLocalRef(encryptionInfo); + } + + return ret; +} + +int MediaCodec_Decoder::getOutput(int index, mc_out *out, bool readBuffer) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + OutputBufferInfo outputBufferInfo{}; + + jobject outputInfo = env->CallObjectMethod(mMediaCodec, jMediaCodec_getOutputBufferInfo, + (jint) index); + if (outputInfo != nullptr) { + OutputBufferInfo::convert(env, &outputBufferInfo, outputInfo); + env->DeleteLocalRef(outputInfo); + } + + if (index >= 0) { + out->type = outputBufferInfo.type; + out->b_eos = outputBufferInfo.eos; + out->buf.index = outputBufferInfo.index; + out->buf.pts = outputBufferInfo.pts; + + if (readBuffer) { + jobject bufferInfo = env->CallObjectMethod(mMediaCodec, jMediaCodec_getOutBuffer, + (jint) index); + if (bufferInfo != nullptr) { + uint8_t *ptr = (uint8_t *) env->GetDirectBufferAddress(bufferInfo); + int offset = outputBufferInfo.bufferOffset; + out->buf.p_ptr = ptr + offset; + out->buf.size = outputBufferInfo.bufferSize; + + env->DeleteLocalRef(bufferInfo); + } + } else { + out->buf.p_ptr = nullptr; + out->buf.size = 0; + } + + } else if (index == MC_INFO_OUTPUT_FORMAT_CHANGED) { + out->type = outputBufferInfo.type; + out->b_eos = outputBufferInfo.eos; + + if (mCodecCategory == CATEGORY_VIDEO) { + out->conf.video.width = outputBufferInfo.videoWidth; + out->conf.video.height = outputBufferInfo.videoHeight; + out->conf.video.stride = outputBufferInfo.videoStride; + out->conf.video.slice_height = outputBufferInfo.videoSliceHeight; + out->conf.video.pixel_format = outputBufferInfo.videoPixelFormat; + out->conf.video.crop_left = outputBufferInfo.videoCropLeft; + out->conf.video.crop_top = outputBufferInfo.videoCropTop; + out->conf.video.crop_right = outputBufferInfo.videoCropRight; + out->conf.video.crop_bottom = outputBufferInfo.videoCropBottom; + } else { + out->conf.audio.channel_count = outputBufferInfo.audioChannelCount; + out->conf.audio.channel_mask = outputBufferInfo.audioChannelMask; + out->conf.audio.sample_rate = outputBufferInfo.audioSampleRate; + out->conf.audio.format = outputBufferInfo.audioFormat; + } + } + + + return 0; +} + +int MediaCodec_Decoder::releaseOutputBuffer(int index, bool render) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return MC_ERROR; + } + + if (mMediaCodec == nullptr) { + return MC_ERROR; + } + + int ret = env->CallIntMethod(mMediaCodec, jMediaCodec_releaseOutputBuffer, (jint) index, + (jboolean) render); + return ret; +} + + + diff --git a/framework/codec/Android/jni/MediaCodec_Decoder.h b/framework/codec/Android/jni/MediaCodec_Decoder.h new file mode 100644 index 000000000..b214e9571 --- /dev/null +++ b/framework/codec/Android/jni/MediaCodec_Decoder.h @@ -0,0 +1,124 @@ +// +// Created by SuperMan on 2020/10/13. +// + +#ifndef SOURCE_MEDIACODEC_DECODER_H +#define SOURCE_MEDIACODEC_DECODER_H + +#include +#include +#include +#include + +#define MC_ERROR (-1) +#define MC_INFO_OUTPUT_FORMAT_CHANGED (-2) +#define MC_INFO_OUTPUT_BUFFERS_CHANGED (-3) +#define MC_INFO_TRYAGAIN (-11) + +#define CATEGORY_VIDEO (0) +#define CATEGORY_AUDIO (1) + +namespace Cicada { + struct mc_out { + int type; + bool b_eos; + union { + struct { + int index; + int64_t pts; + const uint8_t *p_ptr; + size_t size; + } buf; + union { + struct { + unsigned int width, height; + unsigned int stride; + unsigned int slice_height; + int pixel_format; + int crop_left; + int crop_top; + int crop_right; + int crop_bottom; + } video; + struct { + int channel_count; + int channel_mask; + int sample_rate; + int format; + } audio; + } conf; + }; + }; + + class CodecSpecificData { + public: + CodecSpecificData() = default; + + void setScd(const std::string& keyStr ,void* data, int size){ + buffer = data; + len = size; + key = keyStr; + } + + ~CodecSpecificData() { + } + + std::string key{}; + void *buffer = nullptr; + int len = 0; + }; + + class MediaCodec_Decoder { + public: + static void init(JNIEnv *env); + + static void unInit(JNIEnv *env); + + public: + + MediaCodec_Decoder(); + + ~MediaCodec_Decoder(); + + void setCodecSpecificData(std::list csds); + + int setDrmInfo(const std::string &uuid, const void *sessionId, int size); + + void setForceInsecureDecoder(bool force); + + int + configureVideo(const std::string &mime, int width, int height, int angle, void *surface); + + int configureAudio(const std::string &mime, int sampleRate, int channelCount, int isADTS); + + int start(); + + int flush(); + + int stop(); + + int release(); + + int dequeueInputBufferIndex(int64_t timeoutUs); + + int queueInputBuffer(int index, void *buffer, size_t size, int64_t pts, bool isConfig); + + int queueSecureInputBuffer(int index, void *buffer, size_t size, + IAFPacket::EncryptionInfo *pEncryptionInfo, int64_t pts, + bool isConfig); + + int dequeueOutputBufferIndex(int64_t timeoutUs); + + int getOutput(int index, mc_out *out, bool readBuffer); + + int releaseOutputBuffer(int index, bool render); + + private: + jobject mMediaCodec{nullptr}; + + int mCodecCategory{CATEGORY_VIDEO}; + }; + +} + +#endif //SOURCE_MEDIACODEC_DECODER_H diff --git a/framework/codec/Android/jni/OutputBufferInfo.cpp b/framework/codec/Android/jni/OutputBufferInfo.cpp new file mode 100644 index 000000000..fac756511 --- /dev/null +++ b/framework/codec/Android/jni/OutputBufferInfo.cpp @@ -0,0 +1,113 @@ +// +// Created by SuperMan on 2020/10/14. +// + +#include +#include "OutputBufferInfo.h" + +using namespace Cicada; + + +static jclass jOutputBufferInfoClass = nullptr; +static jfieldID jOutputBufferInfo_type = nullptr; +static jfieldID jOutputBufferInfo_index = nullptr; +static jfieldID jOutputBufferInfo_pts = nullptr; +static jfieldID jOutputBufferInfo_flags = nullptr; +static jfieldID jOutputBufferInfo_eos = nullptr; +static jfieldID jOutputBufferInfo_bufferSize = nullptr; +static jfieldID jOutputBufferInfo_bufferOffset = nullptr; +static jfieldID jOutputBufferInfo_videoWidth = nullptr; +static jfieldID jOutputBufferInfo_videoHeight = nullptr; +static jfieldID jOutputBufferInfo_videoStride = nullptr; +static jfieldID jOutputBufferInfo_videoSliceHeight = nullptr; +static jfieldID jOutputBufferInfo_videoPixelFormat = nullptr; +static jfieldID jOutputBufferInfo_videoCropLeft = nullptr; +static jfieldID jOutputBufferInfo_videoCropRight = nullptr; +static jfieldID jOutputBufferInfo_videoCropTop = nullptr; +static jfieldID jOutputBufferInfo_videoCropBottom = nullptr; +static jfieldID jOutputBufferInfo_audioChannelCount = nullptr; +static jfieldID jOutputBufferInfo_audioChannelMask = nullptr; +static jfieldID jOutputBufferInfo_audioSampleRate = nullptr; +static jfieldID jOutputBufferInfo_audioFormat = nullptr; + + +void OutputBufferInfo::init(JNIEnv *env) { + if (env == nullptr) { + return; + } + + if (jOutputBufferInfoClass == nullptr) { + FindClass jclass(env, "com/cicada/player/utils/media/OutputBufferInfo"); + jOutputBufferInfoClass = static_cast<_jclass *>(env->NewGlobalRef(jclass.getClass())); + jOutputBufferInfo_type = env->GetFieldID(jOutputBufferInfoClass, "type", "I"); + jOutputBufferInfo_index = env->GetFieldID(jOutputBufferInfoClass, "index", "I"); + jOutputBufferInfo_pts = env->GetFieldID(jOutputBufferInfoClass, "pts", "J"); + jOutputBufferInfo_flags = env->GetFieldID(jOutputBufferInfoClass, "flags", "I"); + jOutputBufferInfo_eos = env->GetFieldID(jOutputBufferInfoClass, "eos", "Z"); + jOutputBufferInfo_bufferSize = env->GetFieldID(jOutputBufferInfoClass, "bufferSize", "I"); + jOutputBufferInfo_bufferOffset = env->GetFieldID(jOutputBufferInfoClass, "bufferOffset", + "I"); + jOutputBufferInfo_videoWidth = env->GetFieldID(jOutputBufferInfoClass, "videoWidth", "I"); + jOutputBufferInfo_videoHeight = env->GetFieldID(jOutputBufferInfoClass, "videoHeight", "I"); + jOutputBufferInfo_videoStride = env->GetFieldID(jOutputBufferInfoClass, "videoStride", "I"); + jOutputBufferInfo_videoSliceHeight = env->GetFieldID(jOutputBufferInfoClass, + "videoSliceHeight", "I"); + jOutputBufferInfo_videoPixelFormat = env->GetFieldID(jOutputBufferInfoClass, + "videoPixelFormat", "I"); + jOutputBufferInfo_videoCropLeft = env->GetFieldID(jOutputBufferInfoClass, "videoCropLeft", + "I"); + jOutputBufferInfo_videoCropRight = env->GetFieldID(jOutputBufferInfoClass, "videoCropRight", + "I"); + jOutputBufferInfo_videoCropTop = env->GetFieldID(jOutputBufferInfoClass, "videoCropTop", + "I"); + jOutputBufferInfo_videoCropBottom = env->GetFieldID(jOutputBufferInfoClass, + "videoCropBottom", "I"); + jOutputBufferInfo_audioChannelCount = env->GetFieldID(jOutputBufferInfoClass, + "audioChannelCount", "I"); + jOutputBufferInfo_audioChannelMask = env->GetFieldID(jOutputBufferInfoClass, + "audioChannelMask", "I"); + jOutputBufferInfo_audioSampleRate = env->GetFieldID(jOutputBufferInfoClass, + "audioSampleRate", "I"); + jOutputBufferInfo_audioFormat = env->GetFieldID(jOutputBufferInfoClass, "audioFormat", "I"); + } +} + +void OutputBufferInfo::unInit(JNIEnv *env) { + if (env == nullptr) { + return; + } + + if (jOutputBufferInfoClass != nullptr) { + env->DeleteGlobalRef(jOutputBufferInfoClass); + jOutputBufferInfoClass = nullptr; + } +} + +void OutputBufferInfo::convert(JNIEnv *env, OutputBufferInfo *dstInfo, jobject info) { + if (env == nullptr || info == nullptr) { + return; + } + + dstInfo->type = env->GetIntField(info, jOutputBufferInfo_type); + dstInfo->eos = env->GetBooleanField(info, jOutputBufferInfo_eos); + dstInfo->index = env->GetIntField(info, jOutputBufferInfo_index); + dstInfo->flags = env->GetIntField(info, jOutputBufferInfo_flags); + dstInfo->pts = env->GetLongField(info, jOutputBufferInfo_pts); + dstInfo->bufferSize = env->GetIntField(info, jOutputBufferInfo_bufferSize); + dstInfo->bufferOffset = env->GetIntField(info, jOutputBufferInfo_bufferOffset); + + dstInfo->videoCropBottom = env->GetIntField(info, jOutputBufferInfo_videoCropBottom); + dstInfo->videoCropTop = env->GetIntField(info, jOutputBufferInfo_videoCropTop); + dstInfo->videoCropLeft = env->GetIntField(info, jOutputBufferInfo_videoCropLeft); + dstInfo->videoCropRight = env->GetIntField(info, jOutputBufferInfo_videoCropRight); + dstInfo->videoHeight = env->GetIntField(info, jOutputBufferInfo_videoHeight); + dstInfo->videoWidth = env->GetIntField(info, jOutputBufferInfo_videoWidth); + dstInfo->videoSliceHeight = env->GetIntField(info, jOutputBufferInfo_videoSliceHeight); + dstInfo->videoStride = env->GetIntField(info, jOutputBufferInfo_videoStride); + dstInfo->videoPixelFormat = env->GetIntField(info, jOutputBufferInfo_videoPixelFormat); + + dstInfo->audioChannelCount = env->GetIntField(info, jOutputBufferInfo_audioChannelCount); + dstInfo->audioChannelMask = env->GetIntField(info, jOutputBufferInfo_audioChannelMask); + dstInfo->audioSampleRate = env->GetIntField(info, jOutputBufferInfo_audioSampleRate); + dstInfo->audioFormat = env->GetIntField(info, jOutputBufferInfo_audioFormat); +} diff --git a/framework/codec/Android/jni/OutputBufferInfo.h b/framework/codec/Android/jni/OutputBufferInfo.h new file mode 100644 index 000000000..9d0e4cfef --- /dev/null +++ b/framework/codec/Android/jni/OutputBufferInfo.h @@ -0,0 +1,49 @@ +// +// Created by SuperMan on 2020/10/14. +// + +#ifndef SOURCE_OUTPUTBUFFERINFO_H +#define SOURCE_OUTPUTBUFFERINFO_H + +#include +#include + +namespace Cicada { + class OutputBufferInfo { + + public: + static void init(JNIEnv *env); + + static void unInit(JNIEnv *env); + + static void convert(JNIEnv *env, OutputBufferInfo *dstInfo, jobject info); + + public: + int type; + int index; + int64_t pts; + int flags; + bool eos; + + int bufferSize; + int bufferOffset; + + int videoWidth; + int videoHeight; + int videoStride; + int videoSliceHeight; + int videoPixelFormat; + int videoCropLeft; + int videoCropRight; + int videoCropTop; + int videoCropBottom; + + int audioChannelCount; + int audioChannelMask; + int audioSampleRate; + int audioFormat; + }; +} + + +#endif //SOURCE_OUTPUTBUFFERINFO_H diff --git a/framework/codec/Android/mediaCodec.cpp b/framework/codec/Android/mediaCodec.cpp deleted file mode 100644 index 9d11d822a..000000000 --- a/framework/codec/Android/mediaCodec.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "mediaCodec.h" - -namespace Cicada { - - static const struct { - OMX_VIDEO_AVCPROFILETYPE omx_profile; - size_t profile_idc; - } omx_to_profile_idc[] = { - {OMX_VIDEO_AVCProfileBaseline, PROFILE_H264_BASELINE}, - {OMX_VIDEO_AVCProfileMain, PROFILE_H264_MAIN}, - {OMX_VIDEO_AVCProfileExtended, PROFILE_H264_EXTENDED}, - {OMX_VIDEO_AVCProfileHigh, PROFILE_H264_HIGH}, - {OMX_VIDEO_AVCProfileHigh10, PROFILE_H264_HIGH_10}, - {OMX_VIDEO_AVCProfileHigh422, PROFILE_H264_HIGH_422}, - {OMX_VIDEO_AVCProfileHigh444, PROFILE_H264_HIGH_444}, - }; - - size_t convert_omx_to_profile_idc(OMX_VIDEO_AVCPROFILETYPE profile_type) - { - size_t array_length = sizeof(omx_to_profile_idc) / sizeof(omx_to_profile_idc[0]); - - for (size_t i = 0; i < array_length; ++i) { - if (omx_to_profile_idc[i].omx_profile == profile_type) { - return omx_to_profile_idc[i].profile_idc; - } - } - - return 0; - } - - bool OMXCodec_IsBlacklisted(const char *p_name, unsigned int i_name_len) - { - static const char *blacklisted_prefix[] = { - /* ignore OpenCore software codecs */ - "OMX.PV.", - /* The same sw codecs, renamed in ICS (perhaps also in honeycomb) */ - "OMX.google.", - /* This one has been seen on HTC One V - it behaves like it works, - * but FillBufferDone returns buffers filled with 0 bytes. The One V - * has got a working OMX.qcom.video.decoder.avc instead though. */ - "OMX.ARICENT.", - /* Use VC1 decoder for WMV3 for now */ - "OMX.SEC.WMV.Decoder", - /* This decoder does work, but has an insane latency (leading to errors - * about "main audio output playback way too late" and dropped frames). - * At least Samsung Galaxy S III (where this decoder is present) has - * got another one, OMX.SEC.mp3.dec, that works well and has a - * sensible latency. (Also, even if that one isn't found, in general, - * using SW codecs is usually more than fast enough for MP3.) */ - "OMX.SEC.MP3.Decoder", - /* black screen */ - "OMX.MTK.VIDEO.DECODER.VC1", - /* Not working or crashing (Samsung) */ - "OMX.SEC.vp8.dec", - nullptr - }; - static const char *blacklisted_suffix[] = { - /* Codecs with DRM, that don't output plain YUV data but only - * support direct rendering where the output can't be intercepted. */ - ".secure", - /* Samsung sw decoders */ - ".sw.dec", - /* Vivo sw decoders */ - ".hevcswvdec", - - nullptr - }; - - /* p_name is not '\0' terminated */ - - for (const char **pp_bl_prefix = blacklisted_prefix; *pp_bl_prefix != nullptr; - pp_bl_prefix++) { - if (!strncmp(p_name, *pp_bl_prefix, - __MIN(strlen(*pp_bl_prefix), i_name_len))) { - return true; - } - } - - for (const char **pp_bl_suffix = blacklisted_suffix; *pp_bl_suffix != nullptr; - pp_bl_suffix++) { - size_t i_suffix_len = strlen(*pp_bl_suffix); - - if (i_name_len > i_suffix_len - && !strncmp(p_name + i_name_len - i_suffix_len, *pp_bl_suffix, - i_suffix_len)) { - return true; - } - } - - return false; - } - - OMX_VIDEO_AVCPROFILETYPE selectprofiletype(int profile) - { - if (profile == OMX_VIDEO_AVCProfileBaseline) { - return OMX_VIDEO_AVCProfileBaseline; - } - - if (profile == OMX_VIDEO_AVCProfileMain) { - return OMX_VIDEO_AVCProfileMain; - } - - if (profile == OMX_VIDEO_AVCProfileExtended) { - return OMX_VIDEO_AVCProfileExtended; - } - - if (profile == OMX_VIDEO_AVCProfileHigh) { - return OMX_VIDEO_AVCProfileHigh; - } - - if (profile == OMX_VIDEO_AVCProfileHigh10) { - return OMX_VIDEO_AVCProfileHigh10; - } - - if (profile == OMX_VIDEO_AVCProfileHigh422) { - return OMX_VIDEO_AVCProfileHigh422; - } - - if (profile == OMX_VIDEO_AVCProfileHigh444) { - return OMX_VIDEO_AVCProfileHigh444; - } - - if (profile == OMX_VIDEO_AVCProfileKhronosExtensions) { - return OMX_VIDEO_AVCProfileKhronosExtensions; - } - - if (profile == OMX_VIDEO_AVCProfileVendorStartUnused) { - return OMX_VIDEO_AVCProfileVendorStartUnused; - } - - if (profile == OMX_VIDEO_AVCProfileMax) { - return OMX_VIDEO_AVCProfileMax; - } - - return OMX_VIDEO_AVCProfileMax; - } -} \ No newline at end of file diff --git a/framework/codec/Android/mediaCodec.h b/framework/codec/Android/mediaCodec.h deleted file mode 100644 index 80ebe2ae2..000000000 --- a/framework/codec/Android/mediaCodec.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef MEDIACODEC_HH -#define MEDIACODEC_HH - -#include "utils/Android/AndroidJniHandle.h" -#include -#include -#include - -#define GOT_EOS 111 - - -#define PROFILE_H264_BASELINE 66 -#define PROFILE_H264_MAIN 77 -#define PROFILE_H264_EXTENDED 88 -#define PROFILE_H264_HIGH 100 -#define PROFILE_H264_HIGH_10 110 -#define PROFILE_H264_HIGH_422 122 -#define PROFILE_H264_HIGH_444 144 -#define PROFILE_H264_HIGH_444_PREDICTIVE 244 - -#define PROFILE_H264_CAVLC_INTRA 44 -#define PROFILE_H264_SVC_BASELINE 83 -#define PROFILE_H264_SVC_HIGH 86 -#define PROFILE_H264_MVC_STEREO_HIGH 128 -#define PROFILE_H264_MVC_MULTIVIEW_HIGH 118 - -#define PROFILE_H264_MFC_HIGH 134 -#define PROFILE_H264_MVC_MULTIVIEW_DEPTH_HIGH 138 -#define PROFILE_H264_MVC_ENHANCED_MULTIVIEW_DEPTH_HIGH 139 - -#define COLOR_FORMAT_YUV420Planar_VU 19 -#define COLOR_FORMAT_YUV420SemiPlanar 21 -#define COLOR_FORMAT_YUVYCbYCr 25 -#define COLOR_FORMAT_YUV420PackedSemiPlanar 39 -#define COLOR_FORMAT_OMX_QCOM_YUV420PackedSemiPlanar64x32Tile2m8ka 2141391875 -#define COLOR_FORMAT_OMX_QCOM_YUV420PackedSemiPlanar32m 2141391876 - -#define MC_ERROR -1 -#define MC_INFO_TRYAGAIN -11 -#define MC_INFO_OUTPUT_FORMAT_CHANGED (-12) -#define MC_INFO_OUTPUT_BUFFERS_CHANGED (-13) - -/* in sync with OMXCODEC QUIRKS */ -#define MC_NO_QUIRKS 0 -#define MC_QUIRKS_NEED_CSD 0x1 -#define MC_VIDEO_QUIRKS_IGNORE_PADDING 0x2 -#define MC_VIDEO_QUIRKS_SUPPORT_INTERLACED 0x4 -#define MC_OUT_TYPE_BUF 0 -#define MC_OUT_TYPE_CONF 1 - -#define CATEGORY_VIDEO 0 -#define CATEGORY_AUDIO 1 -/* MediaCodec only QUIRKS */ -#define MC_VIDEO_QUIRKS_ADAPTIVE 0x1000 - -#ifndef __MIN -# define __MIN(a, b) ( ((a) < (b)) ? (a) : (b) ) -#endif - -namespace Cicada{ - - union mc_args { - struct { - int width; - int height; - int angle; - } video; - struct { - int sample_rate; - int channel_count; - } audio; - }; - - struct mc_out { - int type; - bool b_eos; - union { - struct { - int index; - int64_t pts; - const uint8_t *p_ptr; - size_t size; - } buf; - union { - struct { - unsigned int width, height; - unsigned int stride; - unsigned int slice_height; - int pixel_format; - int crop_left; - int crop_top; - int crop_right; - int crop_bottom; - } video; - struct { - int channel_count; - int channel_mask; - int sample_rate; - } audio; - } conf; - }; - }; - -/** -* AVC profile types, each profile indicates support for various -* performance bounds and different annexes. -*/ - typedef enum OMX_VIDEO_AVCPROFILETYPE { - OMX_VIDEO_AVCProfileBaseline = 0x01, /**< Baseline profile */ - OMX_VIDEO_AVCProfileMain = 0x02, /**< Main profile */ - OMX_VIDEO_AVCProfileExtended = 0x04, /**< Extended profile */ - OMX_VIDEO_AVCProfileHigh = 0x08, /**< High profile */ - OMX_VIDEO_AVCProfileHigh10 = 0x10, /**< High 10 profile */ - OMX_VIDEO_AVCProfileHigh422 = 0x20, /**< High 4:2:2 profile */ - OMX_VIDEO_AVCProfileHigh444 = 0x40, /**< High 4:4:4 profile */ - OMX_VIDEO_AVCProfileKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ - OMX_VIDEO_AVCProfileVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ - OMX_VIDEO_AVCProfileMax = 0x7FFFFFFF - } OMX_VIDEO_AVCPROFILETYPE; - - size_t convert_omx_to_profile_idc(OMX_VIDEO_AVCPROFILETYPE profile_type); - - bool OMXCodec_IsBlacklisted(const char *p_name, unsigned int i_name_len); - - OMX_VIDEO_AVCPROFILETYPE selectprofiletype(int profile); - - class mediaCodec { - public: - virtual ~mediaCodec() = default; - - virtual int init(const char *mime, int category, jobject surface) = 0; - - virtual void unInit() = 0; - - virtual int setOutputSurface(jobject surface) = 0; - - virtual int configure(size_t i_h264_profile, const mc_args &args) = 0; - - virtual int start() = 0; - - virtual int stop() = 0; - - virtual int flush() = 0; - - virtual int dequeue_in(int64_t timeout) = 0; - - virtual int dequeue_out(int64_t timeout) = 0; - - virtual int - queue_in(int index, const void *p_buf, size_t size, int64_t pts, bool config) = 0; - - virtual int get_out(int index, mc_out *out, bool readBuffer = true) = 0; - - virtual int release_out(int index, bool render) = 0; - }; -} - -#endif // MEDIACODEC_HH diff --git a/framework/codec/Android/mediaCodecDecoder.cpp b/framework/codec/Android/mediaCodecDecoder.cpp index 1afd228b2..1e8800e27 100644 --- a/framework/codec/Android/mediaCodecDecoder.cpp +++ b/framework/codec/Android/mediaCodecDecoder.cpp @@ -1,34 +1,51 @@ #define LOG_TAG "mediaCodecDecoder" #include "mediaCodecDecoder.h" -#include "mediaCodec.h" #include -#include #include #include #include +#include +#include +#include +#include + +extern "C" { +#include +#include +} #define MAX_INPUT_SIZE 4 using namespace std; namespace Cicada { + + typedef struct blackModelDevice { + AFCodecID codec; + string model; + } blackModelDevice; + blackModelDevice blackList[] = { + {AF_CODEC_ID_H264, "2014501"}, + {AF_CODEC_ID_HEVC, "OPPO R9tm"}, + {AF_CODEC_ID_HEVC, "OPPO A59s"}, + }; + mediaCodecDecoder mediaCodecDecoder::se(0); - mediaCodecDecoder::mediaCodecDecoder() - { + mediaCodecDecoder::mediaCodecDecoder() { AF_LOGD("android decoder use jni"); + mName = "VD.mediaCodec"; mFlags |= DECFLAG_HW; - mDecoder = new MediaCodec_JNI(); + mDecoder = new MediaCodec_Decoder(); } - mediaCodecDecoder::~mediaCodecDecoder() - { - lock_guard func_entry_lock(mFuncEntryMutex); + mediaCodecDecoder::~mediaCodecDecoder() { delete mDecoder; } - bool mediaCodecDecoder::checkSupport(AFCodecID codec, uint64_t flags, int maxSize) - { - if (codec != AF_CODEC_ID_H264 && codec != AF_CODEC_ID_HEVC) { + bool mediaCodecDecoder::checkSupport(const Stream_meta &meta, uint64_t flags, int maxSize) { + AFCodecID codec = meta.codec; + if (codec != AF_CODEC_ID_H264 && codec != AF_CODEC_ID_HEVC + && codec != AF_CODEC_ID_AAC) { return false; } @@ -44,21 +61,24 @@ namespace Cicada { } } string model = get_android_property("ro.product.model"); - AF_LOGI("phone model is %s\n", model.c_str()); - if (model == "2014501") { - return false; + for (auto device : blackList) { + if (device.codec == codec && device.model == model) { + AF_LOGI("device %d@%s is in black list\n", device.codec, device.model.c_str()); + return false; + } } return true; } - int mediaCodecDecoder::init_decoder(const Stream_meta *meta, void *voutObsr, uint64_t flags) - { + int mediaCodecDecoder::init_decoder(const Stream_meta *meta, void *voutObsr, uint64_t flags, + const DrmInfo *drmInfo) { if (meta->pixel_fmt == AF_PIX_FMT_YUV422P || meta->pixel_fmt == AF_PIX_FMT_YUVJ422P) { return -ENOSPC; } - if (!checkSupport(meta->codec, flags, max(meta->height, meta->width))) { + if (!checkSupport(*meta, flags, max(meta->height, meta->width)) || + (drmInfo != nullptr && !is_drmSupport(drmInfo))) { return -ENOSPC; } @@ -66,56 +86,165 @@ namespace Cicada { mFlags |= DECFLAG_OUT; } - const char *mime; - if (meta->codec == AF_CODEC_ID_H264) { - mime = "video/avc"; + codecType = CODEC_VIDEO; + mMime = "video/avc"; } else if (meta->codec == AF_CODEC_ID_HEVC) { - mime = "video/hevc"; + codecType = CODEC_VIDEO; + mMime = "video/hevc"; + } else if (meta->codec == AF_CODEC_ID_AAC) { + codecType = CODEC_AUDIO; + mMime = "audio/mp4a-latm"; } else { AF_LOGE("codec is %d, not support", meta->codec); return -ENOSPC; } + mMeta = *meta; + mVideoOutObser = voutObsr; + lock_guard func_entry_lock(mFuncEntryMutex); - int ret; - ret = mDecoder->init(mime, CATEGORY_VIDEO, static_cast(voutObsr)); + setCSD(meta); + + if (drmInfo != nullptr) { + if (mRequireDrmHandlerCallback != nullptr) { + mDrmHandler = dynamic_cast(mRequireDrmHandlerCallback( + *drmInfo)); + assert(mDrmHandler != nullptr); + } - if (ret == MC_ERROR || ret < 0) { - AF_LOGE("failed to init mDecoder, ret %d", ret); - mDecoder->unInit(); - return gen_framework_errno(error_class_codec, codec_error_video_device_error); + int ret = initDrmHandler(); + if (ret == -EAGAIN) { + return 0; + } else if (ret < 0) { + return ret; + } } - mc_args args{}; - args.video.width = meta->width; - args.video.height = meta->height; - args.video.angle = 0; - ret = mDecoder->configure(0, args); + return configDecoder(); + } - if (ret >= 0) { - ret = 0; - } else { - AF_LOGE("failed to config mDecoder rv %d", ret); - mDecoder->unInit(); - ret = gen_framework_errno(error_class_codec, codec_error_video_device_error); - } + int mediaCodecDecoder::setCSD(const Stream_meta *meta) { + if (meta->codec == AF_CODEC_ID_HEVC) { - if (ret == 0) { - if (mDecoder->start() == MC_ERROR) { - AF_LOGE("mediacodec start failed."); - return gen_framework_errno(error_class_codec, codec_error_video_device_error); + if (meta->extradata == nullptr || meta->extradata_size == 0) { + return -1; } - mbInit = true; - mFlushState = 1; - } + uint8_t *vps_data = nullptr; + uint8_t *sps_data = nullptr; + uint8_t *pps_data = nullptr; + int vps_data_size = 0; + int sps_data_size = 0; + int pps_data_size = 0; - return ret; + int ret = parse_h265_extraData(CodecID2AVCodecID(AF_CODEC_ID_HEVC),meta->extradata, meta->extradata_size, + &vps_data,&vps_data_size, + &sps_data,&sps_data_size, + &pps_data,&pps_data_size, + &naluLengthSize); + if (ret >= 0) { + + std::list csdList{}; + CodecSpecificData csd0{}; + + const int data_size = + vps_data_size + sps_data_size + pps_data_size; + char data[data_size]; + + memcpy(data, vps_data, vps_data_size); + memcpy(data + vps_data_size, sps_data, sps_data_size); + memcpy(data + vps_data_size + sps_data_size, pps_data, pps_data_size); + + csd0.setScd("csd-0", data, data_size); + csdList.push_back(csd0); + mDecoder->setCodecSpecificData(csdList); + + csdList.clear(); + } + + return ret; + } else if (meta->codec == AF_CODEC_ID_H264) { + + if (meta->extradata == nullptr || meta->extradata_size == 0) { + return -1; + } + + uint8_t *sps_data = nullptr; + uint8_t *pps_data = nullptr; + int sps_data_size = 0; + int pps_data_size = 0; + + int ret = parse_h264_extraData(CodecID2AVCodecID(AF_CODEC_ID_H264),meta->extradata, meta->extradata_size, + &sps_data,&sps_data_size, + &pps_data,&pps_data_size, + &naluLengthSize); + if (ret >= 0) { + + std::list csdList{}; + CodecSpecificData csd0{}; + csd0.setScd("csd-0", sps_data, sps_data_size); + csdList.push_back(csd0); + CodecSpecificData csd1{}; + csd1.setScd("csd-1", pps_data, pps_data_size); + csdList.push_back(csd1); + mDecoder->setCodecSpecificData(csdList); + + csdList.clear(); + } + + return ret; + } else if (meta->codec == AF_CODEC_ID_AAC) { + if (meta->extradata == nullptr || meta->extradata_size == 0) { + isADTS = true; + //ADTS, has no extra data . MediaCodec MUST set csd when decode aac + + int samplingFreq[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000 + }; + + // Search the Sampling Frequencies + int sampleIndex = -1; + for (int i = 0; i < 12; ++i) { + if (samplingFreq[i] == mMeta.samplerate) { + sampleIndex = i; + break; + } + } + if (sampleIndex < 0) { + return -1; + } + + const size_t kCsdLength = 2; + char csd[kCsdLength]; + csd[0] = (meta->profile + 1) << 3 | sampleIndex >> 1; + csd[1] = (sampleIndex & 0x01) << 7 | meta->channels << 3; + + std::list csdList{}; + CodecSpecificData csd0{}; + csd0.setScd("csd-0", csd, kCsdLength); + csdList.push_back(csd0); + mDecoder->setCodecSpecificData(csdList); + + csdList.clear(); + } else { + isADTS = false; + std::list csdList{}; + CodecSpecificData csd0{}; + csd0.setScd("csd-0", meta->extradata, meta->extradata_size); + csdList.push_back(csd0); + mDecoder->setCodecSpecificData(csdList); + + csdList.clear(); + } + return 0; + } else { + return -1; + } } - void mediaCodecDecoder::flush_decoder() - { + void mediaCodecDecoder::flush_decoder() { lock_guard func_entry_lock(mFuncEntryMutex); mOutputFrameCount = 0; @@ -138,8 +267,7 @@ namespace Cicada { mInputFrameCount = 0; } - void mediaCodecDecoder::close_decoder() - { + void mediaCodecDecoder::close_decoder() { lock_guard func_entry_lock(mFuncEntryMutex); // stop decoder. @@ -147,17 +275,37 @@ namespace Cicada { if (mbInit) { mFlushState = 0; mDecoder->stop(); - mDecoder->unInit(); + releaseDecoder(); mbInit = false; } mInputFrameCount = 0; } + void mediaCodecDecoder::releaseDecoder() { + if (mDecoder != nullptr) { + mDecoder->release(); + } + } + + int mediaCodecDecoder::enqueue_decoder(unique_ptr &pPacket) { + + if (!mbInit && mDrmHandler != nullptr) { + int ret = initDrmHandler(); + if (ret == -EAGAIN) { + return -EAGAIN; + } else if (ret < 0) { + return ret; + } else if (ret == 0) { + ret = configDecoder(); + + if (ret < 0) { + return ret; + } + } + } - int mediaCodecDecoder::enqueue_decoder(unique_ptr &pPacket) - { - int index = mDecoder->dequeue_in(1000); + int index = mDecoder->dequeueInputBufferIndex(1000); if (index == MC_ERROR) { AF_LOGE("dequeue_in error."); @@ -185,13 +333,39 @@ namespace Cicada { mDiscardPTSSet.insert(pts); } } else { - AF_LOGD("queue eos\n"); + AF_LOGD("queue eos codecType = %d\n", codecType); } - ret = mDecoder->queue_in(index, data, static_cast(size), pts, false); + if (mDrmHandler != nullptr) { + IAFPacket::EncryptionInfo encryptionInfo{}; + if (pPacket != nullptr) { + pPacket->getEncryptionInfo(&encryptionInfo); + } + + uint8_t *new_data = nullptr; + int new_size = 0; + mDrmHandler->convertData(naluLengthSize, &new_data, &new_size, data, size); + + if (new_data != nullptr) { + data = new_data; + size = new_size; + } + + ret = mDecoder->queueSecureInputBuffer(index, data, static_cast(size), + &encryptionInfo, pts, + false); + + if (new_data != nullptr) { + free(new_data); + } + + } else { + ret = mDecoder->queueInputBuffer(index, data, static_cast(size), pts, + false); + } if (ret < 0) { - AF_LOGE(" mDecoder->queue_in error \n"); + AF_LOGE(" mDecoder->queue_in error codecType = %d\n", codecType); } mInputFrameCount++; @@ -202,7 +376,7 @@ namespace Cicada { if (pPacket != nullptr) { AF_LOGI("send Frame mFlushState = 2. pts %" - PRId64, pPacket->getInfo().pts); + PRId64, pPacket->getInfo().pts); } mFlushState = 2; @@ -218,11 +392,10 @@ namespace Cicada { return ret; } - int mediaCodecDecoder::dequeue_decoder(unique_ptr &pFrame) - { + int mediaCodecDecoder::dequeue_decoder(unique_ptr &pFrame) { int ret; int index; - index = mDecoder->dequeue_out(1000); + index = mDecoder->dequeueOutputBufferIndex(1000); if (index == MC_ERROR) { AF_LOGE("dequeue_out occur error. flush state %d", mFlushState); @@ -231,49 +404,151 @@ namespace Cicada { return -EAGAIN; } else if (index == MC_INFO_OUTPUT_FORMAT_CHANGED) { mc_out out{}; - mDecoder->get_out(index, &out, false); - mVideoHeight = out.conf.video.height; + mDecoder->getOutput(index, &out, false); - if (out.conf.video.crop_bottom != MC_ERROR && out.conf.video.crop_top != MC_ERROR) { - mVideoHeight = out.conf.video.crop_bottom + 1 - out.conf.video.crop_top; - } + if (codecType == CODEC_VIDEO) { + height = out.conf.video.height; - mVideoWidth = out.conf.video.width; + if (out.conf.video.crop_bottom != MC_ERROR && out.conf.video.crop_top != MC_ERROR) { + height = out.conf.video.crop_bottom + 1 - out.conf.video.crop_top; + } + + width = out.conf.video.width; - if (out.conf.video.crop_right != MC_ERROR && out.conf.video.crop_left != MC_ERROR) { - mVideoWidth = out.conf.video.crop_right + 1 - out.conf.video.crop_left; + if (out.conf.video.crop_right != MC_ERROR && out.conf.video.crop_left != MC_ERROR) { + width = out.conf.video.crop_right + 1 - out.conf.video.crop_left; + } + } else if (codecType == CODEC_AUDIO) { + channel_count = out.conf.audio.channel_count; + sample_rate = out.conf.audio.sample_rate; + format = out.conf.audio.format; } return -EAGAIN; } else if (index >= 0) { mc_out out{}; - ret = mDecoder->get_out(index, &out, false); + ret = mDecoder->getOutput(index, &out, codecType != CODEC_VIDEO); auto item = mDiscardPTSSet.find(out.buf.pts); if (item != mDiscardPTSSet.end()) { - mDecoder->release_out(index, false); + mDecoder->releaseOutputBuffer(index, false); mDiscardPTSSet.erase(item); return -EAGAIN; } - pFrame = unique_ptr(new AFMediaCodecFrame(IAFFrame::FrameTypeVideo, index, - [this](int index, bool render) { - mDecoder->release_out(index, render); - })); + if (out.b_eos) { + return STATUS_EOS; + } + // AF_LOGD("mediacodec out pts %" PRId64, out.buf.pts); - pFrame->getInfo().video.width = mVideoWidth; - pFrame->getInfo().video.height = mVideoHeight; + if (codecType == CODEC_VIDEO) { + pFrame = unique_ptr( + new AFMediaCodecFrame(IAFFrame::FrameTypeVideo, index, + [this](int index, bool render) { + mDecoder->releaseOutputBuffer(index, render); + })); + pFrame->getInfo().video.width = width; + pFrame->getInfo().video.height = height; + } else if (codecType == CODEC_AUDIO) { + + assert(out.buf.p_ptr != nullptr); + + if (out.buf.p_ptr == nullptr) { + return -EAGAIN; + } - pFrame->getInfo().pts = out.buf.pts != -1 ? out.buf.pts : INT64_MIN; + AFSampleFormat afFormat = AFSampleFormat::AF_SAMPLE_FMT_NONE; + if (format < 0 || format == 2) { + afFormat = AF_SAMPLE_FMT_S16; + } else if (format == 3) { + afFormat = AF_SAMPLE_FMT_U8; + } else if (format == 4) { + afFormat = AF_SAMPLE_FMT_S32; + } - if (out.b_eos) { - return STATUS_EOS; + assert(afFormat != AFSampleFormat::AF_SAMPLE_FMT_NONE); + + IAFFrame::AFFrameInfo frameInfo{}; + frameInfo.audio.format = afFormat; + frameInfo.audio.sample_rate = sample_rate; + frameInfo.audio.channels = channel_count; + + uint8_t *data[1] = {nullptr}; + data[0] = const_cast(out.buf.p_ptr); + int lineSize[1] = {0}; + lineSize[0] = out.buf.size; + + pFrame = unique_ptr( + new AVAFFrame(frameInfo, (const uint8_t **) data, (const int *) lineSize, 1, + IAFFrame::FrameTypeAudio)); + mDecoder->releaseOutputBuffer(index, false); + + pFrame->getInfo().audio.sample_rate = sample_rate; + pFrame->getInfo().audio.channels = channel_count; + pFrame->getInfo().audio.format = afFormat; } + pFrame->getInfo().pts = out.buf.pts != -1 ? out.buf.pts : INT64_MIN; + + // TODO: get the timePosition form input packet + pFrame->getInfo().timePosition = INT64_MIN; return 0; } else { AF_LOGE("unknown error %d\n", index); return index; } } + + int mediaCodecDecoder::configDecoder() { + + int ret = -1; + if (codecType == CODEC_VIDEO) { + ret = mDecoder->configureVideo(mMime, mMeta.width, mMeta.height, 0, + static_cast(mVideoOutObser)); + } else if (codecType == CODEC_AUDIO) { + ret = mDecoder->configureAudio(mMime, mMeta.samplerate, mMeta.channels,isADTS); + } + + if (ret >= 0) { + ret = 0; + } else { + AF_LOGE("failed to config mDecoder rv %d", ret); + releaseDecoder(); + ret = gen_framework_errno(error_class_codec, codec_error_video_device_error); + } + + if (ret == 0) { + if (mDecoder->start() == MC_ERROR) { + AF_LOGE("mediacodec start failed."); + return gen_framework_errno(error_class_codec, codec_error_video_device_error); + } + + mbInit = true; + mFlushState = 1; + } + + return ret; + } + + int mediaCodecDecoder::initDrmHandler() { + + mDrmHandler->open(); + + int state = mDrmHandler->getState(); + if (state == SESSION_STATE_OPENED) { + bool insecure = mDrmHandler->isForceInsecureDecoder(); + mDecoder->setForceInsecureDecoder(insecure); + + char *sessionId = nullptr; + int sessionSize = mDrmHandler->getSessionId(&sessionId); + mDecoder->setDrmInfo("edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", sessionId, + sessionSize); + return 0; + } else if (state == SESSION_STATE_IDLE) { + return -EAGAIN; + } else if (state == SESSION_STATE_ERROR) { + return mDrmHandler->getErrorCode(); + } + return -EAGAIN; + } } diff --git a/framework/codec/Android/mediaCodecDecoder.h b/framework/codec/Android/mediaCodecDecoder.h index 6a6484c0e..eef7f6b11 100644 --- a/framework/codec/Android/mediaCodecDecoder.h +++ b/framework/codec/Android/mediaCodecDecoder.h @@ -10,12 +10,17 @@ #include #include #include +#include + #include -#include "mediaCodec.h" -#include "mediacodec_jni.h" +#include #include "codec/ActiveDecoder.h" #include "../codecPrototype.h" -#include +#include "jni/MediaCodec_Decoder.h" + + +#define CODEC_VIDEO (0) +#define CODEC_AUDIO (1) namespace Cicada{ class mediaCodecDecoder : public ActiveDecoder, private codecPrototype { @@ -26,7 +31,7 @@ namespace Cicada{ private: - int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags) override; + int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags, const DrmInfo *drmInfo) override; void close_decoder() override; @@ -42,8 +47,15 @@ namespace Cicada{ }; private: - static bool checkSupport(AFCodecID codec, uint64_t flags, int maxSize); + static bool checkSupport(const Stream_meta &meta, uint64_t flags, int maxSize); + + int setCSD(const Stream_meta *meta); + + int initDrmHandler(); + + void releaseDecoder(); + int configDecoder(); private: explicit mediaCodecDecoder(int dummy) @@ -56,18 +68,36 @@ namespace Cicada{ return new mediaCodecDecoder(); }; - bool is_supported(AFCodecID codec, uint64_t flags, int maxSize) override + bool is_supported(const Stream_meta &meta, uint64_t flags, int maxSize) override { if (flags & DECFLAG_HW) - return checkSupport(codec, flags, maxSize); + return checkSupport(meta, flags, maxSize); return false; }; + + bool is_drmSupport(const DrmInfo *drmInfo) override { + if(drmInfo == nullptr){ + return false; + } + + bool drmSupport = drmInfo->format == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" + && DrmHandlerPrototype::isSupport(drmInfo); + return drmSupport; + } + static mediaCodecDecoder se; private: - int mVideoWidth{0}; - int mVideoHeight{0}; - mediaCodec *mDecoder{nullptr}; + int width{0}; + int height{0}; + int channel_count{0}; + int sample_rate{0}; + int format{0}; + + int codecType = CODEC_VIDEO; + std::string mMime{}; + + MediaCodec_Decoder *mDecoder{nullptr}; std::recursive_mutex mFuncEntryMutex; bool mbInit{false}; @@ -84,6 +114,14 @@ namespace Cicada{ volatile int mFlushState{0}; std::set mDiscardPTSSet; + + Stream_meta mMeta{}; + void* mVideoOutObser = nullptr; + int naluLengthSize = 0; + bool isADTS = false; + + WideVineDrmHandler* mDrmHandler = nullptr; + }; } diff --git a/framework/codec/Android/mediacodec_jni.cpp b/framework/codec/Android/mediacodec_jni.cpp deleted file mode 100644 index 996b1cb5f..000000000 --- a/framework/codec/Android/mediacodec_jni.cpp +++ /dev/null @@ -1,934 +0,0 @@ -#define LOG_TAG "mediaCodecDecoder" - -#include -#include "mediacodec_jni.h" -#include "mediaCodec.h" -#include -#include -#include -#include - -using namespace std; -namespace Cicada { - - struct jfields { - jclass media_codec_list_class, media_codec_class, media_format_class; - jclass buffer_info_class, byte_buffer_class; - jmethodID tostring; - jmethodID init, get_codec_count, get_codec_info_at, is_encoder, get_capabilities_for_type, get_codec_infos; - jmethodID is_feature_supported, get_video_capabilities, is_size_supported; - jfieldID profile_levels_field, profile_field, level_field; - jmethodID get_supported_types, get_name; - jmethodID create_by_codec_name, configure, start, stop, flush, release; - jmethodID get_output_format, set_onFrameRendered_listener, set_output_surface; - jmethodID get_input_buffers, get_input_buffer; - jmethodID get_output_buffers, get_output_buffer; - jmethodID dequeue_input_buffer, dequeue_output_buffer, queue_input_buffer; - jmethodID release_output_buffer; - jmethodID create_video_format, create_audio_format; - jmethodID set_integer, set_bytebuffer, get_integer; - jmethodID buffer_info_ctor; - jfieldID size_field, offset_field, pts_field, flags_field; - }; - static struct jfields jfields; - - enum Types { - METHOD, STATIC_METHOD, FIELD, STATIC_FIELD - }; - - struct classname { - const char *name; - ptrdiff_t offset; - }; - -#define OFF(x) (ptrdiff_t)(&(((struct jfields *)0)->x)) - static const struct classname classes[] = { - {"android/media/MediaCodecList", OFF(media_codec_list_class)}, - {"android/media/MediaCodec", OFF(media_codec_class)}, - {"android/media/MediaFormat", OFF(media_format_class)}, - {"android/media/MediaCodec$BufferInfo", OFF(buffer_info_class)}, - {"java/nio/ByteBuffer", OFF(byte_buffer_class)}, - {nullptr, 0}, - }; - - struct member { - const char *name; - const char *sig; - const char *classpath; - ptrdiff_t offset; - int type; - bool critical; - }; - - static const struct member members[] = { - {"toString", "()Ljava/lang/String;", "java/lang/Object", OFF(tostring), METHOD, true}, - - {"getCodecCount", "()I", "android/media/MediaCodecList", OFF(get_codec_count), STATIC_METHOD, true}, - {"", "(I)V", "android/media/MediaCodecList", OFF(init), METHOD, false}, - {"getCodecInfoAt", "(I)Landroid/media/MediaCodecInfo;", "android/media/MediaCodecList", OFF(get_codec_info_at), STATIC_METHOD, true}, - {"getCodecInfos", "()[Landroid/media/MediaCodecInfo;", "android/media/MediaCodecList", OFF(get_codec_infos), METHOD, false}, - {"isEncoder", "()Z", "android/media/MediaCodecInfo", OFF(is_encoder), METHOD, true}, - {"getSupportedTypes", "()[Ljava/lang/String;", "android/media/MediaCodecInfo", OFF(get_supported_types), METHOD, true}, - {"getName", "()Ljava/lang/String;", "android/media/MediaCodecInfo", OFF(get_name), METHOD, true}, - {"getCapabilitiesForType", "(Ljava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;", "android/media/MediaCodecInfo", OFF(get_capabilities_for_type), METHOD, true}, - {"isFeatureSupported", "(Ljava/lang/String;)Z", "android/media/MediaCodecInfo$CodecCapabilities", OFF(is_feature_supported), METHOD, false}, - {"getVideoCapabilities", "()Landroid/media/MediaCodecInfo$VideoCapabilities;", "android/media/MediaCodecInfo$CodecCapabilities", OFF(get_video_capabilities), METHOD, false}, - {"isSizeSupported", "(II)Z", "android/media/MediaCodecInfo$VideoCapabilities", OFF(is_size_supported), METHOD, false}, - {"profileLevels", "[Landroid/media/MediaCodecInfo$CodecProfileLevel;", "android/media/MediaCodecInfo$CodecCapabilities", OFF(profile_levels_field), FIELD, true}, - {"profile", "I", "android/media/MediaCodecInfo$CodecProfileLevel", OFF(profile_field), FIELD, true}, - {"level", "I", "android/media/MediaCodecInfo$CodecProfileLevel", OFF(level_field), FIELD, true}, - - {"createByCodecName", "(Ljava/lang/String;)Landroid/media/MediaCodec;", "android/media/MediaCodec", OFF(create_by_codec_name), STATIC_METHOD, true}, - {"configure", "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V", "android/media/MediaCodec", OFF(configure), METHOD, true}, - {"start", "()V", "android/media/MediaCodec", OFF(start), METHOD, true}, - {"stop", "()V", "android/media/MediaCodec", OFF(stop), METHOD, true}, - {"flush", "()V", "android/media/MediaCodec", OFF(flush), METHOD, true}, - {"release", "()V", "android/media/MediaCodec", OFF(release), METHOD, true}, - {"setOutputSurface", "(Landroid/view/Surface;)V", "android/media/MediaCodec", OFF(set_output_surface), METHOD, false}, - {"setOnFrameRenderedListener", "(Landroid/media/MediaCodec$OnFrameRenderedListener;Landroid/os/Handler;)V", "android/media/MediaCodec", OFF(set_onFrameRendered_listener), METHOD, false}, - {"getOutputFormat", "()Landroid/media/MediaFormat;", "android/media/MediaCodec", OFF(get_output_format), METHOD, true}, - {"getInputBuffers", "()[Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_input_buffers), METHOD, false}, - {"getInputBuffer", "(I)Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_input_buffer), METHOD, false}, - {"getOutputBuffers", "()[Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_output_buffers), METHOD, false}, - {"getOutputBuffer", "(I)Ljava/nio/ByteBuffer;", "android/media/MediaCodec", OFF(get_output_buffer), METHOD, false}, - {"dequeueInputBuffer", "(J)I", "android/media/MediaCodec", OFF(dequeue_input_buffer), METHOD, true}, - {"dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", "android/media/MediaCodec", OFF(dequeue_output_buffer), METHOD, true}, - {"queueInputBuffer", "(IIIJI)V", "android/media/MediaCodec", OFF(queue_input_buffer), METHOD, true}, - {"releaseOutputBuffer", "(IZ)V", "android/media/MediaCodec", OFF(release_output_buffer), METHOD, true}, - - {"createVideoFormat", "(Ljava/lang/String;II)Landroid/media/MediaFormat;", "android/media/MediaFormat", OFF(create_video_format), STATIC_METHOD, true}, - {"createAudioFormat", "(Ljava/lang/String;II)Landroid/media/MediaFormat;", "android/media/MediaFormat", OFF(create_audio_format), STATIC_METHOD, true}, - {"setInteger", "(Ljava/lang/String;I)V", "android/media/MediaFormat", OFF(set_integer), METHOD, true}, - {"getInteger", "(Ljava/lang/String;)I", "android/media/MediaFormat", OFF(get_integer), METHOD, true}, - {"setByteBuffer", "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V", "android/media/MediaFormat", OFF(set_bytebuffer), METHOD, true}, - - {"", "()V", "android/media/MediaCodec$BufferInfo", OFF(buffer_info_ctor), METHOD, true}, - {"size", "I", "android/media/MediaCodec$BufferInfo", OFF(size_field), FIELD, true}, - {"offset", "I", "android/media/MediaCodec$BufferInfo", OFF(offset_field), FIELD, true}, - {"presentationTimeUs", "J", "android/media/MediaCodec$BufferInfo", OFF(pts_field), FIELD, true}, - {"flags", "I", "android/media/MediaCodec$BufferInfo", OFF(flags_field), FIELD, true}, - {nullptr, nullptr, nullptr, 0, 0, false}, - }; - - static inline jstring jni_new_string(JNIEnv *env, const char *psz_string) - { - return !JniException::clearException(env) ? env->NewStringUTF(psz_string) : nullptr; - } - -#define JNI_NEW_STRING(env, psz_string) jni_new_string(env, psz_string) - - static inline int get_integer(JNIEnv *env, jobject obj, const char *psz_name) - { - AndroidJniHandle jname(JNI_NEW_STRING(env, psz_name)); - - if (jname) { - int i_ret = env->CallIntMethod(obj, jfields.get_integer, (jstring) jname); - - /* getInteger can throw nullptrPointerException (when fetching the - * "channel-mask" property for example) */ - if (JniException::clearException(env)) { - return MC_ERROR; - } - - return i_ret; - } else { - return MC_ERROR; - } - } - -#define GET_INTEGER(env, obj, name) get_integer(env, obj, name) - - static inline void set_integer(JNIEnv *env, jobject jobj, const char *psz_name, - int i_value) - { - AndroidJniHandle jname(JNI_NEW_STRING(env, psz_name)); - - if (jname) { - env->CallVoidMethod(jobj, jfields.set_integer, (jstring) jname, i_value); - } - } - -#define SET_INTEGER(env, obj, name, value) set_integer(env, obj, name, value) - - /* Initialize all jni fields. - * Done only one time during the first initialisation */ - static bool InitJNIFields(JNIEnv *env) - { - static int i_init_state = -1; - bool ret; - AndroidJniHandle last_class; - - if (i_init_state != -1) { - goto end; - } - - i_init_state = 0; - - for (int i = 0; classes[i].name; i++) { - AndroidJniHandle clazz(env->FindClass(classes[i].name)); - - if (JniException::clearException(env)) { - AF_LOGE("Unable to find class %s", classes[i].name); - goto end; - } - - *(jclass *) ((uint8_t *) &jfields + classes[i].offset) = - (jclass) env->NewGlobalRef(clazz); - } - - for (int i = 0; members[i].name; i++) { - if (i == 0 || strcmp(members[i].classpath, members[i - 1].classpath)) { - last_class = env->FindClass(members[i].classpath); - } - - if (JniException::clearException(env)) { - AF_LOGE("Unable to find class %s", members[i].classpath); - - if (members[i].critical) { - goto end; - } else { - continue; - } - } - - switch (members[i].type) { - case METHOD: - *(jmethodID *) ((uint8_t *) &jfields + members[i].offset) = - env->GetMethodID(last_class, members[i].name, members[i].sig); - break; - - case STATIC_METHOD: - *(jmethodID *) ((uint8_t *) &jfields + members[i].offset) = - env->GetStaticMethodID(last_class, members[i].name, members[i].sig); - break; - - case FIELD: - *(jfieldID *) ((uint8_t *) &jfields + members[i].offset) = - env->GetFieldID(last_class, members[i].name, members[i].sig); - break; - - case STATIC_FIELD: - *(jfieldID *) ((uint8_t *) &jfields + members[i].offset) = - env->GetStaticFieldID(last_class, members[i].name, members[i].sig); - break; - - default: - break; - } - - if (JniException::clearException(env)) { - AF_LOGE("Unable to find the member %s in %s", - members[i].name, members[i].classpath); - - if (members[i].critical) { - goto end; - } - } - } - - /* getInputBuffers and getOutputBuffers are deprecated if API >= 21 - * use getInputBuffer and getOutputBuffer instead. */ - if (jfields.get_input_buffer && jfields.get_output_buffer) { - jfields.get_output_buffers = nullptr; - jfields.get_input_buffers = nullptr; - } else if (!jfields.get_output_buffers && !jfields.get_input_buffers) { - AF_LOGE("Unable to find get Output/Input Buffer/Buffers"); - goto end; - } - - i_init_state = 1; -end: - ret = i_init_state == 1; - - if (!ret) { - AF_LOGE("MediaCodec jni init failed"); - } - - return ret; - } - - string mediaCodecGetName(const char *psz_mime, const mc_args &args, - size_t h264_profile, bool *p_adaptive) - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("jni attach failed."); - return string(""); - } - - if (!InitJNIFields((JNIEnv *) handle)) { - return string(""); - } - - AndroidJniHandle jmime(JNI_NEW_STRING((JNIEnv *) handle, psz_mime)); - - if (!jmime) { - return string(""); - } - - AndroidJniHandle codec_info_array; - int num_codecs; - int sdk_version = atoi(get_android_property("ro.build.version.sdk").c_str()); - AF_LOGI("device version %d", sdk_version); - - if (sdk_version >= 21) { - AndroidJniHandle codeclist(handle->NewObject(jfields.media_codec_list_class, jfields.init, - REGULAR_CODECS)); - codec_info_array = handle->CallObjectMethod(codeclist, - jfields.get_codec_infos); - num_codecs = handle->GetArrayLength(codec_info_array); - } else { - num_codecs = handle->CallStaticIntMethod(jfields.media_codec_list_class, - jfields.get_codec_count); - } - - AF_LOGI("mediacodec codec info list inited."); - - for (int i = 0; i < num_codecs; i++) { - AndroidJniHandle info; - - if (sdk_version >= 21) { - info = handle->GetObjectArrayElement(codec_info_array, i); - } else { - info = handle->CallStaticObjectMethod(jfields.media_codec_list_class, - jfields.get_codec_info_at, i); - } - - AndroidJniHandle name(handle->CallObjectMethod(info, jfields.get_name)); - - if (OMXCodec_IsBlacklisted(name.GetStringUTFChars(), name.GetStringUTFLength())) { - continue; - } - - if (handle->CallBooleanMethod(info, jfields.is_encoder)) { - continue; - } - - AndroidJniHandle codec_capabilities(handle->CallObjectMethod(info, - jfields.get_capabilities_for_type, - (jstring) jmime)); - - if (JniException::clearException(handle)) { - AF_LOGW("Exception occurred in MediaCodecInfo.getCapabilitiesForType"); - continue; - } - - AndroidJniHandle profile_levels; - int profile_levels_len = 0; - bool b_adaptive = false; - - if (codec_capabilities) { - profile_levels = (jobjectArray) handle->GetObjectField(codec_capabilities, - jfields.profile_levels_field); - - if (profile_levels) { - profile_levels_len = handle->GetArrayLength(profile_levels); - } - - if (jfields.is_feature_supported) { - AndroidJniHandle jfeature(JNI_NEW_STRING((JNIEnv *) handle, "adaptive-playback")); - b_adaptive = handle->CallBooleanMethod(codec_capabilities, - jfields.is_feature_supported, - (jstring) jfeature); - JniException::clearException(handle); - } - - if (jfields.get_video_capabilities) { - AndroidJniHandle video_capabilities(handle->CallObjectMethod(codec_capabilities, - jfields.get_video_capabilities)); - - if (jfields.is_size_supported) { - bool ret = handle->CallBooleanMethod(video_capabilities, - jfields.is_size_supported, - args.video.width, - args.video.height); - - if (!ret && args.video.width < args.video.height) { - AF_LOGW("width %d < height %d swap and tryAgain.", - args.video.width, args.video.height); - ret = handle->CallBooleanMethod(video_capabilities, - jfields.is_size_supported, - args.video.height, - args.video.width); - } - - AF_LOGI("psz_mime %s, width %d, height %d, is_size_supported ? %d", - psz_mime, args.video.width, args.video.height, ret); - - if (!ret) { - continue; - } - } - } - } - - AF_LOGI("Number of profile levels: %d", profile_levels_len); - AndroidJniHandle types(handle->CallObjectMethod(info, jfields.get_supported_types)); - int num_types = handle->GetArrayLength(types); - bool found = false; - - for (int j = 0; j < num_types && !found; j++) { - AndroidJniHandle type((jstring) handle->GetObjectArrayElement(types, j)); - - if (!strncmp(psz_mime, type.GetStringUTFChars(), type.GetStringUTFLength())) { - /* The mime type is matching for this component. We - now check if the capabilities of the codec is - matching the video format. */ - if (h264_profile) { - /* This decoder doesn't expose its profiles and is high - * profile capable */ - if (!strncmp(name.GetStringUTFChars(), "OMX.LUMEVideoDecoder", __MIN(20, name.GetStringUTFLength()))) { - found = true; - } - - for (int i = 0; i < profile_levels_len && !found; ++i) { - AndroidJniHandle profile_level(handle->GetObjectArrayElement(profile_levels, i)); - int omx_profile = handle->GetIntField(profile_level, - jfields.profile_field); - size_t codec_profile = convert_omx_to_profile_idc( - selectprofiletype(omx_profile)); - - if (codec_profile != h264_profile) { - continue; - } - - /* Some encoders set the level too high, thus we ignore it for the moment. - We could try to guess the actual profile based on the resolution. */ - found = true; - } - } else { - found = true; - } - } - } - - if (found) { - AF_LOGI("using %s", name.GetStringUTFChars()); - *p_adaptive = b_adaptive; - return string(name.GetStringUTFChars(), name.GetStringUTFLength()); - } - } - - AF_LOGI("not found mediacodec."); - return string(""); - } - - int MediaCodec_JNI::init(const char *mime, int category, jobject surface) - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - if (!InitJNIFields((JNIEnv *) handle)) { - AF_LOGE("Android MediaCodec init jni fields failed!"); - return MC_ERROR; - } - - mSurface = surface; - psz_mime = mime; - category_codec = category; - return 0; - } - - int MediaCodec_JNI::start() - { - int i_ret = MC_ERROR; - AndroidJniHandle jinput_buffers; - AndroidJniHandle joutput_buffers; - AndroidJniHandle jbuffer_info; - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return -1; - } - - handle->CallVoidMethod(codec, jfields.start); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception occurred in MediaCodec.start"); - goto error; - } - - b_started = true; - - if (jfields.get_input_buffers && jfields.get_output_buffers) { - jinput_buffers = handle->CallObjectMethod(codec, - jfields.get_input_buffers); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.getInputBuffers"); - goto error; - } - - input_buffers = (jobjectArray) handle->NewGlobalRef(jinput_buffers); - joutput_buffers = handle->CallObjectMethod(codec, - jfields.get_output_buffers); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.getOutputBuffers"); - goto error; - } - - output_buffers = (jobjectArray) handle->NewGlobalRef(joutput_buffers); - } - - jbuffer_info = handle->NewObject(jfields.buffer_info_class, - jfields.buffer_info_ctor); - buffer_info = handle->NewGlobalRef(jbuffer_info); - i_ret = 0; - AF_LOGI("MediaCodec via JNI opened"); -error: - - if (i_ret != 0) { - stop(); - } - - return i_ret; - } - - int MediaCodec_JNI::stop() - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - if (buffer_info) { - handle->DeleteGlobalRef(buffer_info); - buffer_info = nullptr; - } - - if (input_buffers) { - handle->DeleteGlobalRef(input_buffers); - input_buffers = nullptr; - } - - if (output_buffers) { - handle->DeleteGlobalRef(output_buffers); - output_buffers = nullptr; - } - - if (codec) { - if (b_started) { - handle->CallVoidMethod(codec, jfields.stop); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.stop"); - } - - b_started = false; - } - } - - return 0; - } - - int MediaCodec_JNI::flush() - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - handle->CallVoidMethod(codec, jfields.flush); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception occurred in MediaCodec.flush"); - return MC_ERROR; - } - - return 0; - } - - int MediaCodec_JNI::dequeue_in(int64_t timeout) - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - int index; - index = handle->CallIntMethod(codec, - jfields.dequeue_input_buffer, timeout); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception occurred in MediaCodec.dequeueInputBuffer"); - return MC_ERROR; - } - - if (index >= 0) { - return index; - } else { - return MC_INFO_TRYAGAIN; - } - } - - int MediaCodec_JNI::queue_in(int index, const void *p_buf, size_t size, int64_t pts, bool config) - { - if (index < 0) { - return MC_ERROR; - } - - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - AndroidJniHandle j_mc_buf; - - if (jfields.get_input_buffers) { - j_mc_buf = handle->GetObjectArrayElement(input_buffers, index); - } else { - j_mc_buf = handle->CallObjectMethod(codec, - jfields.get_input_buffer, index); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.getInputBuffer"); - return MC_ERROR; - } - } - - jsize j_mc_size = handle->GetDirectBufferCapacity(j_mc_buf); - auto *p_mc_buf = (uint8_t *) handle->GetDirectBufferAddress(j_mc_buf); - - if (j_mc_size < 0) { - AF_LOGE("Java buffer has invalid size"); - return MC_ERROR; - } - - if ((size_t) j_mc_size > size) { - j_mc_size = size; - } - - if (p_buf != nullptr) { - memcpy(p_mc_buf, p_buf, j_mc_size); - } - - jint jflags = 0; - - if (config) { - jflags |= BUFFER_FLAG_CODEC_CONFIG; - } - - if (p_buf == nullptr) { - jflags |= BUFFER_FLAG_END_OF_STREAM; - } - - handle->CallVoidMethod(codec, jfields.queue_input_buffer, - index, 0, j_mc_size, pts, jflags); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.queueInputBuffer"); - return MC_ERROR; - } - - return 0; - } - - int MediaCodec_JNI::dequeue_out(int64_t timeout) - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - int index = handle->CallIntMethod(codec, - jfields.dequeue_output_buffer, - buffer_info, timeout); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.dequeueOutputBuffer"); - return MC_ERROR; - } - - if (index >= 0) { - return index; - } else if (index == INFO_OUTPUT_FORMAT_CHANGED) { - return MC_INFO_OUTPUT_FORMAT_CHANGED; - } else if (index == INFO_OUTPUT_BUFFERS_CHANGED) { - return MC_INFO_OUTPUT_BUFFERS_CHANGED; - } else { - return MC_INFO_TRYAGAIN; - } - } - - int MediaCodec_JNI::get_out(int index, mc_out *out, bool readBuffer) - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - if (index >= 0) { - out->type = MC_OUT_TYPE_BUF; - out->buf.index = index; - out->buf.pts = handle->GetLongField(buffer_info, - jfields.pts_field); - int flags = handle->GetIntField(buffer_info, - jfields.flags_field); - - if ((flags & BUFFER_FLAG_END_OF_STREAM) != 0) { - out->b_eos = true; - } else { - out->b_eos = false; - } - - AndroidJniHandle buf; - uint8_t *ptr = NULL; - int offset = 0; - - if (readBuffer) { - if (jfields.get_output_buffers) { - buf = handle->GetObjectArrayElement(output_buffers, index); - } else { - buf = handle->CallObjectMethod(codec, - jfields.get_output_buffer, - index); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.getOutputBuffer"); - return MC_ERROR; - } - } - - //jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf); - /* buf can be NULL in case of EOS */ - if (buf) { - ptr = (uint8_t *) handle->GetDirectBufferAddress(buf); - offset = handle->GetIntField(buffer_info, - jfields.offset_field); - } - - out->buf.p_ptr = ptr + offset; - out->buf.size = handle->GetIntField(buffer_info, jfields.size_field); - } else { - out->buf.p_ptr = nullptr; - out->buf.size = 0; - } - - return 1; - } else if (index == MC_INFO_OUTPUT_FORMAT_CHANGED) { - AndroidJniHandle format(handle->CallObjectMethod(codec, - jfields.get_output_format)); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.getOutputFormat"); - return MC_ERROR; - } - - AndroidJniHandle format_string(handle->CallObjectMethod(format, jfields.tostring)); - AF_LOGI("output format changed: %s", format_string.GetStringUTFChars()); - out->type = MC_OUT_TYPE_CONF; - out->b_eos = false; - - if (category_codec == CATEGORY_VIDEO) { - out->conf.video.width = GET_INTEGER((JNIEnv *) handle, format, "width"); - out->conf.video.height = GET_INTEGER((JNIEnv *) handle, format, "height"); - out->conf.video.stride = GET_INTEGER((JNIEnv *) handle, format, "stride"); - out->conf.video.slice_height = GET_INTEGER((JNIEnv *) handle, format, "slice-height"); - out->conf.video.pixel_format = GET_INTEGER((JNIEnv *) handle, format, "color-format"); - out->conf.video.crop_left = GET_INTEGER((JNIEnv *) handle, format, "crop-left"); - out->conf.video.crop_top = GET_INTEGER((JNIEnv *) handle, format, "crop-top"); - out->conf.video.crop_right = GET_INTEGER((JNIEnv *) handle, format, "crop-right"); - out->conf.video.crop_bottom = GET_INTEGER((JNIEnv *) handle, format, "crop-bottom"); - AF_LOGI("width %d height %d stride %d slice_height %d crop right %d", - out->conf.video.width, out->conf.video.height, out->conf.video.stride, - out->conf.video.slice_height, out->conf.video.crop_right); - } else { - out->conf.audio.channel_count = GET_INTEGER((JNIEnv *) handle, format, "channel-count"); - out->conf.audio.channel_mask = GET_INTEGER((JNIEnv *) handle, format, "channel-mask"); - out->conf.audio.sample_rate = GET_INTEGER((JNIEnv *) handle, format, "sample-rate"); - } - - return 1; - } else if (index == MC_INFO_OUTPUT_BUFFERS_CHANGED) { - AF_LOGI("output buffers changed"); - - if (!jfields.get_output_buffers) { - return 0; - } - - handle->DeleteGlobalRef(output_buffers); - output_buffers = nullptr; - AndroidJniHandle joutput_buffers(handle->CallObjectMethod(codec, - jfields.get_output_buffers)); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.getOutputBuffer"); - return MC_ERROR; - } - - output_buffers = (jobjectArray) handle->NewGlobalRef(joutput_buffers); - } - - return 0; - } - - int MediaCodec_JNI::configure(size_t i_h264_profile, const mc_args &args) - { - bool b_adaptive; - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - std::string psz_name = mediaCodecGetName(psz_mime, args, i_h264_profile, &b_adaptive); - AF_LOGI("android decode name %s, b_adaptive %d", psz_name.c_str(), b_adaptive); - - if (psz_name.empty()) { - AF_LOGE("can not get mediacodec name."); - return -11; - } - - if (!handle) { - AF_LOGE("env is nullptr."); - return -12; - } - - AndroidJniHandle jmime(JNI_NEW_STRING((JNIEnv *) handle, psz_mime)); - AndroidJniHandle jcodec_name(JNI_NEW_STRING((JNIEnv *) handle, psz_name.c_str())); - - if (!jmime || !jcodec_name) { - AF_LOGE("find jcodec name failed."); - return -13; - } - - /* This method doesn't handle errors nicely, it crashes if the codec isn't - * found. (The same goes for createDecoderByType.) This is fixed in latest - * AOSP and in 4.2, but not in 4.1 devices. */ - AndroidJniHandle jcodec(handle->CallStaticObjectMethod(jfields.media_codec_class, - jfields.create_by_codec_name, - (jstring) jcodec_name)); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception occurred in MediaCodec.createByCodecName %s", psz_name.c_str()); - return -14; - } - - codec = handle->NewGlobalRef(jcodec); - AndroidJniHandle jformat; - - if (category_codec == CATEGORY_VIDEO) { -// assert(args->video.i_angle == 0 || api->b_support_rotation); - jformat = handle->CallStaticObjectMethod( - jfields.media_format_class, - jfields.create_video_format, - (jstring) jmime, - args.video.width, - args.video.height); - - if (args.video.angle != 0) { - SET_INTEGER((JNIEnv *) handle, jformat, "rotation-degrees", args.video.angle); - } - } else { - jformat = handle->CallStaticObjectMethod(jfields.media_format_class, - jfields.create_audio_format, - (jstring) jmime, - args.audio.sample_rate, - args.audio.channel_count); - } - - /* No limits for input size */ - SET_INTEGER((JNIEnv *) handle, jformat, "max-input-size", 0); - handle->CallVoidMethod(codec, jfields.configure, - (jobject) jformat, mSurface, NULL, 0); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception occurred in MediaCodec.configure"); - return -15; - } - - return 0; - } - - - void MediaCodec_JNI::unInit() - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return; - } - - if (codec) { - handle->CallVoidMethod(codec, jfields.release); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.release"); - } - - handle->DeleteGlobalRef(codec); - codec = NULL; - } - } - - int MediaCodec_JNI::setOutputSurface(jobject surface) - { - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - handle->CallVoidMethod(codec, jfields.set_output_surface, surface); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.setOutputSurface"); - return MC_ERROR; - } - - return 0; - } - - int MediaCodec_JNI::release_out(int index, bool render) - { - if (index < 0) { - return MC_ERROR; - } - - JniEnv jniEnv; - JNIEnv *handle = jniEnv.getEnv(); - - if (!handle) { - AF_LOGE("env is nullptr."); - return MC_ERROR; - } - - handle->CallVoidMethod(codec, jfields.release_output_buffer, - index, render); - - if (JniException::clearException(handle)) { - AF_LOGE("Exception in MediaCodec.releaseOutputBuffer"); - return MC_ERROR; - } - - return 0; - } -} diff --git a/framework/codec/Android/mediacodec_jni.h b/framework/codec/Android/mediacodec_jni.h deleted file mode 100644 index a54beff84..000000000 --- a/framework/codec/Android/mediacodec_jni.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef MEDIACODEC_JNI_HH -#define MEDIACODEC_JNI_HH - -#include "mediaCodec.h" -#include - -#define BUFFER_FLAG_CODEC_CONFIG 2 -#define BUFFER_FLAG_END_OF_STREAM 4 -#define INFO_OUTPUT_BUFFERS_CHANGED -3 -#define INFO_OUTPUT_FORMAT_CHANGED -2 -#define INFO_TRY_AGAIN_LATER -1 - -#define ALL_CODECS 1 -#define REGULAR_CODECS 0 - - -namespace Cicada -{ - -std::string mediaCodecGetName(const char *psz_mime, const mc_args &args, - size_t h264_profile, bool *p_adaptive); - -class MediaCodec_JNI : public mediaCodec -{ -public: - - int init(const char *mime, int category, jobject surface) override; - - int setOutputSurface(jobject surface) override; - - int flush() override; - - int configure(size_t i_h264_profile, const mc_args &args) override; - - int queue_in(int index, const void *p_buf, size_t size, int64_t pts, bool config) override; - - int dequeue_in(int64_t timeout) override; - - int dequeue_out(int64_t timeout) override; - - int start() override; - - int stop() override; - - int get_out(int index, mc_out *out, bool readBuffer = true) override; - - int release_out(int index, bool render) override; - - void unInit() override; - -private: - const char *psz_mime; - jobject codec{nullptr}; - int category_codec{-1}; - bool b_started; - jobject mSurface{nullptr}; - jobject buffer_info{nullptr}; - jobjectArray input_buffers{nullptr}, output_buffers{nullptr}; -}; -} -#endif // MEDIACODEC_JNI_HH diff --git a/framework/codec/Apple/AppleVideoToolBox.cpp b/framework/codec/Apple/AppleVideoToolBox.cpp index 1a64b2c47..5b7296839 100644 --- a/framework/codec/Apple/AppleVideoToolBox.cpp +++ b/framework/codec/Apple/AppleVideoToolBox.cpp @@ -6,6 +6,8 @@ #include "utils/errors/framework_error.h" #include "utils/timer.h" #include +#include +#include extern "C" { #include }; @@ -28,9 +30,11 @@ namespace Cicada { mVTOutFmt = AF_PIX_FMT_YUV420P; #endif #elif TARGET_OS_OSX - // mVTOutFmt = AF_PIX_FMT_YUV420P; + mVTOutFmt = AF_PIX_FMT_YUV420P; + outPutFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; #endif mName = "VideoToolBox"; + mFlags = DECFLAG_PASSTHROUGH_INFO | DECFLAG_HW; } AFVTBDecoder::~AFVTBDecoder() @@ -40,6 +44,7 @@ namespace Cicada { } close(); + CGColorSpaceRelease(m_ColorSpace); #if TARGET_OS_IPHONE RemoveIOSNotificationObserver(this); #endif @@ -51,7 +56,7 @@ namespace Cicada { return false; } -#ifdef NDEBUG +#ifndef NDEBUG if (codec == AF_CODEC_ID_HEVC) { #if TARGET_OS_IPHONE @@ -98,11 +103,11 @@ namespace Cicada { #endif } -// if (!VTIsHardwareDecodeSupported(mVideoCodecType)) -// return ret; -// mGotFirstFrame = false; + // if (!VTIsHardwareDecodeSupported(mVideoCodecType)) + // return ret; + // mGotFirstFrame = false; - if (meta->extradata && meta->extradata_size > 0 && mActive) { + if (meta->extradata && meta->extradata_size > 0 && mActive) { ret = createDecompressionSession(meta->extradata, meta->extradata_size, meta->width, meta->height); if (ret < 0) { @@ -114,10 +119,10 @@ namespace Cicada { return 0; } - int AFVTBDecoder::init_decoder(const Stream_meta *meta, void *voutObsr, uint64_t flags) + int AFVTBDecoder::init_decoder(const Stream_meta *meta, void *voutObsr, uint64_t flags, const DrmInfo* drmInfo) { - if (meta->pixel_fmt == AF_PIX_FMT_YUV422P || meta->pixel_fmt == AF_PIX_FMT_YUVJ422P) { - return -ENOSPC; + if (meta->pixel_fmt == AF_PIX_FMT_YUV422P || meta->pixel_fmt == AF_PIX_FMT_YUVJ422P || meta->interlaced == InterlacedType_YES) { + return -ENOTSUP; } mPInMeta = unique_ptr(new streamMeta(meta)); @@ -126,6 +131,9 @@ namespace Cicada { ((Stream_meta *) (*(mPInMeta)))->lang = nullptr; ((Stream_meta *) (*(mPInMeta)))->description = nullptr; ((Stream_meta *) (*(mPInMeta)))->meta = nullptr; + ((Stream_meta *) (*(mPInMeta)))->keyUrl = nullptr; + ((Stream_meta *) (*(mPInMeta)))->keyFormat = nullptr; + mInputCount = 0; if (meta->codec == AF_CODEC_ID_H264 /*|| meta->codec == AF_CODEC_ID_HEVC*/) { @@ -144,8 +152,6 @@ namespace Cicada { if (ret < 0) { return ret; } - - mFlags |= DECFLAG_HW; return 0; } @@ -185,11 +191,7 @@ namespace Cicada { VTDecompressionOutputCallbackRecord outputCallbackRecord; outputCallbackRecord.decompressionOutputCallback = Cicada::AFVTBDecoder::decompressionOutputCallback; outputCallbackRecord.decompressionOutputRefCon = this; - rv = VTDecompressionSessionCreate(kCFAllocatorDefault, - mVideoFormatDesRef, - mDecoder_spec, - buf_attr, - &outputCallbackRecord, + rv = VTDecompressionSessionCreate(kCFAllocatorDefault, mVideoFormatDesRef, mDecoder_spec, buf_attr, &outputCallbackRecord, &mVTDecompressSessionRef); if (rv != 0) { @@ -239,14 +241,20 @@ namespace Cicada { CFDictionarySetValue(dict, key, value); } - static void dict_set_i32(CFMutableDictionaryRef dict, CFStringRef key, - int32_t value) + static void dict_set_i32(CFMutableDictionaryRef dict, CFStringRef key, int32_t value) { CFNumberRef number; number = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value); CFDictionarySetValue(dict, key, number); CFRelease(number); } + static void dict_set_f32(CFMutableDictionaryRef dict, CFStringRef key, float value) + { + CFNumberRef number; + number = CFNumberCreate(nullptr, kCFNumberFloat32Type, &value); + CFDictionarySetValue(dict, key, number); + CFRelease(number); + } int AFVTBDecoder::createVideoFormatDesc(const uint8_t *pData, int size, int width, int height, CFDictionaryRef &decoder_spec, CMFormatDescriptionRef &cm_fmt_desc) @@ -258,28 +266,39 @@ namespace Cicada { return -EINVAL; } - CFMutableDictionaryRef par = CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFMutableDictionaryRef atoms = CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFMutableDictionaryRef extensions = CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); + CFMutableDictionaryRef par = + CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFMutableDictionaryRef atoms = + CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFMutableDictionaryRef extensions = + CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); /* CVPixelAspectRatio dict */ - dict_set_i32(par, CFSTR ("HorizontalSpacing"), 0); - dict_set_i32(par, CFSTR ("VerticalSpacing"), 0); + Stream_meta *meta = ((Stream_meta *) (*(mPInMeta))); + + + float HSpacing = 1.0; + float VSpacing = 1.0; + + if (meta->displayHeight && meta->displayWidth) { + VSpacing = (float) meta->displayHeight / height; + HSpacing = (float) meta->displayWidth / width; + } + dict_set_f32(par, CFSTR("HorizontalSpacing"), HSpacing); + dict_set_f32(par, CFSTR("VerticalSpacing"), VSpacing); + /* SampleDescriptionExtensionAtoms dict */ switch (mVideoCodecType) { case kCMVideoCodecType_H264: - dict_set_data(atoms, CFSTR ("avcC"), (uint8_t *) pData, size); + dict_set_data(atoms, CFSTR("avcC"), (uint8_t *) pData, size); break; case kCMVideoCodecType_HEVC: - dict_set_data(atoms, CFSTR ("hvcC"), (uint8_t *) pData, size); + dict_set_data(atoms, CFSTR("hvcC"), (uint8_t *) pData, size); break; case kCMVideoCodecType_MPEG4Video: - dict_set_data(atoms, CFSTR ("esds"), (uint8_t *) pData, size); + dict_set_data(atoms, CFSTR("esds"), (uint8_t *) pData, size); break; default: @@ -287,11 +306,11 @@ namespace Cicada { } /* Extensions dict */ - dict_set_string(extensions, CFSTR ("CVImageBufferChromaLocationBottomField"), "left"); - dict_set_string(extensions, CFSTR ("CVImageBufferChromaLocationTopField"), "left"); + dict_set_string(extensions, CFSTR("CVImageBufferChromaLocationBottomField"), "left"); + dict_set_string(extensions, CFSTR("CVImageBufferChromaLocationTopField"), "left"); dict_set_boolean(extensions, CFSTR("FullRangeVideo"), FALSE); - dict_set_object(extensions, CFSTR ("CVPixelAspectRatio"), (CFTypeRef *) par); - dict_set_object(extensions, CFSTR ("SampleDescriptionExtensionAtoms"), (CFTypeRef *) atoms); + dict_set_object(extensions, CFSTR("CVPixelAspectRatio"), (CFTypeRef *) par); + dict_set_object(extensions, CFSTR("SampleDescriptionExtensionAtoms"), (CFTypeRef *) atoms); if (width <= 0 || height <= 0) { parserInfo info{0}; @@ -481,16 +500,17 @@ namespace Cicada { int AFVTBDecoder::enqueue_decoder_internal(unique_ptr &pPacket) { // AF_LOGD("mInputQueue size is %d\n", mInputQueue.size()); -// if (mOutputQueue.size() > maxOutQueueSize) { -// // AF_TRACE; -// return -EAGAIN; -// } + // if (mOutputQueue.size() > maxOutQueueSize) { + // // AF_TRACE; + // return -EAGAIN; + // } if (pPacket->getInfo().flags & AF_PKT_FLAG_KEY) { mThrowPacket = false; } if (mThrowPacket) { AF_LOGE("IOS8VT: throw frame"); + enqueueError(-1, pPacket->getInfo().pts); return 0; } @@ -509,8 +529,8 @@ namespace Cicada { CMBlockBufferRef newBuffer = nullptr; - if (CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, pPacket->getData(), pPacket->getSize(), kCFAllocatorNull, - nullptr, 0, pPacket->getSize(), 0, &newBuffer) != 0) { + if (CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, pPacket->getData(), pPacket->getSize(), kCFAllocatorNull, nullptr, 0, + pPacket->getSize(), 0, &newBuffer) != 0) { AF_LOGE("failed to create mblockbuffer"); return -EINVAL; } @@ -521,9 +541,9 @@ namespace Cicada { timeInfo.decodeTimeStamp = CMTimeMake(pPacket->getInfo().dts, 1000000); CMSampleBufferRef sampleBuffer = nullptr; - if (0 != CMSampleBufferCreate(kCFAllocatorDefault, newBuffer, TRUE, nullptr, nullptr, mVideoFormatDesRef, - 1, 1, &timeInfo, 0, nullptr, &sampleBuffer)) { - CFRelease(newBuffer); // add by yager + if (0 != CMSampleBufferCreate(kCFAllocatorDefault, newBuffer, TRUE, nullptr, nullptr, mVideoFormatDesRef, 1, 1, &timeInfo, 0, + nullptr, &sampleBuffer)) { + CFRelease(newBuffer);// add by yager AF_LOGE("failed to CMSampleBufferCreate"); return -EINVAL; } @@ -540,7 +560,7 @@ namespace Cicada { CFRelease(newBuffer); CFRelease(sampleBuffer); - if (rv == kVTInvalidSessionErr) { + if (rv == kVTInvalidSessionErr || rv == kVTVideoDecoderMalfunctionErr) { AF_LOGW("kVTInvalidSessionErr\n"); pPacket.reset(packet); close_decoder(); @@ -577,7 +597,7 @@ namespace Cicada { return; } - if (status != noErr) { + if (!packet->getDiscard() && (status != noErr || !frame)) { AF_LOGW("AFVTBDecoder decoder error %d\n", status); if (status == kVTVideoDecoderBadDataErr) { mThrowPacket = true; @@ -620,12 +640,14 @@ namespace Cicada { return; } + frame->getInfo().timePosition = packet->getInfo().timePosition; if (mVideoCodecType != kCMVideoCodecType_HEVC && keyFrame && mPocErrorCount < MAX_POC_ERROR) { flushReorderQueue(); if (mVTOutFmt == AF_PIX_FMT_YUV420P) { auto *avframe = (AVAFFrame *) (*frame); + avframe->getInfo().timePosition = packet->getInfo().timePosition; std::unique_lock uMutex(mReorderMutex); mReorderedQueue.push(unique_ptr(avframe)); } else { @@ -642,15 +664,11 @@ namespace Cicada { push_to_recovery_queue(unique_ptr(packet)); } - void AFVTBDecoder::decompressionOutputCallback(void *CM_NULLABLE decompressionOutputRefCon, - void *CM_NULLABLE sourceFrameRefCon, - OSStatus status, - VTDecodeInfoFlags infoFlags, - CM_NULLABLE CVImageBufferRef imageBuffer, - CMTime presentationTimeStamp, - CMTime presentationDuration) + void AFVTBDecoder::decompressionOutputCallback(void *CM_NULLABLE decompressionOutputRefCon, void *CM_NULLABLE sourceFrameRefCon, + OSStatus status, VTDecodeInfoFlags infoFlags, CM_NULLABLE CVImageBufferRef imageBuffer, + CMTime presentationTimeStamp, CMTime presentationDuration) { - auto *decoder = static_cast (decompressionOutputRefCon); + auto *decoder = static_cast(decompressionOutputRefCon); IAFPacket *packet = static_cast(sourceFrameRefCon); unique_ptr pbafFrame{}; @@ -664,6 +682,9 @@ namespace Cicada { // TODO: enqueue error return decoder->onDecoded(packet, nullptr, status); } + // Strip the attachments added by VT. They are likely wrong. + // CVBufferRemoveAllAttachments(imageBuffer); + pixelBufferConvertor::UpdateColorInfo(((Stream_meta *) (*(decoder->mPInMeta)))->color_info, imageBuffer); int64_t duration = presentationDuration.value * (1000000.0 / presentationDuration.timescale); int64_t pts = presentationTimeStamp.value * (1000000.0 / presentationTimeStamp.timescale); @@ -689,9 +710,8 @@ namespace Cicada { int64_t poc = (*mReorderFrameMap.begin()).first; - if (poc < 0 || mOutputPoc < 0 - || (mReorderFrameMap.size() >= IOS_OUTPUT_CACHE_FOR_B_FRAMES) - || (poc - mOutputPoc == mPocDelta)) { + if (poc < 0 || mOutputPoc < 0 || (mReorderFrameMap.size() >= IOS_OUTPUT_CACHE_FOR_B_FRAMES) || + (poc - mOutputPoc == mPocDelta)) { mOutputPoc = poc; } else { return -EAGAIN; @@ -750,6 +770,7 @@ namespace Cicada { if (mVTOutFmt == AF_PIX_FMT_YUV420P) { PBAFFrame *pbafFrame = (*(mReorderFrameMap.begin())).second.get(); auto *frame = (AVAFFrame *) (*pbafFrame); + frame->getInfo().timePosition = pbafFrame->getInfo().timePosition; mReorderedQueue.push(unique_ptr(frame)); } else { mReorderedQueue.push(move(*(mReorderFrameMap.begin())).second); @@ -793,9 +814,9 @@ namespace Cicada { AF_LOGD("ios bg decoder appWillResignActive"); mActive = false; mResignActive = true; -// if (mDecodedHandler) { -// mDecodedHandler->OnDecodedMsgHandle(CICADA_VDEC_WARNING_IOS_RESIGN_ACTIVE); -// } + // if (mDecodedHandler) { + // mDecodedHandler->OnDecodedMsgHandle(CICADA_VDEC_WARNING_IOS_RESIGN_ACTIVE); + // } } void AFVTBDecoder::AppDidBecomeActive() @@ -818,6 +839,7 @@ namespace Cicada { case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: case kCVPixelFormatType_420YpCbCr8Planar: case kCVPixelFormatType_32BGRA: + case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: outPutFormat = format; return 0; default: @@ -826,4 +848,4 @@ namespace Cicada { } } -} // namespace +}// namespace Cicada diff --git a/framework/codec/Apple/AppleVideoToolBox.h b/framework/codec/Apple/AppleVideoToolBox.h index 9d4e7e9f8..16419c7b6 100644 --- a/framework/codec/Apple/AppleVideoToolBox.h +++ b/framework/codec/Apple/AppleVideoToolBox.h @@ -30,7 +30,7 @@ namespace Cicada{ int setPixelBufferFormat(OSType format); private: - int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags) override; + int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags , const DrmInfo* drmInfo) override; void close_decoder() override; @@ -56,12 +56,18 @@ namespace Cicada{ return new AFVTBDecoder(); }; - bool is_supported(enum AFCodecID codec, uint64_t flags, int maxSize) override + bool is_supported(const Stream_meta &meta, uint64_t flags, int maxSize) override { if (!(flags & DECFLAG_HW)) return false; - return is_supported(codec); + return is_supported(meta.codec); }; + + bool is_drmSupport(const DrmInfo* drmInfo) override + { + return false; + } + static AFVTBDecoder se; private: @@ -124,7 +130,8 @@ namespace Cicada{ bool mResignActive{false}; bool mIsDummy = false; int mPocErrorCount{0}; - OSType outPutFormat{kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; + OSType outPutFormat{0}; + _Nullable CGColorSpaceRef m_ColorSpace{nullptr}; }; } diff --git a/framework/codec/CMakeLists.txt b/framework/codec/CMakeLists.txt index c2ae69b55..66f073ced 100644 --- a/framework/codec/CMakeLists.txt +++ b/framework/codec/CMakeLists.txt @@ -10,7 +10,6 @@ target_sources(videodec PRIVATE IDecoder.h codecPrototype.cpp codecPrototype.h - videoDecoderFactory.cpp decoderFactory.cpp decoderFactory.h ActiveDecoder.cpp @@ -28,12 +27,15 @@ endif () if (ANDROID AND ENABLE_MEDIA_CODEC_DECODER) target_compile_definitions(videodec PRIVATE ENABLE_MEDIA_CODEC_DECODER) target_sources(videodec PRIVATE - Android/mediaCodec.h - Android/mediaCodec.cpp - Android/mediacodec_jni.h - Android/mediacodec_jni.cpp Android/mediaCodecDecoder.h Android/mediaCodecDecoder.cpp + + Android/jni/MediaCodec_Decoder.h + Android/jni/MediaCodec_Decoder.cpp + Android/jni/OutputBufferInfo.h + Android/jni/OutputBufferInfo.cpp + Android/jni/JEncryptionInfo.h + Android/jni/JEncryptionInfo.cpp ) endif () diff --git a/framework/codec/IDecoder.h b/framework/codec/IDecoder.h index e28a90651..d18b901ab 100644 --- a/framework/codec/IDecoder.h +++ b/framework/codec/IDecoder.h @@ -9,7 +9,11 @@ #include #include #include +#include #include +#include +#include +#include "IVideoFrame.h" enum decoder_status { got_pic, @@ -38,8 +42,6 @@ typedef enum DECODER_FRAME_STATUS { #define STATUS_VT_DECODER_BACKGROUND 1 << decoder_background #define STATUS_CREATE_FAIL 1 << decoder_create_fail -#include -#include "IVideoFrame.h" namespace Cicada { @@ -62,13 +64,15 @@ namespace Cicada { clean_error(); } - virtual int open(const Stream_meta *meta, void *voutObsr, uint64_t flags) = 0; + virtual int open(const Stream_meta *meta, void *voutObsr, uint64_t flags , const DrmInfo *drmInfo ) = 0; virtual void flush() = 0; virtual void close() = 0; - virtual void preClose() = 0; + virtual void prePause() = 0; + + virtual void pause(bool pause) = 0; virtual void setEOF() { @@ -166,6 +170,17 @@ namespace Cicada { virtual int getRecoverQueueSize() = 0; + virtual uint32_t getInputPaddingSize() = 0; + + virtual bool supportReuse() + { + return true; + } + + void setRequireDrmHandlerCallback(std::function callback){ + mRequireDrmHandlerCallback = callback; + } + protected: std::string mName; int mFlags = 0; // VFLAG_HW,VFLAG_OUT @@ -176,6 +191,8 @@ namespace Cicada { bool mInBackground = false; bool bNeedKeyFrame{true}; int64_t keyPts = INT64_MIN; + + std::function mRequireDrmHandlerCallback{nullptr}; }; } diff --git a/framework/codec/avcodecDecoder.cpp b/framework/codec/avcodecDecoder.cpp index 2763b91e3..6f02458e0 100644 --- a/framework/codec/avcodecDecoder.cpp +++ b/framework/codec/avcodecDecoder.cpp @@ -96,7 +96,8 @@ namespace Cicada { mPDecoder = nullptr; } - int avcodecDecoder::init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags) + int avcodecDecoder::init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags, + const DrmInfo *drmInfo) { auto codecId = (enum AVCodecID) CodecID2AVCodecID(meta->codec); mPDecoder->codec = avcodec_find_decoder(codecId); @@ -192,6 +193,7 @@ namespace Cicada { mPDecoder->hwDeviceType_set = CICADA_HWDEVICE_TYPE_UNKNOWN; #endif avcodec_register_all(); + mFlags |= DECFLAG_PASSTHROUGH_INFO; } avcodecDecoder::~avcodecDecoder() @@ -245,7 +247,15 @@ namespace Cicada { } #endif + int64_t timePosition = INT64_MIN; + if (mPDecoder->avFrame->metadata){ + AVDictionaryEntry *t = av_dict_get(mPDecoder->avFrame->metadata,"timePosition", nullptr,AV_DICT_IGNORE_SUFFIX); + if (t){ + timePosition = atoll(t->value); + } + } pFrame = unique_ptr(new AVAFFrame(mPDecoder->avFrame)); + pFrame->getInfo().timePosition = timePosition; return ret; }; @@ -272,6 +282,17 @@ namespace Cicada { AF_LOGD("send null to decoder\n"); } + if (pkt){ + AVDictionary *dict = nullptr; + int size = 0; + av_dict_set_int(&dict,"timePosition",pPacket->getInfo().timePosition,0); + uint8_t *metadata = av_packet_pack_dictionary(dict, &size); + av_dict_free(&dict); + int addRet = av_packet_add_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, metadata, size); + assert(metadata); + assert(addRet >= 0); + } + ret = avcodec_send_packet(mPDecoder->codecCont, pkt); if (0 == ret) { @@ -286,6 +307,13 @@ namespace Cicada { return ret; } - - + bool avcodecDecoder::supportReuse() + { + if (mPDecoder->codecCont == nullptr) { + return true; + } + // TODO: check the data format whether changed (avcc adts ...) + //return mPDecoder->codecCont->extradata_size == 0; + return false; + } } diff --git a/framework/codec/avcodecDecoder.h b/framework/codec/avcodecDecoder.h index a10a75199..28de3a13a 100644 --- a/framework/codec/avcodecDecoder.h +++ b/framework/codec/avcodecDecoder.h @@ -68,12 +68,17 @@ namespace Cicada{ return new avcodecDecoder(); }; - bool is_supported(AFCodecID codec, uint64_t flags, int maxSize) override + bool is_supported(const Stream_meta& meta, uint64_t flags, int maxSize) override { if (flags & DECFLAG_SW) - return is_supported(codec); + return is_supported(meta.codec); return false; }; + + bool is_drmSupport(const DrmInfo *drmInfo) override { + return false; + } + static avcodecDecoder se; private: @@ -82,7 +87,7 @@ namespace Cicada{ int dequeue_decoder(std::unique_ptr &pFrame) override; - int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags) override; + int init_decoder(const Stream_meta *meta, void *wnd, uint64_t flags, const DrmInfo *drmInfo) override; void close_decoder() override; @@ -92,6 +97,7 @@ namespace Cicada{ { return 0; }; + virtual bool supportReuse() override; private: decoder_handle_v *mPDecoder = nullptr; diff --git a/framework/codec/codecPrototype.cpp b/framework/codec/codecPrototype.cpp index 95b71895d..a020b5734 100644 --- a/framework/codec/codecPrototype.cpp +++ b/framework/codec/codecPrototype.cpp @@ -15,7 +15,8 @@ void codecPrototype::addPrototype(codecPrototype *se) // AF_LOGD("codecQueue size is %d\n",codecQueue.size()); } -Cicada::IDecoder *codecPrototype::create(AFCodecID code, uint64_t flags, int maxSize) +Cicada::IDecoder *codecPrototype::create(const Stream_meta &meta, uint64_t flags, int maxSize, + const Cicada::DrmInfo *drmInfo) { bool bHW = static_cast(flags & DECFLAG_HW); @@ -24,22 +25,19 @@ Cicada::IDecoder *codecPrototype::create(AFCodecID code, uint64_t flags, int max decFlags &= ~DECFLAG_SW; for (int i = 0; i < _nextSlot; ++i) { - if (codecQueue[i]->is_supported(code, decFlags, maxSize)) { + if (codecQueue[i]->is_supported(meta, decFlags, maxSize) && + (drmInfo == nullptr || codecQueue[i]->is_drmSupport(drmInfo))) { return codecQueue[i]->clone(); } } } for (int i = 0; i < _nextSlot; ++i) { - if (codecQueue[i]->is_supported(code, flags, maxSize)) { + if (codecQueue[i]->is_supported(meta, flags, maxSize) && + (drmInfo == nullptr || codecQueue[i]->is_drmSupport(drmInfo))) { return codecQueue[i]->clone(); } } return nullptr; -// for (auto item : codecQueue) { -// if (item->is_supported(bHW, code)) -// return item->clone(); -// } -// return nullptr; } diff --git a/framework/codec/codecPrototype.h b/framework/codec/codecPrototype.h index e9d075c18..6fd0dcba3 100644 --- a/framework/codec/codecPrototype.h +++ b/framework/codec/codecPrototype.h @@ -6,9 +6,10 @@ #define CICADA_PLAYER_CODECPROTOTYPE_H #include #include "IDecoder.h" +#include -class codecPrototype { -// static vector codecQueue; +class CICADA_CPLUS_EXTERN codecPrototype { + // static vector codecQueue; static codecPrototype* codecQueue [10]; static int _nextSlot; public: @@ -16,11 +17,13 @@ class codecPrototype { virtual Cicada::IDecoder *clone() = 0; - virtual bool is_supported(AFCodecID code, uint64_t flags, int maxSize) = 0; + virtual bool is_supported(const Stream_meta &meta, uint64_t flags, int maxSize) = 0; + + virtual bool is_drmSupport(const Cicada::DrmInfo *drmInfo) = 0; static void addPrototype(codecPrototype *se); - static Cicada::IDecoder *create(AFCodecID code, uint64_t flags, int maxSize); + static Cicada::IDecoder *create(const Stream_meta &meta, uint64_t flags, int maxSize, const Cicada::DrmInfo *drmInfo); }; diff --git a/framework/codec/decoderFactory.cpp b/framework/codec/decoderFactory.cpp index f7f8df530..d05ea7241 100644 --- a/framework/codec/decoderFactory.cpp +++ b/framework/codec/decoderFactory.cpp @@ -2,6 +2,7 @@ // Created by moqi on 2019-08-20. // +#include #include "decoderFactory.h" #ifdef ANDROID @@ -21,18 +22,20 @@ using namespace Cicada; using namespace std; -unique_ptr decoderFactory::create(AFCodecID codec, uint64_t flags, int maxSize) +unique_ptr decoderFactory::create(const Stream_meta& meta, uint64_t flags, int maxSize, + const DrmInfo *drmInfo) { - IDecoder *decoder = codecPrototype::create(codec, flags, maxSize); + IDecoder *decoder = codecPrototype::create(meta, flags, maxSize, drmInfo); if (decoder != nullptr) { return unique_ptr(decoder); } - return createBuildIn(codec, flags); + return createBuildIn(meta.codec, flags, drmInfo); } -unique_ptr decoderFactory::createBuildIn(const AFCodecID &codec, uint64_t flags) +unique_ptr decoderFactory::createBuildIn(const AFCodecID &codec, uint64_t flags, + const DrmInfo *drmInfo) { if (flags & DECFLAG_HW) { #ifdef ANDROID diff --git a/framework/codec/decoderFactory.h b/framework/codec/decoderFactory.h index a64256b29..11966d473 100644 --- a/framework/codec/decoderFactory.h +++ b/framework/codec/decoderFactory.h @@ -6,16 +6,19 @@ #define CICADA_PLAYER_DECODERFACTORY_H #include +#include #include "IDecoder.h" class decoderFactory { public: - static std::unique_ptr create(AFCodecID codec, uint64_t flags, int maxSize); + static std::unique_ptr create(const Stream_meta & meta, uint64_t flags, int maxSize , + const Cicada::DrmInfo *drmInfo); private: - static std::unique_ptr createBuildIn(const AFCodecID &codec, uint64_t flags); + static std::unique_ptr createBuildIn(const AFCodecID &codec, uint64_t flags, + const Cicada::DrmInfo *drmInfo); }; diff --git a/framework/codec/utils_ios.mm b/framework/codec/utils_ios.mm index a113adb0e..7a2abd081 100644 --- a/framework/codec/utils_ios.mm +++ b/framework/codec/utils_ios.mm @@ -139,12 +139,11 @@ static void IOSNotificationHandler(CFNotificationCenterRef center, IOSNotificationManager *manager = (IOSNotificationManager *) observer; - if (CFStringCompare(name, (__bridge CFStringRef) UIApplicationWillResignActiveNotification, 0) == kCFCompareEqualTo) { + if (CFStringCompare(name, (__bridge CFStringRef) UIApplicationDidEnterBackgroundNotification, 0) == kCFCompareEqualTo) { manager->HandleSystemNotification(IOSResignActive); - } else if (CFStringCompare(name, (__bridge CFStringRef) UIApplicationDidBecomeActiveNotification, 0) == kCFCompareEqualTo) { + } else if (CFStringCompare(name, (__bridge CFStringRef) UIApplicationWillEnterForegroundNotification, 0) == kCFCompareEqualTo) { manager->HandleSystemNotification(IOSBecomeActive); } - } void RegisterIOSNotificationObserver(IOSNotificationObserver *observer, int mask) @@ -167,18 +166,17 @@ void RemoveIOSNotificationObserver(IOSNotificationObserver *observer) IOSNotificationManager::IOSNotificationManager() { - CFNotificationCenterAddObserver( - CFNotificationCenterGetLocalCenter(), this, &IOSNotificationHandler, - (__bridge CFStringRef) UIApplicationWillResignActiveNotification, NULL, - CFNotificationSuspensionBehaviorDeliverImmediately); + CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, &IOSNotificationHandler, + (__bridge CFStringRef) UIApplicationDidEnterBackgroundNotification, NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); - CFNotificationCenterAddObserver( - CFNotificationCenterGetLocalCenter(), this, &IOSNotificationHandler, - (__bridge CFStringRef) UIApplicationDidBecomeActiveNotification, NULL, - CFNotificationSuspensionBehaviorDeliverImmediately); + CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, &IOSNotificationHandler, + (__bridge CFStringRef) UIApplicationWillEnterForegroundNotification, NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); dispatch_async(dispatch_get_main_queue(), ^{ - mbActive = [UIApplication sharedApplication].applicationState == UIApplicationStateActive; + // set active default, the status is Background when globle value init on iOS 12.1.4 + mbActive = true;//[UIApplication sharedApplication].applicationState != UIApplicationStateBackground; }); } diff --git a/framework/codec/videoDecoderFactory.cpp b/framework/codec/videoDecoderFactory.cpp deleted file mode 100644 index c14a04d78..000000000 --- a/framework/codec/videoDecoderFactory.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Created by moqi on 2018/11/8. -// - -#include -#include "videoDecoderFactory.h" - -namespace Cicada { - static std::deque mFactoryQueue; - static std::mutex gMutex; - - void VideoDecoderFactoryManager::registerFactory(IVideoDecoderFactory *factory) - { - std::lock_guard lock(gMutex); - - for (int i = 0; i < mFactoryQueue.size(); ++i) { - if (mFactoryQueue[i] == factory) { - return; - } - } - - mFactoryQueue.push_back(factory); - } - - IDecoder *VideoDecoderFactoryManager::create(enum AFCodecID codec, int flag, bool buildIn) - { - if (buildIn) { -#ifdef ENABLE_AVCODEC_DECODER - - if (avcodecDecoder::is_supported(codec)) { - return new avcodecDecoder(); - } - -#endif - return nullptr; - } - - std::lock_guard lock(gMutex); - - for (int i = 0; i < mFactoryQueue.size(); ++i) { - if (mFactoryQueue[i]->isSupport(codec)) { - return mFactoryQueue[i]->create(codec, flag); - } - } - - return nullptr; - } -} diff --git a/framework/codec/videoDecoderFactory.h b/framework/codec/videoDecoderFactory.h deleted file mode 100644 index f72bbb14e..000000000 --- a/framework/codec/videoDecoderFactory.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by moqi on 2018/11/8. -// - -#ifndef CICADA_PLAYER_VIDEODECODERFACTORY_H -#define CICADA_PLAYER_VIDEODECODERFACTORY_H - -#include -#include "IDecoder.h" - - -namespace Cicada{ - typedef struct IVideoDecoderFactory_t { - bool (*isSupport)(enum AFCodecID codec); - - IDecoder *(*create)(enum AFCodecID codec, int flags); - } IVideoDecoderFactory; - - class VideoDecoderFactoryManager { - public: - static void registerFactory(IVideoDecoderFactory *factory); - - static IDecoder *create(enum AFCodecID codec, int flag, bool buildIn); - }; -} - - -#endif //CICADA_PLAYER_VIDEODECODERFACTORY_H diff --git a/framework/data_source/CMakeLists.txt b/framework/data_source/CMakeLists.txt index 6e15baa81..f89592c39 100644 --- a/framework/data_source/CMakeLists.txt +++ b/framework/data_source/CMakeLists.txt @@ -58,6 +58,12 @@ if (ENABLE_CACHED_SOURCE) ) endif () +if (ANDROID) + target_sources(data_source PRIVATE + ContentDataSource.cpp + ContentDataSource.h + ) +endif () target_include_directories(data_source PRIVATE ${FFMPEG_SOURCE_DIR} diff --git a/framework/data_source/ContentDataSource.cpp b/framework/data_source/ContentDataSource.cpp new file mode 100644 index 000000000..3f6ca90df --- /dev/null +++ b/framework/data_source/ContentDataSource.cpp @@ -0,0 +1,147 @@ +// +// Created by SuperMan on 2020/9/21. +// + +#include +#include +#include +#include +#include "ContentDataSource.h" + +using namespace Cicada; + +ContentDataSource ContentDataSource::se(0); + +jclass jContentDataSourceClass = nullptr; +jmethodID jContentDataSource_init = nullptr; +jmethodID jContentDataSource_setUri = nullptr; +jmethodID jContentDataSource_open = nullptr; +jmethodID jContentDataSource_close = nullptr; +jmethodID jContentDataSource_read = nullptr; +jmethodID jContentDataSource_seek = nullptr; + +ContentDataSource::ContentDataSource(const std::string &url) : IDataSource(url) { +} + +ContentDataSource::~ContentDataSource() { + +} + +int ContentDataSource::Open(int flags) { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return -EINVAL; + } + + if (jContentDataSourceClass == nullptr) { + return -EINVAL; + } + + jobject jContent = pEnv->NewObject(jContentDataSourceClass, jContentDataSource_init); + mJContentDataSource = pEnv->NewGlobalRef(jContent); + pEnv->DeleteLocalRef(jContent); + + NewStringUTF jurl(pEnv, mUri.c_str()); + pEnv->CallVoidMethod(mJContentDataSource, jContentDataSource_setUri, jurl.getString()); + + jint ret = pEnv->CallIntMethod(mJContentDataSource, jContentDataSource_open, (jint) flags); + JniException::clearException(pEnv); + return (int) ret; +} + +void ContentDataSource::Close() { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return; + } + + pEnv->CallVoidMethod(mJContentDataSource, jContentDataSource_close); + JniException::clearException(pEnv); +} + +int64_t ContentDataSource::Seek(int64_t offset, int whence) { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return -EINVAL; + } + + jlong ret = pEnv->CallLongMethod(mJContentDataSource, jContentDataSource_seek, (jlong) offset, + (jint) whence); + JniException::clearException(pEnv); + return (int64_t) ret; +} + +int ContentDataSource::Read(void *buf, size_t nbyte) { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return -EINVAL; + } + + jbyteArray buffer = pEnv->NewByteArray((jsize) nbyte); + int readSize = (int) pEnv->CallIntMethod(mJContentDataSource, jContentDataSource_read, buffer); + if (readSize <= 0) { + return readSize; + } + jboolean isCopy = false; + jbyte *jBytes = pEnv->GetByteArrayElements(buffer, &isCopy); + memcpy(buf, jBytes, readSize); + + pEnv->ReleaseByteArrayElements(buffer, jBytes, 0); + pEnv->DeleteLocalRef(buffer); + return readSize; +} +void ContentDataSource::Interrupt(bool interrupt) { + IDataSource::Interrupt(interrupt); +} + +std::string ContentDataSource::Get_error_info(int error) { + return IDataSource::Get_error_info(error); +} + +std::string ContentDataSource::GetOption(const std::string &key) { + return IDataSource::GetOption(key); +} + +void ContentDataSource::init() { + + if (jContentDataSourceClass != nullptr) { + return; + } + + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return; + } + + FindClass nativePlayerClass(pEnv, "com/cicada/player/utils/ContentDataSource"); + jclass dataSourceClass = nativePlayerClass.getClass(); + if (dataSourceClass == nullptr) { + return; + } + jContentDataSourceClass = static_cast(pEnv->NewGlobalRef(dataSourceClass)); + jContentDataSource_init = pEnv->GetMethodID(jContentDataSourceClass, "", "()V"); + jContentDataSource_setUri = pEnv->GetMethodID(jContentDataSourceClass, "setUri", + "(Ljava/lang/String;)V"); + jContentDataSource_open = pEnv->GetMethodID(jContentDataSourceClass, "open", "(I)I"); + jContentDataSource_read = pEnv->GetMethodID(jContentDataSourceClass, "read", "([B)I"); + jContentDataSource_seek = pEnv->GetMethodID(jContentDataSourceClass, "seek", "(JI)J"); + jContentDataSource_close = pEnv->GetMethodID(jContentDataSourceClass, "close", "()V"); +} + + +void ContentDataSource::unInit() { + JniEnv env{}; + JNIEnv *pEnv = env.getEnv(); + if (pEnv == nullptr) { + return; + } + if (jContentDataSourceClass != nullptr) { + pEnv->DeleteGlobalRef(jContentDataSourceClass); + jContentDataSourceClass = nullptr; + } +} diff --git a/framework/data_source/ContentDataSource.h b/framework/data_source/ContentDataSource.h new file mode 100644 index 000000000..5bc410e18 --- /dev/null +++ b/framework/data_source/ContentDataSource.h @@ -0,0 +1,72 @@ +// +// Created by SuperMan on 2020/9/21. +// + +#ifndef SOURCE_CONTENTDATASOURCE_H +#define SOURCE_CONTENTDATASOURCE_H + +#include +#include +#include +#include +#include +#include +#include "IDataSource.h" +#include "dataSourcePrototype.h" + +namespace Cicada { + class ContentDataSource : public IDataSource, private dataSourcePrototype { + public: + + static bool probe(const std::string &path) { + return AfString::startWith(path, "content://"); + }; + + explicit ContentDataSource(const std::string &url); + + ~ContentDataSource() override; + + int Open(int flags) override; + + void Close() override; + + int64_t Seek(int64_t offset, int whence) override; + + int Read(void *buf, size_t nbyte) override; + + void Interrupt(bool interrupt) override; + + std::string Get_error_info(int error) override; + + std::string GetOption(const std::string &key) override; + + static void init(); + + static void unInit(); + private: + + private: + + private: + explicit ContentDataSource(int dummy) : IDataSource("") { + addPrototype(this); + } + + Cicada::IDataSource *clone(const std::string &uri) override { + return new ContentDataSource(uri); + }; + + bool is_supported(const std::string &uri) override { + return probe(uri); + }; + + static ContentDataSource se; + + private: + + jobject mJContentDataSource = nullptr; + }; +} + + +#endif //SOURCE_CONTENTDATASOURCE_H diff --git a/framework/data_source/IDataSource.cpp b/framework/data_source/IDataSource.cpp index 5cdb99b5f..5c2597ef4 100644 --- a/framework/data_source/IDataSource.cpp +++ b/framework/data_source/IDataSource.cpp @@ -86,6 +86,12 @@ namespace Cicada { return 0; } + void IDataSource::setPost(bool post, int64_t size, const uint8_t *data) { + mBPost = post; + mPostData = data; + mPostSize = size; + } + std::string IDataSource::SourceConfig::toString() { CicadaJSONItem item{}; diff --git a/framework/data_source/IDataSource.h b/framework/data_source/IDataSource.h index a217000d6..582a87df1 100644 --- a/framework/data_source/IDataSource.h +++ b/framework/data_source/IDataSource.h @@ -67,6 +67,8 @@ namespace Cicada { virtual int setRange(int64_t start, int64_t end); + virtual void setPost(bool post, int64_t size, const uint8_t *data); + virtual int Open(int flags) = 0; virtual int Open(const std::string &url); @@ -101,6 +103,10 @@ namespace Cicada { int64_t rangeStart{INT64_MIN}; int64_t rangeEnd{INT64_MIN}; + bool mBPost{false}; + const uint8_t *mPostData{nullptr}; + int64_t mPostSize{0}; + }; } diff --git a/framework/data_source/cache/cachedSource.cpp b/framework/data_source/cache/cachedSource.cpp index 5d0503200..0168633bd 100644 --- a/framework/data_source/cache/cachedSource.cpp +++ b/framework/data_source/cache/cachedSource.cpp @@ -20,7 +20,11 @@ namespace Cicada { int cachedSource::onReadSource(uint8_t *buffer, int size, uint64_t pos) { int sizeRead = 0; - mDataSource->Seek(pos, SEEK_SET); + int64_t seekPos = mDataSource->Seek(pos, SEEK_SET); + + if (seekPos < 0) { + return seekPos; + } while (size > 0) { int ret; diff --git a/framework/data_source/cache/slice.cpp b/framework/data_source/cache/slice.cpp index bb433f4d9..787a5f93b 100644 --- a/framework/data_source/cache/slice.cpp +++ b/framework/data_source/cache/slice.cpp @@ -69,7 +69,7 @@ namespace Cicada { int slice::readAt(void *buffer, int size, uint64_t offset) { - int readSize = (int) MIN(size, mSize - offset); + int readSize = (int) MIN(size, (int) (mSize - offset)); if (readSize > 0 && buffer != nullptr) { memcpy(buffer, mBufferPtr + offset, static_cast(readSize)); diff --git a/framework/data_source/curl/CURLConnection.cpp b/framework/data_source/curl/CURLConnection.cpp index 0cc1126fd..0a3e9c478 100644 --- a/framework/data_source/curl/CURLConnection.cpp +++ b/framework/data_source/curl/CURLConnection.cpp @@ -115,6 +115,17 @@ Cicada::CURLConnection::CURLConnection(Cicada::IDataSource::SourceConfig *pConfi multi_handle = curl_multi_init(); } +void CURLConnection::setSSLBackEnd(curl_sslbackend sslbackend) +{ + //For https ignore CA certificates + curl_easy_setopt(mHttp_handle, CURLOPT_SSL_VERIFYPEER, FALSE); + + // openssl not verify host, otherwise will return CURLE_PEER_FAILED_VERIFICATION + if (sslbackend == CURLSSLBACKEND_OPENSSL) { + curl_easy_setopt(mHttp_handle, CURLOPT_SSL_VERIFYHOST, FALSE); + } +} + Cicada::CURLConnection::~CURLConnection() { if (multi_handle && mHttp_handle) { @@ -292,6 +303,17 @@ void Cicada::CURLConnection::setSource(const string &location, struct curl_slist curl_easy_setopt(mHttp_handle, CURLOPT_RESOLVE, reSolveList); } } +void CURLConnection::setPost(bool post, int64_t size, const uint8_t *data) +{ + if (post) { + curl_easy_setopt(mHttp_handle, CURLOPT_POST, 1); + curl_easy_setopt(mHttp_handle, CURLOPT_POSTFIELDSIZE, (long) size); + curl_easy_setopt(mHttp_handle, CURLOPT_POSTFIELDS, data); + + } else { + curl_easy_setopt(mHttp_handle, CURLOPT_POST, 0); + } +} int CURLConnection::my_trace(CURL *handle, curl_infotype type, char *data, size_t size, @@ -356,9 +378,6 @@ int Cicada::CURLConnection::esayHandle_set_common_opt() curl_easy_setopt(mHttp_handle, CURLOPT_DEBUGDATA, this); curl_easy_setopt(mHttp_handle, CURLOPT_HEADERFUNCTION, write_response); curl_easy_setopt(mHttp_handle, CURLOPT_HEADERDATA, this); -//For https ignore CA certificates - curl_easy_setopt(mHttp_handle, CURLOPT_SSL_VERIFYPEER, FALSE); - // curl_easy_setopt(mHttp_handle, CURLOPT_SSL_VERIFYHOST, FALSE); return 0; } @@ -485,6 +504,10 @@ int CURLConnection::FillBuffer(uint32_t want) case CURLE_OUT_OF_MEMORY: return FRAMEWORK_ERR(ENOMEM); + case CURLE_URL_MALFORMAT: + return gen_framework_errno(error_class_network, network_errno_url_malformat); + + default: return FRAMEWORK_ERR(EIO); } @@ -493,13 +516,12 @@ int CURLConnection::FillBuffer(uint32_t want) } // Don't retry when we didn't "see" any error -#ifndef WIN32 - if (CURLResult == CURLE_OK) { - return FRAMEWORK_NET_ERR_UNKNOWN; + // we assume eof + AF_LOGW("assume a abnormal eos\n"); + return 0; } -#endif // Close handle disconnect(); @@ -674,7 +696,7 @@ int CURLConnection::short_seek(int64_t off) return ret; } - AF_LOGI("read buffer size %d need is %d\n", RingBuffergetMaxReadSize(pRbuf), delta - len); + AF_LOGI("read buffer size %" PRIu32 " need is %d\n", RingBuffergetMaxReadSize(pRbuf), (int) (delta - len)); if (!RingBufferSkipBytes(pRbuf, (int) (delta - len))) { AF_LOGI("%s - Failed to skip to position after having filled buffer", __FUNCTION__); @@ -723,4 +745,27 @@ void CURLConnection::updateSource(const string &location) { curl_easy_setopt(mHttp_handle, CURLOPT_URL, location.c_str()); mFileSize = -1; + + uri = location; + if (reSolveList) { + curl_slist_free_all(reSolveList); + } + + CURLSH *sh = nullptr; + reSolveList = CURLShareInstance::Instance()->getHosts(uri, &sh); + assert(sh != nullptr); + curl_easy_setopt(mHttp_handle, CURLOPT_SHARE, sh); + + if (reSolveList != nullptr) { + curl_easy_setopt(mHttp_handle, CURLOPT_RESOLVE, reSolveList); + } +} + +void CURLConnection::updateHeaderList(struct curl_slist *headerList) +{ + if (headerList) { + curl_easy_setopt(mHttp_handle, CURLOPT_HTTPHEADER, headerList); + } else { + curl_easy_setopt(mHttp_handle, CURLOPT_HTTPHEADER, NULL); + } } diff --git a/framework/data_source/curl/CURLConnection.h b/framework/data_source/curl/CURLConnection.h index 037a994b9..13ac60534 100644 --- a/framework/data_source/curl/CURLConnection.h +++ b/framework/data_source/curl/CURLConnection.h @@ -16,14 +16,20 @@ namespace Cicada { public: explicit CURLConnection(Cicada::IDataSource::SourceConfig *pConfig); + void setSSLBackEnd(curl_sslbackend sslbackend); + ~CURLConnection(); void disconnect(); void setSource(const std::string &location, struct curl_slist *headerList); + void setPost(bool post, int64_t size, const uint8_t *data); + void updateSource(const std::string &location); + void updateHeaderList(struct curl_slist *headerList); + void setInterrupt(std::atomic_bool *inter); void SetResume(int64_t pos); diff --git a/framework/data_source/curl/curlShare.cpp b/framework/data_source/curl/curlShare.cpp index 228ee9c72..a3c924158 100644 --- a/framework/data_source/curl/curlShare.cpp +++ b/framework/data_source/curl/curlShare.cpp @@ -3,6 +3,7 @@ // #include "curlShare.h" +static int gCurlSharedLive =-1; Cicada::curlShare::curlShare(uint64_t flags) { @@ -14,24 +15,29 @@ Cicada::curlShare::curlShare(uint64_t flags) curl_share_setopt(mShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); if (flags & SHARED_DNS) curl_share_setopt(mShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); + gCurlSharedLive = 1; } void Cicada::curlShare::lockData(CURL *handle, curl_lock_data data, curl_lock_access access, void *useptr) { auto *pThis = reinterpret_cast(useptr); - pThis->mutexes_[data].lock(); + if (gCurlSharedLive == 1) { + pThis->mutexes_[data].lock(); + } } void Cicada::curlShare::unlockData(CURL *handle, curl_lock_data data, void *useptr) { auto *pThis = reinterpret_cast(useptr); - pThis->mutexes_[data].unlock(); + if (gCurlSharedLive == 1) { + pThis->mutexes_[data].unlock(); + } } Cicada::curlShare::~curlShare() { + gCurlSharedLive = 0; curl_share_cleanup(mShare); - } Cicada::curlShare::operator CURLSH *() const diff --git a/framework/data_source/curl/curl_data_source.cpp b/framework/data_source/curl/curl_data_source.cpp index b3dddd5ae..33af75e59 100644 --- a/framework/data_source/curl/curl_data_source.cpp +++ b/framework/data_source/curl/curl_data_source.cpp @@ -22,10 +22,10 @@ #include #include //#include +#include #include - // TODO: move to another file #if defined(WIN32) || defined(WIN64) #define strcasecmp _stricmp @@ -42,13 +42,17 @@ static pthread_once_t once = PTHREAD_ONCE_INIT; using namespace Cicada; +static curl_sslbackend g_sslbackend = CURLSSLBACKEND_NONE; + CurlDataSource CurlDataSource::se(0); using std::string; CURLConnection *CurlDataSource::initConnection() { auto *pHandle = new CURLConnection(pConfig); + pHandle->setSSLBackEnd(g_sslbackend); pHandle->setSource(mLocation, headerList); + pHandle->setPost(mBPost, mPostSize, mPostData); return pHandle; } @@ -79,6 +83,8 @@ int CurlDataSource::curl_connect(CURLConnection *pConnection, int64_t filePos) if (length > 0.0) { mFileSize = pConnection->tell() + (int64_t) length; //AF_LOGE("file size is %lld\n",mFileSize); + } else { + mFileSize = 0; } // if (curlContext.fileSize == 0) @@ -89,8 +95,6 @@ int CurlDataSource::curl_connect(CURLConnection *pConnection, int64_t filePos) curl_easy_getinfo(pConnection->getCurlHandle(), CURLINFO_EFFECTIVE_URL, &location)) { if (location) { mLocation = location; - } else { - mLocation = ""; } } @@ -113,10 +117,23 @@ int CurlDataSource::curl_connect(CURLConnection *pConnection, int64_t filePos) return 0; } +static curl_sslbackend getCurlSslBackend() +{ + const curl_ssl_backend **list; + CURLsslset result = curl_global_sslset((curl_sslbackend) -1, nullptr, &list); + assert(result == CURLSSLSET_UNKNOWN_BACKEND); + // we only build one ssl backend + if (list[0]) { + return list[0]->id; + } + + return CURLSSLBACKEND_NONE; +} static void init_curl() { + g_sslbackend = getCurlSslBackend(); curl_global_init(CURL_GLOBAL_DEFAULT); #if (OPENSSL_VERSION_NUMBER < 0x10100000L) // openssl_thread_setup(); @@ -220,6 +237,23 @@ int CurlDataSource::Open(const string &url) mLocation = (isRTMP ? (url + " live=1").c_str() : url.c_str()); // only change url, don,t change share and resolve mPConnection->updateSource(mLocation); + + if (headerList) { + curl_slist_free_all(headerList); + headerList = nullptr; + } + + std::vector &customHeaders = mConfig.customHeaders; + + for (string &item : customHeaders) { + if (!item.empty()) { + headerList = curl_slist_append(headerList, item.c_str()); + } + } + + mPConnection->updateHeaderList(headerList); + mPConnection->setPost(mBPost, mPostSize, mPostData); + int ret = curl_connect(mPConnection, rangeStart != INT64_MIN ? rangeStart : 0); mOpenTimeMS = af_gettime_relative() / 1000 - mOpenTimeMS; @@ -250,20 +284,29 @@ void CurlDataSource::closeConnections(bool current) } if (deleteConnection) { - AsyncJob::Instance()->addJob([deleteConnection] { + if (AsyncJob::Instance()) { + AsyncJob::Instance()->addJob([deleteConnection] { delete deleteConnection; }); + } else { delete deleteConnection; - }); + } } if (pConnections) { - AsyncJob::Instance()->addJob([pConnections] { - for (auto item = pConnections->begin(); item != pConnections->end();) - { + if (AsyncJob::Instance()) { + AsyncJob::Instance()->addJob([pConnections] { + for (auto item = pConnections->begin(); item != pConnections->end();) { + delete *item; + item = pConnections->erase(item); + } + delete pConnections; + }); + } else { + for (auto item = pConnections->begin(); item != pConnections->end();) { delete *item; item = pConnections->erase(item); } delete pConnections; - }); + } } } @@ -298,8 +341,13 @@ if (!mPConnection) { return offset; } - if (offset > mFileSize) { - return -1; + /* do not try to make a new connection if seeking past the end of the file */ + if (rangeEnd != INT64_MIN || mFileSize > 0) { + uint64_t end_pos = rangeEnd != INT64_MIN ? rangeEnd : mFileSize; + if (offset >= end_pos) { + mPConnection->SetResume(offset); + return offset; + } } if (offset == mFileSize) { @@ -310,7 +358,7 @@ if (!mPConnection) { AF_LOGI("short seek ok\n"); return offset; } else { - AF_LOGI("short seek filed\n"); + AF_LOGI("short seek failed\n"); } CURLConnection *con = nullptr; @@ -339,11 +387,11 @@ if (!mPConnection) { AF_LOGW("short seek ok\n"); return offset; } else { - AF_LOGW("short seek filed\n"); + AF_LOGW("short seek failed\n"); } int64_t ret = TrySeekByNewConnection(offset); - return (ret >= 0) ? ret : -1; + return ret; } int64_t CurlDataSource::TrySeekByNewConnection(int64_t offset) @@ -381,11 +429,23 @@ int CurlDataSource::Read(void *buf, size_t size) { int ret = 0; - if (rangeEnd != INT64_MIN) { - size = std::min(size, (size_t) (rangeEnd - mPConnection->tell())); + if (rangeEnd != INT64_MIN || mFileSize > 0) { + /* + * avoid read after seek to end + */ - if (size == 0) { - return 0; + int64_t end = mFileSize; + if (rangeEnd > 0) { + end = rangeEnd; + } + end = std::min(mFileSize, end); + + if (end > 0) { + size = std::min(size, (size_t)(end - mPConnection->tell())); + + if (size <= 0) { + return 0; + } } } @@ -509,3 +569,8 @@ CurlDataSource::CurlDataSource(int dummy) : IDataSource("") init_curl(); addPrototype(this); } + +std::string CurlDataSource::GetUri() +{ + return mLocation; +} diff --git a/framework/data_source/curl/curl_data_source.h b/framework/data_source/curl/curl_data_source.h index 8afff17de..c4b98f731 100644 --- a/framework/data_source/curl/curl_data_source.h +++ b/framework/data_source/curl/curl_data_source.h @@ -37,6 +37,8 @@ namespace Cicada { void Interrupt(bool interrupt) override; + std::string GetUri() override; + private: CURLConnection *initConnection(); @@ -62,6 +64,8 @@ namespace Cicada { static CurlDataSource se; + void closeConnections(bool current); + private: const static int max_connection = 1; std::string mLocation; @@ -82,8 +86,6 @@ namespace Cicada { std::string mConnectInfo; bool mBDummy = false; std::vector* mConnections {nullptr}; - - void closeConnections(bool current); }; } diff --git a/framework/data_source/dataSourcePrototype.cpp b/framework/data_source/dataSourcePrototype.cpp index ff4567b16..b7b3e290d 100644 --- a/framework/data_source/dataSourcePrototype.cpp +++ b/framework/data_source/dataSourcePrototype.cpp @@ -3,7 +3,9 @@ // #include "dataSourcePrototype.h" +#ifdef ENABLE_CURL_SOURCE #include "data_source/curl/curl_data_source.h" +#endif #include "../../plugin/BiDataSource.h" #include "ffmpeg_data_source.h" diff --git a/framework/data_source/ffmpeg_data_source.cpp b/framework/data_source/ffmpeg_data_source.cpp index 1fb2c33a1..ff7c32ae4 100644 --- a/framework/data_source/ffmpeg_data_source.cpp +++ b/framework/data_source/ffmpeg_data_source.cpp @@ -11,8 +11,8 @@ #include extern "C" { +#include #include -#include } using std::string; namespace Cicada { @@ -35,7 +35,7 @@ namespace Cicada { { AVDictionary *format_opts = nullptr; av_dict_set_int(&format_opts, "rw_timeout", mConfig.low_speed_time_ms * 1000, 0); - int ret = ffurl_open(&mPuc, mUri.c_str(), AVIO_FLAG_READ | AVIO_FLAG_NONBLOCK, &mInterruptCB, &format_opts); + int ret = avio_open2(&mPuc, mUri.c_str(), AVIO_FLAG_READ | AVIO_FLAG_NONBLOCK, &mInterruptCB, &format_opts); if (ret == AVERROR_PROTOCOL_NOT_FOUND) { ret = FRAMEWORK_ERR_PROTOCOL_NOT_SUPPORT; @@ -44,8 +44,12 @@ namespace Cicada { av_dict_free(&format_opts); } + if (ret < 0) { + AF_LOGE("open error\n"); + return ret; + } if (rangeStart != INT64_MIN) { - ffurl_seek(mPuc, (int64_t) rangeStart, SEEK_SET); + avio_seek(mPuc, (int64_t) rangeStart, SEEK_SET); } return ret; @@ -54,26 +58,37 @@ namespace Cicada { void ffmpegDataSource::Close() { if (mPuc) { - ffurl_closep(&mPuc); - mPuc = nullptr; + avio_closep(&mPuc); } } int64_t ffmpegDataSource::Seek(int64_t offset, int whence) { - if (whence == SEEK_SIZE) { - whence = AVSEEK_SIZE; + if (mPuc == nullptr) { + return -EINVAL; } - - if (mPuc) { - return ffurl_seek(mPuc, offset, whence); + int64_t pos = offset; + switch (whence) { + case SEEK_SIZE: + return avio_size(mPuc); + case SEEK_END: { + int64_t size = avio_size(mPuc); + if (size <= 0) { + return -EINVAL; + } + pos = size + offset; + whence = SEEK_SET; + } + default: + return avio_seek(mPuc, pos, whence); } - - return -EINVAL; } int ffmpegDataSource::Read(void *buf, size_t nbyte) { + if (mPuc == nullptr) { + return -EINVAL; + } if (rangeEnd != INT64_MIN) { nbyte = std::min(nbyte, (size_t) (rangeEnd - Seek(0, SEEK_CUR))); @@ -82,7 +97,7 @@ namespace Cicada { } } - int ret = ffurl_read(mPuc, (unsigned char *) buf, nbyte); + int ret = avio_read(mPuc, (unsigned char *) buf, nbyte); if (ret == AVERROR_EOF) { ret = 0; diff --git a/framework/data_source/ffmpeg_data_source.h b/framework/data_source/ffmpeg_data_source.h index 7cdb60a35..0f142cbc0 100644 --- a/framework/data_source/ffmpeg_data_source.h +++ b/framework/data_source/ffmpeg_data_source.h @@ -64,7 +64,7 @@ namespace Cicada { static ffmpegDataSource se; private: - URLContext *mPuc{}; + AVIOContext *mPuc{}; AVIOInterruptCB mInterruptCB{}; int mInterrupted{}; char mErrorMsg[AV_ERROR_MAX_STRING_SIZE]{}; diff --git a/framework/demuxer/AVBSF.cpp b/framework/demuxer/AVBSF.cpp index 4f996bf03..03ce93bcc 100644 --- a/framework/demuxer/AVBSF.cpp +++ b/framework/demuxer/AVBSF.cpp @@ -11,6 +11,7 @@ extern "C" { #include #include "AVBSF.h" #include "utils/ffmpeg_utils.h" +#include "AdtsBSF.h" namespace Cicada { AVBSF::AVBSF() @@ -193,6 +194,8 @@ namespace Cicada { { if (name == "h26xAnnexb2xVcc") { return new AFAVBSF(); + }else if(name == "latm2Adts") { + return new AdtsBSF(); } return new AVBSF(); diff --git a/framework/demuxer/AdtsBSF.cpp b/framework/demuxer/AdtsBSF.cpp new file mode 100644 index 000000000..8511d5226 --- /dev/null +++ b/framework/demuxer/AdtsBSF.cpp @@ -0,0 +1,104 @@ +// +// Created by SuperMan on 2020/10/15. +// +#define LOG_TAG "AdtsBSF" + +#include "AdtsBSF.h" +#include +#include +#include + +using namespace Cicada; + +#define IO_BUFFER_SIZE 32768 + +AdtsBSF::AdtsBSF() +{} + +AdtsBSF::~AdtsBSF() +{ + if (mFormatContext != nullptr) { + avio_flush(mFormatContext->pb); + avio_context_free(&mFormatContext->pb); + avformat_free_context(mFormatContext); + mFormatContext = nullptr; + } + + if (mIobuf != nullptr) { + av_free(mIobuf); + mIobuf = nullptr; + } +} + +int AdtsBSF::init(const std::string &name, AVCodecParameters *codecpar) +{ + if (name != "aacAdts") { + return -EINVAL; + } + + if (codecpar->codec_id != AV_CODEC_ID_AAC) { + return -EINVAL; + } + + int ret = avformat_alloc_output_context2(&mFormatContext, nullptr, "adts", nullptr); + if (ret < 0) { + AF_LOGE("create adts muxer fail %d", ret); + return ret; + } + + mIobuf = (uint8_t *) av_malloc(IO_BUFFER_SIZE); + mFormatContext->pb = avio_alloc_context(mIobuf, IO_BUFFER_SIZE, AVIO_FLAG_WRITE, this, nullptr, io_write, nullptr); + mFormatContext->pb->seekable = 0; + stream = avformat_new_stream(mFormatContext, nullptr); + ret = avcodec_parameters_copy(stream->codecpar, codecpar); + if (ret < 0) { + AF_LOGE("create adts codec par fail %d", ret); + return ret; + } + + ret = avformat_write_header(mFormatContext, nullptr); + if (ret < 0) { + AF_LOGE("create adts write head fail %d", ret); + return ret; + } + + return 0; +} + +int AdtsBSF::io_write(void *opaque, uint8_t *buf, int size) +{ + auto *adtsBSF = static_cast(opaque); + assert(adtsBSF->targetPkt != nullptr); + + int growBy = size - adtsBSF->targetPkt->size; + av_grow_packet(adtsBSF->targetPkt, growBy); + memcpy(adtsBSF->targetPkt->data, buf, size); + + return size; +} + +int AdtsBSF::push(AVPacket *pkt) +{ + return 0; +} + +int AdtsBSF::pull(AVPacket *pkt) +{ + if (pkt == nullptr) { + return 0; + } + + if ((AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { + //is adts + return pkt->size; + } + + targetPkt = pkt; + + int stream_index = pkt->stream_index; + pkt->stream_index = stream->index; + av_write_frame(mFormatContext, pkt); + pkt->stream_index = stream_index; + + return pkt->size; +} \ No newline at end of file diff --git a/framework/demuxer/AdtsBSF.h b/framework/demuxer/AdtsBSF.h new file mode 100644 index 000000000..9587a6a5f --- /dev/null +++ b/framework/demuxer/AdtsBSF.h @@ -0,0 +1,42 @@ +// +// Created by SuperMan on 2020/10/15. +// + +#ifndef SOURCE_ADTSBSF_H +#define SOURCE_ADTSBSF_H + +extern "C" { +#include +}; + +#include "AVBSF.h" + +namespace Cicada { + class AdtsBSF : public IAVBSF { + public: + AdtsBSF(); + + ~AdtsBSF() override; + + int init(const std::string &name, AVCodecParameters *codecpar) override; + + int push(AVPacket *pkt) override; + + int pull(AVPacket *pkt) override; + + private: + static int io_write(void *opaque, uint8_t *buf, int size); + + private: + + uint8_t *mIobuf = nullptr; + AVFormatContext *mFormatContext = nullptr; + AVStream *stream = nullptr; + + AVPacket *targetPkt = nullptr; + }; + +} + + +#endif //SOURCE_ADTSBSF_H diff --git a/framework/demuxer/CMakeLists.txt b/framework/demuxer/CMakeLists.txt index 8e4b46914..06c32313e 100644 --- a/framework/demuxer/CMakeLists.txt +++ b/framework/demuxer/CMakeLists.txt @@ -24,6 +24,8 @@ target_sources(demuxer PRIVATE avFormatDemuxer.h AVBSF.cpp AVBSF.h + AdtsBSF.cpp + AdtsBSF.h sample_decrypt/HLSSampleAesDecrypter.h sample_decrypt/HLSSampleAesDecrypter.cpp demuxerPrototype.cpp @@ -46,6 +48,7 @@ if (ENABLE_HLS_DEMUXER) target_compile_definitions(demuxer PRIVATE ENABLE_HLS_DEMUXER) target_sources(demuxer PRIVATE play_list/segment.cpp + play_list/SegmentPart.h play_list/Representation.cpp play_list/AdaptationSet.cpp play_list/Period.cpp diff --git a/framework/demuxer/IDemuxer.h b/framework/demuxer/IDemuxer.h index fb5074229..f848406af 100644 --- a/framework/demuxer/IDemuxer.h +++ b/framework/demuxer/IDemuxer.h @@ -28,6 +28,11 @@ namespace Cicada { demuxer_type_webvtt, } demuxer_type; + typedef enum header_type { + header_type_no_touch, + header_type_merge, + header_type_extract, + } header_type; class CICADA_CPLUS_EXTERN IDemuxer : public OptionOwner, public IDCA { public: @@ -81,7 +86,7 @@ namespace Cicada { virtual void flush() = 0; - virtual int Seek(int64_t us, int flags, int index) = 0; + virtual int64_t Seek(int64_t us, int flags, int index) = 0; virtual int GetNbStreams() const = 0; @@ -124,6 +129,11 @@ namespace Cicada { return false; } + virtual int64_t getMaxGopTimeUs() + { + return INT64_MIN; + } + virtual void setDataSourceIO() { @@ -137,20 +147,20 @@ namespace Cicada { virtual const std::string GetProperty(int index, const string &key) const { return ""; } - virtual void SetOption(const options *opts) + virtual int SetOption(const std::string &key, const int64_t value) { - mOpts = opts; - }; + return 0; + } - virtual int SetOption(const std::string &key, const int64_t value) + virtual int SetOption(const std::string &key, const std::string& value) { return 0; } - virtual void setBitStreamFormat(bool vMergeHeader, bool aMergeHeader) + virtual void setBitStreamFormat(header_type vMergeHeader, header_type aMergeHeader) { mMergeVideoHeader = vMergeHeader; - mMergerAudioHeader = aMergeHeader; + mMergeAudioHeader = aMergeHeader; } virtual void setDemuxerCb(std::function func) @@ -175,6 +185,11 @@ namespace Cicada { { return mName; } + + virtual bool isRealTimeStream(int index) + { + return false; + } protected: demuxer_callback_read mReadCb{nullptr}; @@ -186,8 +201,8 @@ namespace Cicada { string mPath{}; IDataSource::SourceConfig sourceConfig{}; - bool mMergeVideoHeader = false; - bool mMergerAudioHeader = false; + header_type mMergeVideoHeader = header_type ::header_type_no_touch; + header_type mMergeAudioHeader = header_type ::header_type_no_touch; DemuxerMetaInfo *mMetaInfo = nullptr; std::string mName = "IDemuxer"; diff --git a/framework/demuxer/avFormatDemuxer.cpp b/framework/demuxer/avFormatDemuxer.cpp index dd3a33545..4d876638a 100644 --- a/framework/demuxer/avFormatDemuxer.cpp +++ b/framework/demuxer/avFormatDemuxer.cpp @@ -144,6 +144,18 @@ namespace Cicada { return ret; } + int probeHeader_nbStreams = mCtx->nb_streams; + int64_t probeHeader_pos = -1; + int probeHeader_seekCount = -1; + if (mCtx->pb != nullptr) { + probeHeader_pos = mCtx->pb->bytes_read; + probeHeader_seekCount = mCtx->pb->seek_count; + } + + if (mSeekCb == nullptr && strcmp(mCtx->iformat->name, "mpegts") == 0) { + mNedParserPkt = true; + } + mCtx->flags |= AVFMT_FLAG_GENPTS; // TODO: add a opt to set fps probe mCtx->fps_probe_size = 0; @@ -176,6 +188,18 @@ namespace Cicada { return ret; } + int64_t probeStream_pos = -1; + int probeStream_seekCount = -1; + if (mCtx->pb != nullptr) { + probeStream_pos = mCtx->pb->bytes_read; + probeStream_seekCount = mCtx->pb->seek_count; + } + + int probeStream_nbFrames = 0; + for(int i = 0 ; i < mCtx->nb_streams; i++) { + probeStream_nbFrames += mCtx->streams[i]->codec_info_nb_frames; + } + /* * this flag is only affect on mp3 and flac */ @@ -188,6 +212,12 @@ namespace Cicada { CicadaJSONItem json; json.addValue("cost", (int) used); json.addValue("time", (double) af_getsteady_ms()); + json.addValue("headerPos" , (double) probeHeader_pos); + json.addValue("headerSeekCount" , (int) probeHeader_seekCount); + json.addValue("headerNbStreams" , (int) probeHeader_nbStreams); + json.addValue("streamPos" , (double)probeStream_pos); + json.addValue("streamSeekCount" , (int)probeStream_seekCount); + json.addValue("streamNbFrames" , (int)probeStream_nbFrames); mProbeString = json.printJSON(); if (mStartTime > 0 && mStartTime < mCtx->duration) { @@ -287,6 +317,10 @@ namespace Cicada { av_packet_unref(pkt); } while (true); + if (mNedParserPkt) { + av_compute_pkt_fields(mCtx, mCtx->streams[pkt->stream_index], nullptr, pkt, AV_NOPTS_VALUE, AV_NOPTS_VALUE); + } + if (pkt->pts == AV_NOPTS_VALUE) { AF_LOGW("pkt pts error\n"); } @@ -295,6 +329,20 @@ namespace Cicada { AF_LOGW("pkt dts error\n"); } + int streamIndex = pkt->stream_index; + + int encryption_info_size; + const uint8_t *new_encryption_info = av_packet_get_side_data(pkt, + AV_PKT_DATA_ENCRYPTION_INFO, + &encryption_info_size); + if (encryption_info_size > 0 && new_encryption_info != nullptr) { + mStreamCtxMap[streamIndex]->bsf = nullptr; + } else { + if (mStreamCtxMap[streamIndex]->bsf == nullptr) { + createBsf(streamIndex); + } + } + bool needUpdateExtraData = false; int new_extradata_size; const uint8_t *new_extradata = av_packet_get_side_data(pkt, @@ -303,7 +351,6 @@ namespace Cicada { if (new_extradata) { AF_LOGI("AV_PKT_DATA_NEW_EXTRADATA"); - int streamIndex = pkt->stream_index; AVCodecParameters *codecpar = mCtx->streams[streamIndex]->codecpar; av_free(codecpar->extradata); codecpar->extradata = static_cast(av_malloc(new_extradata_size + AV_INPUT_BUFFER_PADDING_SIZE)); @@ -312,10 +359,13 @@ namespace Cicada { if (mStreamCtxMap[streamIndex]->bsf) { createBsf(streamIndex); - } else { - needUpdateExtraData = true; } + needUpdateExtraData = true; } + /* + * TODO: can't support this for now, audio render only support fixed sample size + */ + av_packet_shrink_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 0); if (mStreamCtxMap[pkt->stream_index]->bsf) { // TODO: while pulling and ret value @@ -333,7 +383,7 @@ namespace Cicada { } } - err = pkt->size; + int packet_size = pkt->size; if (pkt->pts != AV_NOPTS_VALUE) { pkt->pts = av_rescale_q(pkt->pts, mCtx->streams[pkt->stream_index]->time_base, av_get_time_base_q()); @@ -344,18 +394,23 @@ namespace Cicada { } if (pkt->duration > 0) { - pkt->duration = av_rescale_q(pkt->duration, mCtx->streams[pkt->stream_index]->time_base, av_get_time_base_q()); + pkt->duration = av_rescale_q(pkt->duration, mCtx->streams[pkt->stream_index]->time_base, + av_get_time_base_q()); } else if (mCtx->streams[pkt->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { AVCodecParameters *codecpar = mCtx->streams[pkt->stream_index]->codecpar; - if (codecpar->sample_rate > 0) { + if (codecpar->sample_rate > 0 && codecpar->frame_size > 0) { pkt->duration = codecpar->frame_size * 1000000 / codecpar->sample_rate; } } packet = unique_ptr(new AVAFPacket(&pkt, mSecretDemxuer)); + if (mSecretDemxuer){ + packet->setMagicKey(mDrmMagicKey); + } + if (needUpdateExtraData) { - packet->setExtraData(new_extradata, new_extradata_size); + packet->setExtraData(mCtx->streams[streamIndex]->codecpar->extradata, mCtx->streams[streamIndex]->codecpar->extradata_size); } if (packet->getInfo().pts != INT64_MIN) { @@ -365,7 +420,7 @@ namespace Cicada { packet->getInfo().timePosition = packet->getInfo().pts - mCtx->start_time; } - return err; + return packet_size; } int avFormatDemuxer::OpenStream(int index) @@ -386,7 +441,6 @@ namespace Cicada { mStreamCtxMap[index] = unique_ptr(new AVStreamCtx()); mStreamCtxMap[index]->opened = true; - createBsf(index); return 0; } @@ -410,7 +464,7 @@ namespace Cicada { int ret = 0; const AVCodecParameters *codecpar = mCtx->streams[index]->codecpar; - if (mMergeVideoHeader) { + if (mMergeVideoHeader == header_type::header_type_merge) { if (codecpar->codec_id == AV_CODEC_ID_H264 && codecpar->extradata != nullptr && (codecpar->extradata[0] == 1)) { bsfName = "h264_mp4toannexb"; } else if (codecpar->codec_id == AV_CODEC_ID_HEVC && codecpar->extradata_size >= 5 && @@ -420,7 +474,7 @@ namespace Cicada { } // TODO: mpeg4 dump extra bsf - } else { + } else if (mMergeVideoHeader == header_type::header_type_extract) { if (codecpar->codec_id == AV_CODEC_ID_H264 && codecpar->extradata != nullptr && (codecpar->extradata[0] != 1)) { bsfName = "h26xAnnexb2xVcc"; } else if (codecpar->codec_id == AV_CODEC_ID_HEVC && codecpar->extradata_size >= 5 && @@ -428,9 +482,14 @@ namespace Cicada { AV_RB24(codecpar->extradata) != 0x000001)) { bsfName = "h26xAnnexb2xVcc"; } + }else if (mMergeVideoHeader == header_type::header_type_no_touch) { + } if (!bsfName.empty()) { +#if AF_HAVE_PTHREAD + std::lock_guard uLock(mCtxMutex); +#endif mStreamCtxMap[index]->bsf = unique_ptr(IAVBSFFactory::create(bsfName)); ret = mStreamCtxMap[index]->bsf->init(bsfName, mCtx->streams[index]->codecpar); @@ -455,7 +514,7 @@ namespace Cicada { mInterrupted = inter; } - int avFormatDemuxer::Seek(int64_t us, int flags, int index) + int64_t avFormatDemuxer::Seek(int64_t us, int flags, int index) { us = getWorkAroundSeekPos(us); if (!bOpened) { @@ -535,7 +594,10 @@ namespace Cicada { int avFormatDemuxer::GetStreamMeta(Stream_meta *meta, int index, bool sub) const { - if (index < 0 || index > mCtx->nb_streams) { +#if AF_HAVE_PTHREAD + std::lock_guard uLock(mCtxMutex); +#endif + if (index < 0 || mCtx == nullptr || index >= mCtx->nb_streams) { return -EINVAL; } @@ -669,6 +731,24 @@ namespace Cicada { return ""; } + bool avFormatDemuxer::isRealTimeStream(int index) + { +#if AF_HAVE_PTHREAD + std::lock_guard uLock(mCtxMutex); +#endif + if (mCtx == nullptr) { + return false; + } + bool isLive = (mCtx->duration == AV_NOPTS_VALUE || mCtx->duration == 0); + bool isHls = false; + bool isDash = false; + if (mCtx->iformat) { + isHls = (strcmp(mCtx->iformat->name, "hls,applehttp") == 0); + isDash = (strcmp(mCtx->iformat->name, "dash") == 0); + } + return isLive && !(isHls || isDash); + } + bool avFormatDemuxer::is_supported(const string &uri, const uint8_t *buffer, int64_t size, int *type, const Cicada::DemuxerMeta *meta, const Cicada::options *opts) { @@ -683,9 +763,7 @@ namespace Cicada { int score = AVPROBE_SCORE_RETRY; AVInputFormat *fmt = av_probe_input_format2(&pd, 1, &score); - if (fmt && - (strcmp(fmt->name, "hls,applehttp") == 0 - || strcmp(fmt->name, "webvtt") == 0)) { + if (fmt && (strcmp(fmt->name, "hls,applehttp") == 0 || strcmp(fmt->name, "webvtt") == 0 || strcmp(fmt->name, "srt") == 0)) { return false; } @@ -730,4 +808,5 @@ namespace Cicada { } return pos >= mCtx->duration - 2 * AV_TIME_BASE ? mCtx->duration - 2 * AV_TIME_BASE : pos; } + } diff --git a/framework/demuxer/avFormatDemuxer.h b/framework/demuxer/avFormatDemuxer.h index 0c0c4c903..ada36f927 100644 --- a/framework/demuxer/avFormatDemuxer.h +++ b/framework/demuxer/avFormatDemuxer.h @@ -70,12 +70,14 @@ namespace Cicada { void CloseStream(int index) override; - int Seek(int64_t us, int flags, int index) override; + int64_t Seek(int64_t us, int flags, int index) override; int ReadPacket(std::unique_ptr &packet, int index) override; virtual const std::string GetProperty(int index, const string &key) const override; + bool isRealTimeStream(int index) override; + protected: explicit avFormatDemuxer(int dummy); @@ -126,6 +128,7 @@ namespace Cicada { AVFormatContext *mCtx = nullptr; int MAX_QUEUE_SIZE = 60; // about 500ms video and audio packet bool mSecretDemxuer{false}; + std::string mDrmMagicKey{}; private: std::atomic_bool mInterrupted{false}; @@ -136,12 +139,15 @@ namespace Cicada { std::deque> mPacketQueue{}; std::atomic_bool bEOS{false}; std::atomic_bool bPaused{false}; + bool mNedParserPkt{false}; + #if AF_HAVE_PTHREAD afThread *mPthread{nullptr}; std::mutex mMutex{}; std::mutex mQueLock{}; std::condition_variable mQueCond{}; atomic mError{0}; + mutable std::mutex mCtxMutex{}; #endif }; diff --git a/framework/demuxer/avFormatSubtitleDemuxer.cpp b/framework/demuxer/avFormatSubtitleDemuxer.cpp index 48de91d76..2b690f5d8 100644 --- a/framework/demuxer/avFormatSubtitleDemuxer.cpp +++ b/framework/demuxer/avFormatSubtitleDemuxer.cpp @@ -3,6 +3,8 @@ // #define LOG_TAG "avFormatSubtitleDemuxer" +#include + #include "avFormatSubtitleDemuxer.h" #include @@ -70,7 +72,7 @@ namespace Cicada { do { ret = readPacketInternal(); - } while (ret > 0); + } while (ret >= 0); return 0; } @@ -148,7 +150,7 @@ namespace Cicada { { } - int avFormatSubtitleDemuxer::Seek(int64_t us, int flags, int index) + int64_t avFormatSubtitleDemuxer::Seek(int64_t us, int flags, int index) { mSeekPTS = us; return 0; @@ -179,10 +181,6 @@ namespace Cicada { mSeekPTS = INT64_MIN; } - if (mCurrent == mPacketMap.end()) { - return 0; - } - if (mCurrentPts == INT64_MIN) { mCurrent = mPacketMap.begin(); mCurrentPts = (*(mCurrent)).second->getInfo().pts; @@ -192,6 +190,8 @@ namespace Cicada { packet = ((*(mCurrent)).second->clone()); mCurrentPts = packet->getInfo().pts; mCurrent++; + } else { + return 0; } return static_cast(packet->getSize()); @@ -236,7 +236,7 @@ namespace Cicada { } av_packet_free(&pkt); - return 0;// EOS + return AVERROR_EOF;// EOS } if (err == AVERROR_EXIT) { @@ -281,7 +281,7 @@ namespace Cicada { int score = AVPROBE_SCORE_RETRY; AVInputFormat *fmt = av_probe_input_format2(&pd, 1, &score); - if (fmt && strcmp(fmt->name, "webvtt") == 0) { + if (fmt && (strcmp(fmt->name, "webvtt") == 0 || strcmp(fmt->name, "srt") == 0)) { return true; }; diff --git a/framework/demuxer/avFormatSubtitleDemuxer.h b/framework/demuxer/avFormatSubtitleDemuxer.h index e81eb81c3..46f593626 100644 --- a/framework/demuxer/avFormatSubtitleDemuxer.h +++ b/framework/demuxer/avFormatSubtitleDemuxer.h @@ -45,7 +45,7 @@ namespace Cicada { void CloseStream(int index) override; - int Seek(int64_t us, int flags, int index) override; + int64_t Seek(int64_t us, int flags, int index) override; int ReadPacket(std::unique_ptr &packet, int index) override; diff --git a/framework/demuxer/demuxer_service.cpp b/framework/demuxer/demuxer_service.cpp index e4da228a7..f33b38a1d 100644 --- a/framework/demuxer/demuxer_service.cpp +++ b/framework/demuxer/demuxer_service.cpp @@ -142,7 +142,10 @@ namespace Cicada { } if (!mNoFile) { - if (mPDataSource == nullptr && mSeekCb == nullptr) { + /* + * demuxer will occurred an error when set seek callback but can't seek + */ + if ((mPDataSource == nullptr || mPDataSource->Seek(0, SEEK_SIZE) <= 0) && (mSeekCb == nullptr)) { AF_LOGD("not support seek\n"); mDemuxerPtr->SetDataCallBack(read_callback, nullptr, open_callback, interrupt_callback, this); } else { @@ -287,6 +290,15 @@ namespace Cicada { return mDemuxerPtr->GetRemainSegmentCount(index); } + bool demuxer_service::isRealTimeStream(int index) + { + if (nullptr == mDemuxerPtr) { + return false; + } + + return mDemuxerPtr->isRealTimeStream(index); + } + void demuxer_service::interrupt(int inter) { AF_TRACE; @@ -294,7 +306,7 @@ namespace Cicada { return mDemuxerPtr->interrupt(inter); } - int demuxer_service::Seek(int64_t us, int flags, int index) + int64_t demuxer_service::Seek(int64_t us, int flags, int index) { AF_TRACE; @@ -391,4 +403,5 @@ namespace Cicada { return pHandle->mPDataSource->Interrupt(static_cast(inter)); } } + } diff --git a/framework/demuxer/demuxer_service.h b/framework/demuxer/demuxer_service.h index abcab6320..af29429f5 100644 --- a/framework/demuxer/demuxer_service.h +++ b/framework/demuxer/demuxer_service.h @@ -64,7 +64,7 @@ namespace Cicada { void preStop(); - int Seek(int64_t us, int flags, int index); + int64_t Seek(int64_t us, int flags, int index); static int read_callback(void *arg, uint8_t *buffer, int size); @@ -83,6 +83,7 @@ namespace Cicada { void setDemuxerCb(const std::function &func); void setDemuxerMeta(std::unique_ptr &meta); + public: @@ -102,6 +103,8 @@ namespace Cicada { int GetNbSubStream(int index); int GetRemainSegmentCount(int index); + + bool isRealTimeStream(int index); int SwitchStreamAligned(int from, int to); diff --git a/framework/demuxer/play_list/AbstractStream.h b/framework/demuxer/play_list/AbstractStream.h index f3fdc5f2f..fb76a9ac0 100644 --- a/framework/demuxer/play_list/AbstractStream.h +++ b/framework/demuxer/play_list/AbstractStream.h @@ -67,7 +67,7 @@ namespace Cicada{ mSourceConfig = config; } - virtual void setBitStreamFormat(bool vMergeHeader, bool aMergeHeader) + virtual void setBitStreamFormat(header_type vMergeHeader, header_type aMergeHeader) { mMergeVideoHeader = vMergeHeader; mMergerAudioHeader = aMergeHeader; @@ -76,8 +76,8 @@ namespace Cicada{ protected: IDataSource *mExtDataSource = nullptr; IDataSource::SourceConfig mSourceConfig{}; - bool mMergeVideoHeader = false; - bool mMergerAudioHeader = false; + header_type mMergeVideoHeader = header_type::header_type_no_touch; + header_type mMergerAudioHeader = header_type::header_type_no_touch; }; } diff --git a/framework/demuxer/play_list/HLSManager.cpp b/framework/demuxer/play_list/HLSManager.cpp index 3cf656f82..5ab657b24 100644 --- a/framework/demuxer/play_list/HLSManager.cpp +++ b/framework/demuxer/play_list/HLSManager.cpp @@ -396,9 +396,8 @@ namespace Cicada { return 0; } - int HLSManager::seek(int64_t us, int flags, int index) + int64_t HLSManager::seek(int64_t us, int flags, int index) { - int ret = 0; bool hasOpened = false; for (auto &i : mStreamInfoList) { @@ -564,4 +563,38 @@ namespace Cicada { i->mPStream->interrupt(inter); } } + + bool HLSManager::isRealTimeStream(int index) + { + for (auto &i : mStreamInfoList) { + if (i->mPStream->getId() == index) { + return i->mPStream->isRealTimeStream(); + } + } + + return false; + } + + int64_t HLSManager::getTargetDuration() + { + if (mMuxedStream) { + return mMuxedStream->getTargetDuration(); + } + + int64_t targetDuration = INT64_MIN; + for (auto &i : mStreamInfoList) { + if (i->mPStream->isOpened() && i->selected) { + int64_t streamTargetDuration = i->mPStream->getTargetDuration(); + if (streamTargetDuration > 0) { + if (targetDuration == INT64_MIN) { + targetDuration = streamTargetDuration; + } else if (streamTargetDuration < targetDuration) { + targetDuration = streamTargetDuration; + } + } + } + } + + return targetDuration; + } } diff --git a/framework/demuxer/play_list/HLSManager.h b/framework/demuxer/play_list/HLSManager.h index 82a4ed725..69418cf0c 100644 --- a/framework/demuxer/play_list/HLSManager.h +++ b/framework/demuxer/play_list/HLSManager.h @@ -43,7 +43,7 @@ namespace Cicada{ int start() override; - int seek(int64_t us, int flags, int index) override; + int64_t seek(int64_t us, int flags, int index) override; int SwitchStreamAligned(int from, int to) override; @@ -54,6 +54,10 @@ namespace Cicada{ const std::string GetProperty(int index, const string &key) override; int GetRemainSegmentCount(int index) override; + + bool isRealTimeStream(int index) override; + + int64_t getTargetDuration() override; private: std::list mStreamInfoList{}; diff --git a/framework/demuxer/play_list/HLSStream.cpp b/framework/demuxer/play_list/HLSStream.cpp index 0d604246d..085e6ecf0 100644 --- a/framework/demuxer/play_list/HLSStream.cpp +++ b/framework/demuxer/play_list/HLSStream.cpp @@ -3,22 +3,22 @@ // #define LOG_TAG "HLSStream" -#include -#include -#include -#include -#include -#include -#include "../../utils/mediaFrame.h" #include "HLSStream.h" #include "Helper.h" +#include "demuxer/IDemuxer.h" #include "segment_decrypt/SegDecryptorFactory.h" -//#define NOLOGD -#include "../../utils/frame_work_log.h" -#include "../IDemuxer.h" +#include "utils/frame_work_log.h" +#include "utils/mediaFrame.h" +#include +#include #include #include +#include +#include #include +#include +#include +#include // TODO support active and no active mode @@ -95,6 +95,29 @@ namespace Cicada { ret = pHandle->readSegment(buffer, size); + if (ret == 0) { + MoveToNextPart move_ret = pHandle->moveToNextPartialSegment(); + if (move_ret == MoveToNextPart::moveSuccess) { + return pHandle->readSegment(buffer, size); + } else if (move_ret == MoveToNextPart::tryAgain) { + int tryTimes = 150; + while (tryTimes > 0) { + af_msleep(20); + pHandle->mPTracker->reLoadPlayList(); + MoveToNextPart move_ret = pHandle->moveToNextPartialSegment(); + if (move_ret == MoveToNextPart::moveSuccess) { + return pHandle->readSegment(buffer, size); + } else if (move_ret == MoveToNextPart::segmentEnd) { + return 0; + } + --tryTimes; + }; + return 0; + } else { + return move_ret; + } + } + if (pHandle->getStreamType() == STREAM_TYPE_SUB && pHandle->mVttPtsOffSet == INT64_MIN && ret > 0) { pHandle->mVttPtsOffSet = pHandle->mWVTTParser.addBuffer(buffer, ret); @@ -123,6 +146,28 @@ namespace Cicada { return ret; } + MoveToNextPart HLSStream::moveToNextPartialSegment() + { + auto curSeg = mPTracker->getCurSegment(); + if (curSeg && curSeg->mSegType == SEG_LHLS) { + bool bHasUnusedParts = false; + bool downloadComplete = curSeg->isDownloadComplete(bHasUnusedParts); + if (bHasUnusedParts) { + curSeg->moveToNextPart(); + string uri = Helper::combinePaths(mPTracker->getBaseUri(), curSeg->getDownloadUrl()); + tryOpenSegment(uri, curSeg->rangeStart, curSeg->rangeEnd); + return MoveToNextPart::moveSuccess; + } else { + if (downloadComplete) { + return MoveToNextPart::segmentEnd; + } else { + return MoveToNextPart::tryAgain; + } + } + } + return MoveToNextPart::segmentEnd; + } + int64_t HLSStream::seekSegment(off_t offset, int whence) { int64_t ret; @@ -171,7 +216,7 @@ namespace Cicada { return 0; } - uri = Helper::combinePaths(mPTracker->getBaseUri(), mCurSeg->init_section->mUri); + uri = Helper::combinePaths(mPTracker->getBaseUri(), mCurSeg->init_section->getDownloadUrl()); ret = tryOpenSegment(uri, mCurSeg->init_section->rangeStart, mCurSeg->init_section->rangeEnd); if (ret < 0) { @@ -267,7 +312,6 @@ namespace Cicada { } if (mPTracker->isLive() && !mPTracker->isSeeked()) { - uint64_t curNum; if (mOpts) { string value = mOpts->get("liveStartIndex"); if (!value.empty()) { @@ -275,13 +319,7 @@ namespace Cicada { AF_LOGI("set liveStartIndex to %lld\n", mLiveStartIndex); } } - if (mLiveStartIndex >= 0) { - curNum = std::min(mPTracker->getFirstSegNum() + mLiveStartIndex, mPTracker->getLastSegNum()); - } else { - curNum = std::max(mPTracker->getLastSegNum() + mLiveStartIndex + 1, mPTracker->getFirstSegNum()); - } - - mPTracker->setCurSegNum(curNum); + mPTracker->MoveToLiveStartSegment(mLiveStartIndex); } mStopOnSegEnd = false; @@ -298,7 +336,7 @@ namespace Cicada { return -EAGAIN; } else { AF_LOGE("can't find seg %llu\n", mPTracker->getCurSegNum()); - return -1; + return gen_framework_errno(error_class_format, 0); } } @@ -310,7 +348,7 @@ namespace Cicada { string uri; uri = Helper::combinePaths(mPTracker->getBaseUri(), - mCurSeg->mUri); + mCurSeg->getDownloadUrl()); AF_LOGD("open uri is %s seq is %llu\n", uri.c_str(), mCurSeg->sequence); ret = tryOpenSegment(uri, mCurSeg->rangeStart, mCurSeg->rangeEnd); @@ -404,6 +442,13 @@ namespace Cicada { mDemuxerMeta = nullptr; } + for (SegmentEncryption &item: mCurSeg->encryptions) { + if (item.keyFormat.empty() || DrmUtils::isSupport(item.keyFormat)) { + mCurrentEncryption = item; + break; + } + } + ret = updateDecrypter(); if (ret < 0) { @@ -484,6 +529,7 @@ namespace Cicada { int HLSStream::tryOpenSegment(const string &uri, int64_t start, int64_t end) { + AF_LOGI("tryOpenSegment: %s\n", uri.c_str()); int retryTimes = 0; int ret; @@ -561,7 +607,7 @@ namespace Cicada { bool HLSStream::updateKey() { string keyUrl = Helper::combinePaths(mPTracker->getBaseUri(), - mCurSeg->encryption.keyUrl); + mCurrentEncryption.keyUrl); if (mKeyUrl == keyUrl) { return false; @@ -604,19 +650,19 @@ namespace Cicada { return true; } - bool HLSStream::updateIV() const + bool HLSStream::updateIV() { - if (!mCurSeg->encryption.ivStatic) { - mCurSeg->encryption.iv.clear(); - mCurSeg->encryption.iv.resize(16); + if (!mCurrentEncryption.ivStatic) { + mCurrentEncryption.iv.clear(); + mCurrentEncryption.iv.resize(16); int number = (int) mCurSeg->getSequenceNumber(); - mCurSeg->encryption.iv[15] = static_cast( + mCurrentEncryption.iv[15] = static_cast( (number /* - segment::SEQUENCE_FIRST*/) & 0xff); - mCurSeg->encryption.iv[14] = static_cast( + mCurrentEncryption.iv[14] = static_cast( ((number /* - segment::SEQUENCE_FIRST*/) >> 8) & 0xff); - mCurSeg->encryption.iv[13] = static_cast( + mCurrentEncryption.iv[13] = static_cast( ((number/* - segment::SEQUENCE_FIRST*/) >> 16) & 0xff); - mCurSeg->encryption.iv[12] = static_cast( + mCurrentEncryption.iv[12] = static_cast( ((number /* - segment::SEQUENCE_FIRST*/) >> 24) & 0xff); return true; } @@ -626,41 +672,48 @@ namespace Cicada { int HLSStream::updateSegDecrypter() { - if (mCurSeg->encryption.method == SegmentEncryption::AES_128) { + if (mCurrentEncryption.method == SegmentEncryption::AES_128) { if (updateKey()) { if (mSegDecrypter == nullptr) mSegDecrypter = unique_ptr( - SegDecryptorFactory::create(mCurSeg->encryption.method, Decrypter_read_callback, this)); + SegDecryptorFactory::create(mCurrentEncryption.method, Decrypter_read_callback, this)); mSegDecrypter->SetOption("decryption key", mKey, 16); } if (updateIV()) { - mSegDecrypter->SetOption("decryption IV", &mCurSeg->encryption.iv[0], 16); + mSegDecrypter->SetOption("decryption IV", &mCurrentEncryption.iv[0], 16); } mSegDecrypter->flush(); - } else if (mCurSeg->encryption.method == SegmentEncryption::AES_PRIVATE) { + + if (mDRMMagicKey.empty() && mSegKeySource){ + mDRMMagicKey = mSegKeySource->GetOption("drmMagicKey"); + } + } else if (mCurrentEncryption.method == SegmentEncryption::AES_PRIVATE) { memset(mKey, 0, 16); - long length = mCurSeg->encryption.keyUrl.length(); + long length = mCurrentEncryption.keyUrl.length(); if (length > 16) { length = 16; } - memcpy(mKey, mCurSeg->encryption.keyUrl.c_str(), length); + memcpy(mKey, mCurrentEncryption.keyUrl.c_str(), length); if (mSegDecrypter == nullptr) { mSegDecrypter = unique_ptr( - SegDecryptorFactory::create(mCurSeg->encryption.method, + SegDecryptorFactory::create(mCurrentEncryption.method, Decrypter_read_callback, this)); } - mCurSeg->encryption.iv.clear(); - mCurSeg->encryption.iv.resize(16); + mCurrentEncryption.iv.clear(); + mCurrentEncryption.iv.resize(16); mSegDecrypter->SetOption("decryption key", mKey, 16); - mSegDecrypter->SetOption("decryption IV", &mCurSeg->encryption.iv[0], 16); + mSegDecrypter->SetOption("decryption IV", &mCurrentEncryption.iv[0], 16); mSegDecrypter->flush(); + if (mDRMMagicKey.empty() && mSegKeySource){ + mDRMMagicKey = mSegDecrypter->GetOption("drmMagicKey"); + } } return 0; @@ -681,11 +734,14 @@ namespace Cicada { assert(mSampeAesDecrypter != nullptr); if (mSampeAesDecrypter) { - mSampeAesDecrypter->SetOption("decryption IV", &mCurSeg->encryption.iv[0], 16); + mSampeAesDecrypter->SetOption("decryption IV", &mCurrentEncryption.iv[0], 16); // mSampeAesDecrypter->SetOption("decryption KEYFORMAT", (uint8_t *) mCurSeg->encryption.keyFormat.c_str(), // (int) mCurSeg->encryption.keyFormat.length()); } } + if (mDRMMagicKey.empty() && mSegKeySource) { + mDRMMagicKey = mSegKeySource->GetOption("drmMagicKey"); + } return 0; } @@ -826,16 +882,17 @@ namespace Cicada { int HLSStream::updateDecrypter() { int ret = 0; - mProtectedBuffer = mCurSeg->encryption.method != SegmentEncryption::NONE; + mProtectedBuffer = mCurrentEncryption.method != SegmentEncryption::NONE; - if (mCurSeg->encryption.method == SegmentEncryption::AES_128 || - mCurSeg->encryption.method == SegmentEncryption::AES_PRIVATE) { + if (mCurrentEncryption.method == SegmentEncryption::AES_128 || + mCurrentEncryption.method == SegmentEncryption::AES_PRIVATE) { ret = updateSegDecrypter(); if (ret < 0) { return ret; } - } else if (mCurSeg->encryption.method == SegmentEncryption::AES_SAMPLE) { + } else if (mCurrentEncryption.method == SegmentEncryption::AES_SAMPLE + && mCurrentEncryption.keyFormat.empty()) { ret = updateSampleAesDecrypter(); if (ret < 0) { @@ -861,15 +918,19 @@ namespace Cicada { do { mCurSeg = seg; string uri = Helper::combinePaths(mPTracker->getBaseUri(), - seg->mUri); + seg->getDownloadUrl()); ret = tryOpenSegment(uri, seg->rangeStart, seg->rangeEnd); - if (isHttpError(ret)) { + if (isHttpError(ret) || isLocalFileError(ret)) { resetSource(); seg = mPTracker->getNextSegment(); if (seg) { - af_msleep(20); + if (seg->mSegType == SEG_LHLS) { + af_msleep(5); + } else { + af_msleep(20); + } continue; } else if (mPTracker->isLive()) { return -EAGAIN; @@ -878,7 +939,7 @@ namespace Cicada { break; } } - } while (isHttpError(ret)); + } while (isHttpError(ret) || isLocalFileError(ret)); if (ret < 0) { mDataSourceError = ret; @@ -891,7 +952,7 @@ namespace Cicada { return ret; } - AF_LOGD("stream(%p) read seg %s seqno is %llu\n", this, seg->mUri.c_str(), + AF_LOGD("stream(%p) read seg %s seqno is %llu\n", this, seg->getDownloadUrl().c_str(), seg->getSequenceNumber()); ret = updateDecrypter(); @@ -936,7 +997,7 @@ namespace Cicada { return -EAGAIN; } - if (ret == network_errno_http_range) { + if (ret == gen_framework_errno(error_class_network, network_errno_http_range)) { ret = 0; } @@ -982,7 +1043,13 @@ namespace Cicada { } if (ret == -EAGAIN && mPTracker->getDuration() == 0) { - ret = updateSegment(); + + MoveToNextPart move_ret = moveToNextPartialSegment(); + if (move_ret == MoveToNextPart::segmentEnd) { + ret = updateSegment(); + } else { + return -EAGAIN; + } if (ret < 0) { return ret; @@ -994,8 +1061,9 @@ namespace Cicada { if (packet != nullptr) { // AF_LOGD("read a frame \n"); - if (mProtectedBuffer) { + if (mProtectedBuffer && !mDRMMagicKey.empty()) { packet->setProtected(); + packet->setMagicKey(mDRMMagicKey); } if (mPTracker->getStreamType() != STREAM_TYPE_MIXED) { packet->getInfo().streamIndex = 0; @@ -1123,6 +1191,9 @@ namespace Cicada { meta->description = strdup(mPTracker->getDescriptionInfo().c_str()); } + meta->keyUrl = mCurrentEncryption.keyUrl.empty() ? nullptr : strdup(mCurrentEncryption.keyUrl.c_str()); + meta->keyFormat = mCurrentEncryption.keyFormat.empty() ? nullptr : strdup(mCurrentEncryption.keyFormat.c_str()); + return 0; } @@ -1131,6 +1202,14 @@ namespace Cicada { return mIsOpened; } + int64_t HLSStream::getTargetDuration() + { + if (mPTracker) { + return mPTracker->getTargetDuration(); + } + return INT64_MIN; + } + int HLSStream::start() { // demuxer_msg::StartReq start; @@ -1209,17 +1288,31 @@ namespace Cicada { if (!b_ret) { AF_LOGE("(%d)getSegmentNumberByTime error us is %lld\n", mPTracker->getStreamType(), us); + // us's accuracy is ms, so change duration's accuracy to ms + bool seekOnLast = false; + if (us == (mPTracker->getDuration() / 1000 * 1000)) { + num = mPTracker->getLastSegNum(); + + // reopen will -- it + if (mIsOpened_internal) { + num++; + } + usSought = us; + seekOnLast = true; + } - if (mPTracker->getStreamType() == STREAM_TYPE_SUB) { - mIsEOS = false; - mError = 0; + if (!seekOnLast) { + if (mPTracker->getStreamType() == STREAM_TYPE_SUB) { + mIsEOS = false; + mError = 0; - if (mThreadPtr) { - mThreadPtr->start(); + if (mThreadPtr) { + mThreadPtr->start(); + } + } else { + return -1; } } - - return -1; } AF_LOGD("%s:%d stream (%d) usSeeked is %lld seek num is %d\n", __func__, __LINE__, @@ -1430,11 +1523,22 @@ namespace Cicada { if (mPDemuxer) { return mPDemuxer->GetProperty(-1, key); } + } else if ("keyUrl" == key) { + return mCurrentEncryption.keyUrl; } return ""; } + bool HLSStream::isRealTimeStream() + { + if (mPTracker != nullptr) { + return mPTracker->isRealTimeStream(); + } else { + return false; + } + } + HLSStream::WebVttParser::WebVttParser() = default; HLSStream::WebVttParser::~WebVttParser() @@ -1529,4 +1633,5 @@ namespace Cicada { mMapPTS = INT64_MIN; bFinished = false; } + } diff --git a/framework/demuxer/play_list/HLSStream.h b/framework/demuxer/play_list/HLSStream.h index ac20d6667..90c6c5154 100644 --- a/framework/demuxer/play_list/HLSStream.h +++ b/framework/demuxer/play_list/HLSStream.h @@ -20,6 +20,9 @@ #include namespace Cicada { + + enum MoveToNextPart { tryAgain = -11, segmentEnd = 0, moveSuccess = 1 }; + class HLSStream : public AbstractStream { class WebVttParser { @@ -92,7 +95,11 @@ namespace Cicada { void interrupt(int inter) override; std::string GetProperty(const string &key); + + bool isRealTimeStream(); + + int64_t getTargetDuration(); private: @@ -137,13 +144,15 @@ namespace Cicada { int readSegment(const uint8_t *buffer, int size); + MoveToNextPart moveToNextPartialSegment(); + int upDateInitSection(); int64_t seekSegment(off_t offset, int whence); int updateSegment(); - bool updateIV() const; + bool updateIV(); enum OpenType { SegNum, SegPosition @@ -215,7 +224,11 @@ namespace Cicada { int OpenedStreamIndex = 0; bool mProtectedBuffer{false}; - int64_t mLiveStartIndex{-3};//segment index to start live streams at (negative values are from the end) + //segment index to start live streams at (negative values are from the end). for lhls, it is partial segment index + int64_t mLiveStartIndex{-3}; + + std::string mDRMMagicKey{}; + SegmentEncryption mCurrentEncryption{}; }; } diff --git a/framework/demuxer/play_list/HlsParser.cpp b/framework/demuxer/play_list/HlsParser.cpp index 205a061a7..bbda7af08 100644 --- a/framework/demuxer/play_list/HlsParser.cpp +++ b/framework/demuxer/play_list/HlsParser.cpp @@ -3,14 +3,13 @@ // #define LOG_TAG "HlsParser" -#include -#include -#include #include "HlsParser.h" #include "Helper.h" -#include "../../utils/frame_work_log.h" #include "utils/AFMediaType.h" - +#include "utils/frame_work_log.h" +#include +#include +#include #define CLOCK_FREQ INT64_C(1000000) @@ -165,10 +164,12 @@ namespace Cicada { uint64_t discontinuityNum = 0; std::size_t prevbyterangeoffset = 0; const SingleValueTag *ctx_byterange = nullptr; - SegmentEncryption encryption; + std::vector encryptionArray; const ValuesListTag *ctx_extinf = nullptr; std::list::const_iterator it; std::shared_ptr curInitSegment = nullptr; + std::vector segmentParts; + bool clearKeyArray = true; for (it = tagslist.begin(); it != tagslist.end(); ++it) { const Tag *tag = *it; @@ -196,30 +197,35 @@ namespace Cicada { auto pSegment = std::make_shared(sequenceNumber++); pSegment->setSourceUrl(uritag->getValue().value); -// if ((unsigned) rep->getStreamFormat() == StreamFormat::UNKNOWN) -// setFormatFromExtension(rep, uritag->getValue().value); + if (segmentParts.size() > 0) { + pSegment->updateParts(segmentParts); + segmentParts.clear(); + } + + //if ((unsigned) rep->getStreamFormat() == StreamFormat::UNKNOWN) + // setFormatFromExtension(rep, uritag->getValue().value); /* Need to use EXTXTARGETDURATION as default as some can't properly set segment one */ - double duration = rep->targetDuration; + int64_t nzDuration = rep->targetDuration; if (ctx_extinf) { const Attribute *durAttribute = ctx_extinf->getAttributeByName("DURATION"); if (durAttribute) { - duration = durAttribute->floatingPoint(); + double duration = durAttribute->floatingPoint(); + nzDuration = static_cast(CLOCK_FREQ * duration); } ctx_extinf = nullptr; } - const auto nzDuration = static_cast(CLOCK_FREQ * duration); pSegment->duration = nzDuration; pSegment->startTime = static_cast(nzStartTime); nzStartTime += nzDuration; totalduration += nzDuration; -// if (absReferenceTime > VLC_TS_INVALID) { -// segment->utcTime = absReferenceTime; -// absReferenceTime += nzDuration; -// } + //if (absReferenceTime > VLC_TS_INVALID) { + // segment->utcTime = absReferenceTime; + // absReferenceTime += nzDuration; + //} pSegment->init_section = curInitSegment; segmentList->addSegment(pSegment); @@ -237,16 +243,18 @@ namespace Cicada { pSegment->discontinuityNum = discontinuityNum; - if (encryption.method != SegmentEncryption::NONE) { - pSegment->setEncryption(encryption); + if(!encryptionArray.empty()) { + pSegment->setEncryption(encryptionArray); + clearKeyArray = true; } + } break; + + case SingleValueTag::EXTXTARGETDURATION: { + int64_t duration = static_cast(tag)->getValue().decimal(); + rep->targetDuration = static_cast(CLOCK_FREQ * duration); } break; - case SingleValueTag::EXTXTARGETDURATION: - rep->targetDuration = static_cast(tag)->getValue().decimal(); - break; - case SingleValueTag::EXTXPLAYLISTTYPE: rep->b_live = (static_cast(tag)->getValue().value != "VOD"); break; @@ -262,6 +270,13 @@ namespace Cicada { break; case AttributesTag::EXTXKEY: { + + if(clearKeyArray) { + encryptionArray.clear(); + clearKeyArray = false; + } + + SegmentEncryption encryption{}; const auto *keytag = static_cast(tag); if (keytag->getAttributeByName("METHOD") && @@ -300,15 +315,20 @@ namespace Cicada { encryption.ivStatic = true; } + string keyFormat; if (keytag->getAttributeByName("KEYFORMAT")) { - encryption.keyFormat = keytag->getAttributeByName("KEYFORMAT")->quotedString(); + keyFormat = keytag->getAttributeByName("KEYFORMAT")->quotedString(); } + encryption.keyFormat = keyFormat; + } else { /* unsupported or invalid */ encryption.method = SegmentEncryption::NONE; encryption.keyUrl = ""; encryption.iv.clear(); } + + encryptionArray.push_back(encryption); } break; @@ -334,6 +354,44 @@ namespace Cicada { } break; + case AttributesTag::EXTXPART: { + const auto *keytag = static_cast(tag); + SegmentPart part; + part.sequence = segmentParts.size(); + + if (keytag->getAttributeByName("DURATION")) { + double duration = keytag->getAttributeByName("DURATION")->floatingPoint(); + const auto nzDuration = static_cast(CLOCK_FREQ * duration); + part.duration = nzDuration; + } + + if (part.duration > rep->partTargetDuration) { + rep->partTargetDuration = part.duration; + } + + if (keytag->getAttributeByName("URI")) { + part.uri = keytag->getAttributeByName("URI")->quotedString(); + } + + if (keytag->getAttributeByName("INDEPENDENT")) { + part.independent = (keytag->getAttributeByName("INDEPENDENT")->value == "YES"); + } + + segmentParts.push_back(part); + break; + } + + case AttributesTag::EXTXPARTINF: { + const AttributesTag *keytag = static_cast(tag); + const Attribute *partTargetAttr; + if (keytag && (partTargetAttr = keytag->getAttributeByName("PART-TARGET"))) { + double duration = partTargetAttr->floatingPoint(); + const auto nzDuration = static_cast(CLOCK_FREQ * duration); + rep->partTargetDuration = nzDuration; + } + break; + } + case Tag::EXTXDISCONTINUITY: discontinuityNum++; break; @@ -346,6 +404,46 @@ namespace Cicada { break; } } + + if (segmentParts.size() > 0) { + auto pSegment = std::make_shared(sequenceNumber); + pSegment->setSourceUrl(""); + int64_t duration = 0; + + for (auto part : segmentParts) { + duration += part.duration; + } + + pSegment->duration = duration; + pSegment->startTime = static_cast(nzStartTime); + pSegment->updateParts(segmentParts); + totalduration += duration; + + if (ctx_byterange) { + std::pair range = ctx_byterange->getValue().getByteRange(); + + if (range.first == 0) { + range.first = prevbyterangeoffset; + } + + prevbyterangeoffset = range.first + range.second; + pSegment->setByteRange(range.first, prevbyterangeoffset - 1); + ctx_byterange = nullptr; + } + + if(!encryptionArray.empty()) { + pSegment->setEncryption(encryptionArray); + clearKeyArray = true; + } + + pSegment->init_section = curInitSegment; + pSegment->discontinuityNum = discontinuityNum; + + segmentList->addSegment(pSegment); + ctx_byterange = nullptr; + + segmentParts.clear(); + } if (rep->b_live) { rep->getPlaylist()->setDuration(0); @@ -574,7 +672,7 @@ namespace Cicada { while (!stream->isEOF()) { stream->get_line(mBuffer, MAX_LINE_SIZE); - + AF_LOGD("HLS: %s", mBuffer); if (*mBuffer == '#') { if (!strncmp(mBuffer, "#EXT", 4)) { //tag std::string key; diff --git a/framework/demuxer/play_list/HlsTags.cpp b/framework/demuxer/play_list/HlsTags.cpp index 23db475b7..ac1eb239c 100644 --- a/framework/demuxer/play_list/HlsTags.cpp +++ b/framework/demuxer/play_list/HlsTags.cpp @@ -300,6 +300,9 @@ namespace Cicada { {"EXT-X-STREAM-INF", AttributesTag::EXTXSTREAMINF}, {"EXTINF", ValuesListTag::EXTINF}, {"", SingleValueTag::URI}, + {"EXT-X-PART", AttributesTag::EXTXPART}, + {"EXT-X-PART-INF", AttributesTag::EXTXPARTINF}, + // TODO: add other lhls tag {NULL, 0}, }; @@ -331,6 +334,8 @@ namespace Cicada { case AttributesTag::EXTXMAP: case AttributesTag::EXTXMEDIA: case AttributesTag::EXTXSTREAMINF: + case AttributesTag::EXTXPART: + case AttributesTag::EXTXPARTINF: return new (std::nothrow) AttributesTag(exttagmapping[i].i, value); } } diff --git a/framework/demuxer/play_list/HlsTags.h b/framework/demuxer/play_list/HlsTags.h index 87b07dee7..93dca4e06 100644 --- a/framework/demuxer/play_list/HlsTags.h +++ b/framework/demuxer/play_list/HlsTags.h @@ -82,6 +82,8 @@ namespace Cicada{ EXTXMAP, EXTXMEDIA, EXTXSTREAMINF, + EXTXPART, + EXTXPARTINF, }; AttributesTag(int, const std::string &); diff --git a/framework/demuxer/play_list/PlaylistManager.h b/framework/demuxer/play_list/PlaylistManager.h index 0c53abe5c..ad7a2a36b 100644 --- a/framework/demuxer/play_list/PlaylistManager.h +++ b/framework/demuxer/play_list/PlaylistManager.h @@ -34,7 +34,7 @@ namespace Cicada{ virtual void CloseStream(int id) = 0; - virtual int seek(int64_t us, int flags, int index) = 0; + virtual int64_t seek(int64_t us, int flags, int index) = 0; virtual int SwitchStreamAligned(int from, int to) = 0; @@ -56,18 +56,22 @@ namespace Cicada{ mSourceConfig = config; } - virtual void setBitStreamFormat(bool vMergeHeader, bool aMergeHeader) + virtual void setBitStreamFormat(header_type vMergeHeader, header_type aMergeHeader) { mMergeVideoHeader = vMergeHeader; mMergerAudioHeader = aMergeHeader; } + + virtual bool isRealTimeStream(int index) = 0; + + virtual int64_t getTargetDuration() = 0; protected: playList *mPList = nullptr; IDataSource *mExtDataSource = nullptr; IDataSource::SourceConfig mSourceConfig{}; - bool mMergeVideoHeader = false; - bool mMergerAudioHeader = false; + header_type mMergeVideoHeader = header_type::header_type_no_touch; + header_type mMergerAudioHeader = header_type::header_type_no_touch; }; } diff --git a/framework/demuxer/play_list/Representation.h b/framework/demuxer/play_list/Representation.h index ce0f73359..fe39e22f2 100644 --- a/framework/demuxer/play_list/Representation.h +++ b/framework/demuxer/play_list/Representation.h @@ -62,6 +62,7 @@ namespace Cicada{ public: // TODO use set and get time_t targetDuration = 0; + time_t partTargetDuration = 0; bool b_live = false; int mPlayListType{0}; Stream_type mStreamType = STREAM_TYPE_MIXED; diff --git a/framework/demuxer/play_list/SegmentList.cpp b/framework/demuxer/play_list/SegmentList.cpp index 464fe4ced..9f6aebffc 100644 --- a/framework/demuxer/play_list/SegmentList.cpp +++ b/framework/demuxer/play_list/SegmentList.cpp @@ -3,8 +3,8 @@ // #define LOG_TAG "SegmentList" -#include #include "SegmentList.h" +#include namespace Cicada { @@ -61,8 +61,9 @@ namespace Cicada { if (seg->startTime == UINT64_MAX) { seg->startTime = mNextStartTime; } - - mNextStartTime = seg->startTime + seg->duration; + if (!seg->mUri.empty()) { + mNextStartTime = seg->startTime + seg->duration; + } mLastSeqNum = seg->sequence; segments.push_back(seg); } @@ -87,7 +88,7 @@ namespace Cicada { for (auto &i : segments) { duration += i->duration; - if ((duration / 1000 * 1000) > time) { + if (duration > time) { num = i->sequence; time = duration - i->duration; return true; @@ -105,8 +106,14 @@ namespace Cicada { int size = sList.size(); for (auto i = sList.begin(); i != sList.end();) { - if ((*i)->sequence <= mLastSeqNum) { + if ((*i)->sequence < mLastSeqNum) { (*i) = nullptr; + } else if ((*i)->sequence == mLastSeqNum) { + if ((*i)->mSegType == SEG_LHLS) { + updateLastLHLSSegment((*i)); + } else { + (*i) = nullptr; + } } else { AF_LOGI("xxxxxx add a new seg %llu", (*i)->sequence); (*i)->startTime = UINT64_MAX; @@ -120,7 +127,9 @@ namespace Cicada { segments.pop_front(); } - mFirstSeqNum = segments.front()->sequence; + if (!segments.empty()) { + mFirstSeqNum = segments.front()->sequence; + } delete pSList; return 0; } @@ -133,4 +142,45 @@ namespace Cicada { { return static_cast(mLastSeqNum); } -} + + void SegmentList::updateLastLHLSSegment(const std::shared_ptr &seg) + { + std::lock_guard uMutex(segmetsMuxtex); + + if (segments.size() > 0) { + std::shared_ptr old_seg = segments.back(); + if (old_seg != nullptr && old_seg->sequence == mLastSeqNum && old_seg->mUri.empty()) { + if (old_seg != nullptr && seg != nullptr) { + old_seg->updateParts(seg->getSegmentParts()); + if (!seg->mUri.empty()) { + old_seg->duration = seg->duration; + mNextStartTime += old_seg->duration; + old_seg->setSourceUrl(seg->mUri); + } + } + } + } + } + + bool SegmentList::hasLHLSSegments() + { + bool ret = false; + + for (auto seg : segments) { + if (seg->mSegType == SEG_LHLS) { + ret = true; + break; + } + } + + return ret; + } + + int64_t SegmentList::getTargetDuration() + { + if (mRep == nullptr) { + return INT64_MIN; + } + return mRep->targetDuration; + } +}// namespace Cicada diff --git a/framework/demuxer/play_list/SegmentList.h b/framework/demuxer/play_list/SegmentList.h index bb39e2455..3763acbf2 100644 --- a/framework/demuxer/play_list/SegmentList.h +++ b/framework/demuxer/play_list/SegmentList.h @@ -41,8 +41,14 @@ namespace Cicada{ uint64_t getFirstSeqNum() const; uint64_t getLastSeqNum() const; + + bool hasLHLSSegments(); + + int64_t getTargetDuration(); private: + void updateLastLHLSSegment(const std::shared_ptr &seg); + std::list> segments; std::mutex segmetsMuxtex; diff --git a/framework/demuxer/play_list/SegmentPart.h b/framework/demuxer/play_list/SegmentPart.h new file mode 100644 index 000000000..2f6d52e1d --- /dev/null +++ b/framework/demuxer/play_list/SegmentPart.h @@ -0,0 +1,33 @@ +// +// SegmentPart.h +// CicadaPlayerSDK +// +// Created by weixin on 2020/9/21. +// + +#ifndef SegmentPart_h +#define SegmentPart_h + +#include +using namespace std; + +namespace Cicada +{ + typedef struct SegmentPart + { + int64_t duration; + string uri; + bool independent; + uint64_t sequence; + + SegmentPart() + { + duration = 0; + uri = ""; + independent = false; + sequence = 0; + } + } SegmentPart; +} + +#endif /* SegmentPart_h */ diff --git a/framework/demuxer/play_list/SegmentTracker.cpp b/framework/demuxer/play_list/SegmentTracker.cpp index b11ad25de..d79f90e41 100644 --- a/framework/demuxer/play_list/SegmentTracker.cpp +++ b/framework/demuxer/play_list/SegmentTracker.cpp @@ -4,19 +4,18 @@ #define LOG_TAG "SegmentTracker" -#include -#include #include "SegmentTracker.h" +#include "Helper.h" #include "HlsParser.h" #include "playList_demuxer.h" -#include "Helper.h" -#include "../../utils/timer.h" - +#include "utils/timer.h" +#include #include - #include +#include +#include -#define IS_LIVE (mPPlayList->getDuration() == 0) +#define IS_LIVE (mRep && mRep->b_live) namespace Cicada { @@ -80,7 +79,7 @@ namespace Cicada { } else { mCurSegNum--; } - + return seg; } @@ -96,12 +95,79 @@ namespace Cicada { return count; } + void SegmentTracker::MoveToLiveStartSegment(const int64_t liveStartIndex) + { + SegmentList *segList = mRep->GetSegmentList(); + if (segList == nullptr) { + AF_LOGW("SegmentTracker::MoveToLiveStartSegment, segmentList is empty"); + return; + } + if (segList->hasLHLSSegments()) { + // lhls stream, liveStartIndex is partial segment index + auto segments = segList->getSegments(); + if (liveStartIndex >= 0) { + int offset = liveStartIndex; + bool isFindPart = false; + for (auto iter = segments.begin(); iter != segments.end(); iter++) { + const vector &segmentParts = (*iter)->getSegmentParts(); + if (offset >= segmentParts.size()) { + offset -= segmentParts.size(); + } else { + (*iter)->moveToNearestIndependentPart(offset); + isFindPart = true; + setCurSegNum((*iter)->getSequenceNumber()); + break; + } + } + if (!isFindPart) { + // use last independent part + auto iter = segments.back(); + iter->moveToNearestIndependentPart(iter->getSegmentParts().size() - 1); + setCurSegNum(iter->getSequenceNumber()); + } + } else { + int offset = -liveStartIndex - 1; + bool isFindPart = false; + for (auto iter = segments.rbegin(); iter != segments.rend(); iter++) { + const vector &segmentParts = (*iter)->getSegmentParts(); + if (offset >= segmentParts.size()) { + offset -= segmentParts.size(); + } else { + (*iter)->moveToNearestIndependentPart(segmentParts.size() - 1 - offset); + isFindPart = true; + setCurSegNum((*iter)->getSequenceNumber()); + break; + } + } + if (!isFindPart) { + // use first independent part + auto iter = segments.front(); + iter->moveToNearestIndependentPart(0); + setCurSegNum(iter->getSequenceNumber()); + } + } + } else { + // hls stream, liveStartIndex is segment index + uint64_t curNum; + if (liveStartIndex >= 0) { + curNum = std::min(getFirstSegNum() + liveStartIndex, getLastSegNum()); + } else { + curNum = std::max(getLastSegNum() + liveStartIndex + 1, getFirstSegNum()); + } + setCurSegNum(curNum); + } + } + int SegmentTracker::loadPlayList() { int ret; string uri; string *pUri; + if (!mRep) { + return -EINVAL; + } + if (mLocation.empty()) { std::unique_lock locker(mMutex); uri = Helper::combinePaths(mRep->getPlaylist()->getPlaylistUrl(), @@ -150,7 +216,8 @@ namespace Cicada { Representation *rep = (*(*(*pPlayList->GetPeriods().begin())->GetAdaptSets().begin())->getRepresentations().begin()); SegmentList *sList = rep->GetSegmentList(); SegmentList *pList = mRep->GetSegmentList(); - mTargetDuration = rep->targetDuration * 1000000; + mTargetDuration = rep->targetDuration; + mPartTargetDuration = rep->partTargetDuration; // sList->print(); //live stream should always keeps the new lists. @@ -162,6 +229,9 @@ namespace Cicada { rep->SetSegmentList(nullptr); + // update is live + mRep->b_live = rep->b_live; + if (pPlayList->getDuration() > 0) { mPDataSource->Close(); delete mPDataSource; @@ -211,6 +281,10 @@ namespace Cicada { playListOwnedByMe = false; } + if (mRep != nullptr && mRep->GetSegmentList() != nullptr) { + mRealtime = mRep->GetSegmentList()->hasLHLSSegments(); + } + if (IS_LIVE) { mThread->start(); } @@ -286,7 +360,7 @@ namespace Cicada { return mPPlayList->getDuration(); } - return 0; + return INT64_MIN; } int SegmentTracker::reLoadPlayList() @@ -296,7 +370,15 @@ namespace Cicada { int64_t time = af_gettime_relative(); // AF_LOGD("mTargetDuration is %lld", (int64_t)mTargetDuration); - if (time - mLastLoadTime > (mTargetDuration / 2)) { + int64_t reloadInterval = 0; + if (mPartTargetDuration > 0) { + // lhls reload interval is 2 times part target duration + reloadInterval = mPartTargetDuration * 2; + } else { + // hls reload interval is half target dutaion + reloadInterval = mTargetDuration / 2; + } + if (time - mLastLoadTime > reloadInterval) { std::unique_lock locker(mSegMutex); mNeedUpdate = true; mSegCondition.notify_all(); @@ -328,6 +410,11 @@ namespace Cicada { if (!mStopLoading) { mPlayListStatus = loadPlayList(); + if (!mRealtime && mRep != nullptr && mRep->GetSegmentList() != nullptr) + { + mRealtime = mRep->GetSegmentList()->hasLHLSSegments(); + } + mNeedUpdate = false; } } @@ -391,4 +478,12 @@ namespace Cicada { mSeeked = true; } + int64_t SegmentTracker::getTargetDuration() + { + if (mRep->GetSegmentList() != nullptr) { + return mRep->GetSegmentList()->getTargetDuration(); + } else { + return mRep->targetDuration; + } + } } diff --git a/framework/demuxer/play_list/SegmentTracker.h b/framework/demuxer/play_list/SegmentTracker.h index d17a839df..163d9e6ed 100644 --- a/framework/demuxer/play_list/SegmentTracker.h +++ b/framework/demuxer/play_list/SegmentTracker.h @@ -84,6 +84,12 @@ namespace Cicada { { return mSeeked.load(); } + + bool isRealTimeStream() { return mRealtime; } + + void MoveToLiveStartSegment(const int64_t liveStartIndex); + + int64_t getTargetDuration(); private: int loadPlayList(); @@ -98,6 +104,7 @@ namespace Cicada { std::string mLocation = ""; std::atomic mTargetDuration{0}; + std::atomic mPartTargetDuration{0}; int64_t mLastLoadTime = 0; bool playListOwnedByMe = false; @@ -120,6 +127,8 @@ namespace Cicada { std::atomic_int mPlayListStatus{0}; std::atomic_bool mSeeked{false}; + + bool mRealtime = false; }; } diff --git a/framework/demuxer/play_list/playList_demuxer.cpp b/framework/demuxer/play_list/playList_demuxer.cpp index f32a3784b..be177c657 100644 --- a/framework/demuxer/play_list/playList_demuxer.cpp +++ b/framework/demuxer/play_list/playList_demuxer.cpp @@ -61,7 +61,7 @@ namespace Cicada { playlistManager->setOptions(mOpts); playlistManager->setExtDataSource(mProxySource); playlistManager->setDataSourceConfig(sourceConfig); - playlistManager->setBitStreamFormat(mMergeVideoHeader, mMergerAudioHeader); + playlistManager->setBitStreamFormat(mMergeVideoHeader, mMergeAudioHeader); mPPlaylistManager = playlistManager; ret = playlistManager->init(); @@ -103,7 +103,7 @@ namespace Cicada { } } - int playList_demuxer::Seek(int64_t us, int flags, int index) + int64_t playList_demuxer::Seek(int64_t us, int flags, int index) { if (mPPlaylistManager) { return mPPlaylistManager->seek(us, flags, index); @@ -195,4 +195,21 @@ namespace Cicada { mPPlaylistManager->interrupt(inter); } } + + bool playList_demuxer::isRealTimeStream(int index) + { + if (mPPlaylistManager) { + return mPPlaylistManager->isRealTimeStream(index); + } + + return false; + } + + int64_t playList_demuxer::getMaxGopTimeUs() + { + if (mPPlaylistManager) { + return mPPlaylistManager->getTargetDuration(); + } + return INT64_MIN; + } } diff --git a/framework/demuxer/play_list/playList_demuxer.h b/framework/demuxer/play_list/playList_demuxer.h index f7570e689..a56107879 100644 --- a/framework/demuxer/play_list/playList_demuxer.h +++ b/framework/demuxer/play_list/playList_demuxer.h @@ -48,7 +48,7 @@ namespace Cicada{ void Stop() override; - int Seek(int64_t us, int flags, int index) override; + int64_t Seek(int64_t us, int flags, int index) override; int GetNbStreams() const override; @@ -68,6 +68,8 @@ namespace Cicada{ int SwitchStreamAligned(int from, int to) override; + int64_t getMaxGopTimeUs() override; + void flush() override { // TODO: @@ -79,6 +81,8 @@ namespace Cicada{ } const std::string GetProperty(int index, const string &key) const override; + + bool isRealTimeStream(int index) override; private: explicit playList_demuxer(int dummy) : IDemuxer("") diff --git a/framework/demuxer/play_list/segment.cpp b/framework/demuxer/play_list/segment.cpp index 0e2a4c018..af7df24ef 100644 --- a/framework/demuxer/play_list/segment.cpp +++ b/framework/demuxer/play_list/segment.cpp @@ -2,7 +2,8 @@ // Created by moqi on 2018/4/25. // #include "segment.h" -#include "../../utils/frame_work_log.h" +#include "utils/frame_work_log.h" +#include namespace Cicada { const int segment::SEQUENCE_INVALID = 0; @@ -30,18 +31,20 @@ namespace Cicada { void segment::setSourceUrl(const std::string &value) { + std::lock_guard lck(mMutex); mUri = value; } void segment::print() { AF_LOGD("segment %llu," - " %s duration %lld startTime is %llu\n", sequence, mUri.c_str(), duration, startTime); + " %s duration %lld startTime is %llu\n", + sequence, mUri.c_str(), duration, startTime); } - void segment::setEncryption(SegmentEncryption enc) + void segment::setEncryption(const std::vector &enc) { - encryption = enc; + encryptions = enc; } void segment::setByteRange(int64_t start, int64_t end) @@ -49,4 +52,123 @@ namespace Cicada { rangeStart = start; rangeEnd = end; } -} \ No newline at end of file + + std::string segment::getDownloadUrl() + { + std::lock_guard lck(mMutex); + if (mSegType == SEG_NORMAL) { + return mUri; + } else if (mSegType == SEG_LHLS) { + std::lock_guard lck(mMutex); + return mDownloadUri; + } else { + assert(0); + return ""; + } + } + + void segment::updateParts(const std::vector &parts) + { + std::lock_guard lck(mMutex); + mParts = parts; + if (mParts.size() > 0) { + mSegType = SEG_LHLS; + if (mPartsNextIndex == 0) { + moveToNextPart(); + } + } else { + mSegType = SEG_NORMAL; + } + } + + const vector &segment::getSegmentParts() + { + std::lock_guard lck(mMutex); + return mParts; + } + + void segment::moveToNextPart() + { + lock_guard lck(mMutex); + std::string uri = ""; + + if (mUri.empty()) { + // dose not have complete segment, use part + if (mParts.size() > 0 && mPartsNextIndex >= 0 && mPartsNextIndex < mParts.size()) { + uri = mParts[mPartsNextIndex].uri; + ++mPartsNextIndex; + } else { + AF_LOGD("Not have enough segment parts [%d] [%d]", mPartsNextIndex, (int) mParts.size()); + } + } else { + // has complete segment + if (mPartsNextIndex > 0) { + // has played part, use next part + if (mParts.size() > 0 && mPartsNextIndex >= 0 && mPartsNextIndex < mParts.size()) { + uri = mParts[mPartsNextIndex].uri; + ++mPartsNextIndex; + } else { + AF_LOGD("Not have enough segment parts [%d] [%d]", mPartsNextIndex, (int) mParts.size()); + } + } else { + // has not been played, use complete segment + uri = mUri; + mPartsNextIndex = -1; + } + } + + mDownloadUri = uri; + } + + void segment::moveToPart(int partIndex) + { + lock_guard lck(mMutex); + int fixedPartIndex = partIndex; + if (fixedPartIndex < 0) { + fixedPartIndex = 0; + } + if (fixedPartIndex >= mParts.size()) { + fixedPartIndex = mParts.size() - 1; + } + std::string uri = ""; + if (!mUri.empty() && fixedPartIndex == 0) { + uri = mUri; + mPartsNextIndex = -1; + } else { + uri = mParts[fixedPartIndex].uri; + mPartsNextIndex = fixedPartIndex + 1; + } + + mDownloadUri = uri; + } + + void segment::moveToNearestIndependentPart(int partIndex) + { + lock_guard lck(mMutex); + int fixedIndex = partIndex; + if (fixedIndex < 0) { + fixedIndex = 0; + } + if (fixedIndex >= mParts.size()) { + fixedIndex = mParts.size() - 1; + } + bool isFind = false; + for (int i = fixedIndex; i >= 0; i--) { + if (mParts.at(i).independent) { + moveToPart(i); + isFind = true; + } + } + if (!isFind) { + moveToPart(0); + } + } + + bool segment::isDownloadComplete(bool &bHasUnusedParts) + { + lock_guard lck(mMutex); + bool isComplete = !mUri.empty(); + bHasUnusedParts = (mPartsNextIndex < mParts.size()); + return isComplete; + } +}// namespace Cicada diff --git a/framework/demuxer/play_list/segment.h b/framework/demuxer/play_list/segment.h index 5b252c9a0..eea7bf318 100644 --- a/framework/demuxer/play_list/segment.h +++ b/framework/demuxer/play_list/segment.h @@ -5,19 +5,27 @@ #ifndef FRAMEWORK_SEGMENT_H #define FRAMEWORK_SEGMENT_H - +#include "SegmentPart.h" +#include "segment_decrypt/SegmentEncryption.h" #include -#include #include -#include "demuxer/play_list/segment_decrypt/SegmentEncryption.h" +#include +#include //using namespace std; namespace Cicada{ + + enum SegTypes + { + SEG_NORMAL = 0, + SEG_LHLS, + }; + class segment { public: explicit segment(uint64_t seq); - ~segment(); + virtual ~segment(); uint64_t getSequenceNumber(); @@ -27,10 +35,19 @@ namespace Cicada{ void print(); - void setEncryption(SegmentEncryption enc); + void setEncryption(const std::vector &enc); void setByteRange(int64_t start, int64_t end); + // Low-Latency HLS + std::string getDownloadUrl(); + void updateParts(const std::vector &parts); + const std::vector &getSegmentParts(); + void moveToNextPart(); + void moveToPart(int partIndex); + void moveToNearestIndependentPart(int partIndex); + bool isDownloadComplete(bool &bHasUnusedParts); + public: std::string mUri = ""; uint64_t startTime = 0; @@ -39,12 +56,18 @@ namespace Cicada{ uint64_t discontinuityNum = 0; static const int SEQUENCE_INVALID; static const int SEQUENCE_FIRST; - SegmentEncryption encryption; + std::vector encryptions; int64_t rangeStart {INT64_MIN}; int64_t rangeEnd {INT64_MIN}; std::shared_ptr init_section{nullptr}; + // Low-Latency HLS + SegTypes mSegType = SEG_NORMAL; + std::vector mParts; + int mPartsNextIndex = 0; + std::recursive_mutex mMutex; + std::string mDownloadUri = ""; }; } diff --git a/framework/demuxer/play_list/segment_decrypt/ISegDecrypter.h b/framework/demuxer/play_list/segment_decrypt/ISegDecrypter.h index f5ece43d6..75290148f 100644 --- a/framework/demuxer/play_list/segment_decrypt/ISegDecrypter.h +++ b/framework/demuxer/play_list/segment_decrypt/ISegDecrypter.h @@ -7,6 +7,7 @@ #include +#include class ISegDecrypter { public: @@ -25,6 +26,10 @@ class ISegDecrypter { virtual void SetOption(const char *key, uint8_t *buffer, int size) = 0; + virtual std::string GetOption(const std::string & key){ + return ""; + }; + virtual void flush() = 0; protected: diff --git a/framework/demuxer/sample_decrypt/HLSSampleAesDecrypter.cpp b/framework/demuxer/sample_decrypt/HLSSampleAesDecrypter.cpp index c2d7a46ad..b298e97ea 100644 --- a/framework/demuxer/sample_decrypt/HLSSampleAesDecrypter.cpp +++ b/framework/demuxer/sample_decrypt/HLSSampleAesDecrypter.cpp @@ -125,12 +125,8 @@ static uint8_t *find_naulunit(uint8_t *buffer, int size, int *nal_size, int &sta return nullptr; } - if (find_end_pos == -1) { - *nal_size = size - find_start_pos; - return buffer + find_start_pos; - } - - return nullptr; + *nal_size = size - find_start_pos; + return buffer + find_start_pos; } static int remove_nalunit_prevention(uint8_t *nal_unit, int nal_size, uint8_t *dst, int &dst_pos) diff --git a/framework/drm/CMakeLists.txt b/framework/drm/CMakeLists.txt new file mode 100644 index 000000000..a6a2e732b --- /dev/null +++ b/framework/drm/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.6) +project(framework_drm) +set(CMAKE_CXX_STANDARD 11) +set(SOURCE_FILES + DrmInfo.cpp + DrmManager.cpp + DrmHandler.cpp + DrmHandlerPrototype.cpp + ) + +if (ANDROID) + list(APPEND SOURCE_FILES + WideVineDrmHandler.cpp + ) +endif () + + +include_directories( + ${COMMON_INC_DIR}) +add_library(framework_drm ${TARGET_LIBRARY_TYPE} ${SOURCE_FILES}) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS") + +if (HAVE_COVERAGE_CONFIG) + target_link_libraries(framework_drm PUBLIC coverage_config) +endif () \ No newline at end of file diff --git a/framework/drm/DrmHandler.cpp b/framework/drm/DrmHandler.cpp new file mode 100644 index 000000000..14034e668 --- /dev/null +++ b/framework/drm/DrmHandler.cpp @@ -0,0 +1,11 @@ +// +// Created by SuperMan on 11/27/20. +// + +#include "DrmHandler.h" + +using namespace Cicada; + +DrmHandler::DrmHandler(const DrmInfo &drmInfo) { + this->drmInfo = drmInfo; +} diff --git a/framework/drm/DrmHandler.h b/framework/drm/DrmHandler.h new file mode 100644 index 000000000..d935da34c --- /dev/null +++ b/framework/drm/DrmHandler.h @@ -0,0 +1,80 @@ +// +// Created by SuperMan on 11/27/20. +// + +#ifndef SOURCE_DRMHANDLER_H +#define SOURCE_DRMHANDLER_H + +#include +#include +#include +#include +#include +#include "DrmInfo.h" + +namespace Cicada { + + class DrmRequestParam { + public: + DrmRequestParam() = default; + + ~DrmRequestParam() = default; + + std::string mDrmType{}; + void *mParam{nullptr}; + }; + + class DrmResponseData { + public: + DrmResponseData(char *data, int size) { + if (data != nullptr && size > 0) { + mData = static_cast(malloc(size)); + memcpy(mData, data, size); + mSize = size; + } + }; + + ~DrmResponseData() { + if (mData != nullptr) { + free(mData); + } + }; + + const char* getData(int* size){ + *size = mSize; + return mData; + } + + private: + int mSize{0}; + char *mData{nullptr}; + }; + + class DrmHandler { + + public: + + DrmHandler(const DrmInfo &drmInfo); + + virtual ~DrmHandler() = default; + + void setDrmCallback(const std::function &callback) { + drmCallback = callback; + } + + virtual bool isErrorState() { + return false; + } + + protected: + DrmInfo drmInfo; + + std::function drmCallback{ + nullptr}; + + }; +} + + +#endif //SOURCE_DRMHANDLER_H diff --git a/framework/drm/DrmHandlerPrototype.cpp b/framework/drm/DrmHandlerPrototype.cpp new file mode 100644 index 000000000..c08ce8b34 --- /dev/null +++ b/framework/drm/DrmHandlerPrototype.cpp @@ -0,0 +1,36 @@ + +#include "DrmHandlerPrototype.h" + +using namespace std; +using namespace Cicada; + +DrmHandlerPrototype *DrmHandlerPrototype::drmHandlerQueue[]; +int DrmHandlerPrototype::_nextSlot; + +void DrmHandlerPrototype::addPrototype(DrmHandlerPrototype *se) { + drmHandlerQueue[_nextSlot++] = se; +} + +DrmHandler *DrmHandlerPrototype::create(const DrmInfo &drmInfo) { + + for (int i = 0; i < _nextSlot; ++i) { + if (drmHandlerQueue[i]->is_supported(drmInfo)) { + return drmHandlerQueue[i]->clone(drmInfo); + } + } + + return nullptr; +} + +bool DrmHandlerPrototype::isSupport(const DrmInfo *drmInfo) { + if (drmInfo == nullptr) { + return false; + } + + for (int i = 0; i < _nextSlot; ++i) { + if (drmHandlerQueue[i]->is_supported(*drmInfo)) { + return true; + } + } + return false; +} diff --git a/framework/drm/DrmHandlerPrototype.h b/framework/drm/DrmHandlerPrototype.h new file mode 100644 index 000000000..9e608ada6 --- /dev/null +++ b/framework/drm/DrmHandlerPrototype.h @@ -0,0 +1,32 @@ + +#ifndef CICADA_PLAYER_DRMHANDLERPROTOTYPE_H +#define CICADA_PLAYER_DRMHANDLERPROTOTYPE_H + +#include + +#include +#include "DrmHandler.h" +#include "DrmInfo.h" + +namespace Cicada { + + class CICADA_CPLUS_EXTERN DrmHandlerPrototype { + static DrmHandlerPrototype *drmHandlerQueue[10]; + static int _nextSlot; + public: + virtual ~DrmHandlerPrototype() = default; + + virtual DrmHandler *clone(const DrmInfo &drmInfo) = 0; + + virtual bool is_supported(const DrmInfo &drmInfo) = 0; + + static void addPrototype(DrmHandlerPrototype *se); + + static bool isSupport(const DrmInfo *drmInfo); + + static Cicada::DrmHandler *create(const DrmInfo &drmInfo); + }; +} + + +#endif //CICADA_PLAYER_DRMHANDLERPROTOTYPE_H diff --git a/framework/drm/DrmInfo.cpp b/framework/drm/DrmInfo.cpp new file mode 100644 index 000000000..037943500 --- /dev/null +++ b/framework/drm/DrmInfo.cpp @@ -0,0 +1,6 @@ +// +// Created by SuperMan on 11/27/20. +// + +#include "DrmInfo.h" + diff --git a/framework/drm/DrmInfo.h b/framework/drm/DrmInfo.h new file mode 100644 index 000000000..a149ab4fd --- /dev/null +++ b/framework/drm/DrmInfo.h @@ -0,0 +1,37 @@ +// +// Created by SuperMan on 11/27/20. +// + +#ifndef SOURCE_DRMINFO_H +#define SOURCE_DRMINFO_H + +#include + +namespace Cicada { + class DrmInfo { + public: + std::string uri; + std::string format; + + bool operator==(const DrmInfo &drmInfo) const { + return uri == drmInfo.uri && + format == drmInfo.format; + } + + bool empty() const { + return uri.empty() && + format.empty(); + } + + struct DrmInfoCompare + { + bool operator() (const DrmInfo& lhs, const DrmInfo& rhs) const + { + return lhs.format < rhs.format || lhs.uri < rhs.uri; + } + }; + }; +} + + +#endif //SOURCE_DRMINFO_H diff --git a/framework/drm/DrmManager.cpp b/framework/drm/DrmManager.cpp new file mode 100644 index 000000000..07192dd37 --- /dev/null +++ b/framework/drm/DrmManager.cpp @@ -0,0 +1,59 @@ +// +// Created by SuperMan on 11/27/20. +// + +#include +#include "DrmManager.h" +#include "DrmHandlerPrototype.h" + +using namespace Cicada; + +DrmManager::DrmManager() { + +} + +DrmManager::~DrmManager() { + mDrmMap.clear(); +} + +DrmHandler *DrmManager::require(const DrmInfo &drmInfo) { + + std::lock_guard drmLock(mDrmMutex); + + if (!mDrmMap.empty()) { + for (auto &item : mDrmMap) { + auto &drmItem = (DrmInfo &) item.first; + if (drmItem == drmInfo) { + return item.second.get(); + } + } + } + + DrmHandler *pDrmHandler = DrmHandlerPrototype::create(drmInfo); + + assert(pDrmHandler != nullptr); + + if (pDrmHandler == nullptr) { + return nullptr; + } + + pDrmHandler->setDrmCallback(mDrmCallback); + mDrmMap[drmInfo] = std::unique_ptr(pDrmHandler); + + return pDrmHandler; +} + +void DrmManager::clearErrorItems() { + std::lock_guard drmLock(mDrmMutex); + + if (!mDrmMap.empty()) { + for (auto iter = mDrmMap.begin(); iter != mDrmMap.end();) { + DrmHandler *handler = iter->second.get(); + if (handler->isErrorState()) { + iter = mDrmMap.erase(iter); + } else { + iter++; + } + } + } +} diff --git a/framework/drm/DrmManager.h b/framework/drm/DrmManager.h new file mode 100644 index 000000000..203ce1b1d --- /dev/null +++ b/framework/drm/DrmManager.h @@ -0,0 +1,38 @@ +// +// Created by SuperMan on 11/27/20. +// + +#ifndef SOURCE_DRMMANAGER_H +#define SOURCE_DRMMANAGER_H + +#include +#include +#include "DrmHandler.h" +#include "DrmInfo.h" +#include + +namespace Cicada { + + class DrmManager { + public: + + DrmManager(); + + ~DrmManager(); + + void setDrmCallback(const std::function &callback) { + mDrmCallback = callback; + } + + DrmHandler *require(const DrmInfo &drmInfo); + + void clearErrorItems(); + + private: + std::mutex mDrmMutex{}; + std::map , DrmInfo::DrmInfoCompare> mDrmMap{}; + std::function mDrmCallback{nullptr}; + }; + +} +#endif //SOURCE_DRMMANAGER_H diff --git a/framework/drm/WideVineDrmHandler.cpp b/framework/drm/WideVineDrmHandler.cpp new file mode 100644 index 000000000..d7857326d --- /dev/null +++ b/framework/drm/WideVineDrmHandler.cpp @@ -0,0 +1,417 @@ +// +// Created by SuperMan on 11/27/20. +// + +#define LOG_TAG "WideVineDrmHandler" + +#include "WideVineDrmHandler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +#include +} + +static jclass jMediaDrmSessionClass = nullptr; +static jmethodID jMediaDrmSession_init = nullptr; +static jmethodID jMediaDrmSession_requireSession = nullptr; +static jmethodID jMediaDrmSession_releaseSession = nullptr; +static jmethodID jMediaDrmSession_isForceInsecureDecoder = nullptr; +using namespace Cicada; + +WideVineDrmHandler WideVineDrmHandler::dummyWideVineHandler(0); + +WideVineDrmHandler::WideVineDrmHandler(const DrmInfo &drmInfo) + : DrmHandler(drmInfo) { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return; + } + + jobject pJobject = env->NewObject(jMediaDrmSessionClass, jMediaDrmSession_init, (jlong) this); + mJDrmSessionManger = env->NewGlobalRef(pJobject); + env->DeleteLocalRef(pJobject); +} + +WideVineDrmHandler::~WideVineDrmHandler() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return; + } + + if (mJDrmSessionManger != nullptr) { + env->CallVoidMethod(mJDrmSessionManger, jMediaDrmSession_releaseSession); + env->DeleteGlobalRef(mJDrmSessionManger); + } + + if (mSessionId != nullptr) { + free(mSessionId); + mSessionId = nullptr; + } + +} + +void WideVineDrmHandler::open() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return; + } + + { + std::lock_guard lock(mDrmMutex); + if (bSessionRequested) { + return; + } else { + bSessionRequested = true; + } + } + + NewStringUTF jUrl(env, drmInfo.uri.c_str()); + NewStringUTF jFormat(env, drmInfo.format.c_str()); + jstring pJurl = jUrl.getString(); + jstring pJformat = jFormat.getString(); + env->CallVoidMethod(mJDrmSessionManger, + jMediaDrmSession_requireSession, + pJurl, pJformat, + nullptr, nullptr); +} + + +bool WideVineDrmHandler::isErrorState() { + std::lock_guard lock(mDrmMutex); + return mState == SESSION_STATE_ERROR; +} + +DrmHandler * +WideVineDrmHandler::clone(const DrmInfo &drmInfo) { + return new WideVineDrmHandler(drmInfo); +} + +bool WideVineDrmHandler::is_supported(const DrmInfo &drmInfo) { + return drmInfo.format == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"; +} + +static JNINativeMethod mediaCodec_method_table[] = { + {"native_requestProvision", "(JLjava/lang/String;[B)[B", (void *) WideVineDrmHandler::requestProvision}, + {"native_requestKey", "(JLjava/lang/String;[B)[B", (void *) WideVineDrmHandler::requestKey}, + {"native_changeState", "(JII)V", (void *) WideVineDrmHandler::changeState}, + {"native_updateSessionId", "(J[B)V", (void *) WideVineDrmHandler::updateSessionId}, +}; + +int WideVineDrmHandler::registerMethod(JNIEnv *pEnv) { + if (jMediaDrmSessionClass == nullptr) { + return JNI_FALSE; + } + + if (pEnv->RegisterNatives(jMediaDrmSessionClass, mediaCodec_method_table, + sizeof(mediaCodec_method_table) / sizeof(JNINativeMethod)) < 0) { + return JNI_FALSE; + } + + return JNI_TRUE; +} + +void WideVineDrmHandler::init(JNIEnv *env) { + if (env == nullptr) { + return; + } + + if (jMediaDrmSessionClass == nullptr) { + FindClass jClass(env, "com/cicada/player/utils/media/DrmSessionManager"); + jMediaDrmSessionClass = static_cast(env->NewGlobalRef(jClass.getClass())); + jMediaDrmSession_init = env->GetMethodID(jMediaDrmSessionClass, "", "(J)V"); + jMediaDrmSession_requireSession = env->GetMethodID(jMediaDrmSessionClass, + "requireSession", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + jMediaDrmSession_releaseSession = env->GetMethodID(jMediaDrmSessionClass, "releaseSession", + "()V"); + jMediaDrmSession_isForceInsecureDecoder = env->GetMethodID(jMediaDrmSessionClass, + "isForceInsecureDecoder", + "()Z"); + } +} + +void WideVineDrmHandler::unInit(JNIEnv *env) { + if (env == nullptr) { + return; + } + + if (jMediaDrmSessionClass != nullptr) { + env->DeleteGlobalRef(jMediaDrmSessionClass); + jMediaDrmSessionClass = nullptr; + } +} + +bool WideVineDrmHandler::isForceInsecureDecoder() { + JniEnv jniEnv{}; + + JNIEnv *env = jniEnv.getEnv(); + if (env == nullptr) { + return false; + } + + jboolean ret = env->CallBooleanMethod(mJDrmSessionManger, + jMediaDrmSession_isForceInsecureDecoder); + return (bool) ret; +} + +void WideVineDrmHandler::updateSessionId(JNIEnv *env, jobject instance, jlong nativeInstance, + jbyteArray jSessionId) { + + if (jSessionId == nullptr) { + return; + } + + auto *drmSessionManager = (WideVineDrmHandler *) (int64_t) nativeInstance; + if (drmSessionManager == nullptr) { + return; + } + + { + std::lock_guard lock(drmSessionManager->mDrmMutex); + drmSessionManager->mSessionSize = env->GetArrayLength(jSessionId);; + drmSessionManager->mSessionId = JniUtils::jByteArrayToChars(env, jSessionId); + } +} + +void WideVineDrmHandler::changeState(JNIEnv *env, jobject instance, jlong nativeIntance, + jint state, jint errorCode) { + + auto *drmSessionManager = (WideVineDrmHandler *) (int64_t) nativeIntance; + if (drmSessionManager == nullptr) { + return; + } + + { + std::lock_guard lock(drmSessionManager->mDrmMutex); + + if (state == 0) { + drmSessionManager->mState = SESSION_STATE_OPENED; + AF_LOGI("drm prepared OK"); + } else if (state == -1) { + drmSessionManager->mState = SESSION_STATE_ERROR; + } else if (state == -2) { + drmSessionManager->mState = SESSION_STATE_IDLE; + } + + drmSessionManager->mErrorCode = gen_framework_errno(error_class_drm, errorCode); + } +} + + +jbyteArray +WideVineDrmHandler::requestProvision(JNIEnv *env, jobject instance, jlong nativeIntance, + jstring url, jbyteArray data) { + + AF_LOGI("drm requestProvision.,"); + if (nativeIntance == 0) { + return nullptr; + } + + auto *drmSessionManager = (WideVineDrmHandler *) (int64_t) nativeIntance; + if (drmSessionManager->drmCallback == nullptr) { + return nullptr; + } + + GetStringUTFChars cUrl(env, url); + char *cData = JniUtils::jByteArrayToChars(env, data); + int dataLen = env->GetArrayLength(data); + + DrmRequestParam drmRequestParam{}; + drmRequestParam.mDrmType = "WideVine"; + + CicadaJSONItem param{}; + param.addValue("requestType", "provision"); + param.addValue("url", std::string(cUrl.getChars())); + param.addValue("data", CicadaUtils::base64enc(cData, dataLen)); + drmRequestParam.mParam = ¶m; + + free(cData); + + DrmResponseData *drmResponseData = drmSessionManager->drmCallback(drmRequestParam); + + if (drmResponseData == nullptr) { + return nullptr; + } + + int responseDataSize = 0; + const char *responseData = drmResponseData->getData(&responseDataSize); + + jbyteArray mResult = nullptr; + if (responseData != nullptr && responseDataSize > 0) { + mResult = env->NewByteArray(responseDataSize); + env->SetByteArrayRegion(mResult, 0, responseDataSize, (jbyte *) (responseData)); + } + + delete drmResponseData; + + return mResult; +} + +jbyteArray +WideVineDrmHandler::requestKey(JNIEnv *env, jobject instance, jlong nativeIntance, + jstring url, + jbyteArray data) { + AF_LOGI("drm requestKey.,"); + if (nativeIntance == 0) { + return nullptr; + } + + auto *drmSessionManager = (WideVineDrmHandler *) (int64_t) nativeIntance; + if (drmSessionManager->drmCallback == nullptr) { + return nullptr; + } + + GetStringUTFChars cUrl(env, url); + char *cData = JniUtils::jByteArrayToChars(env, data); + int dataLen = env->GetArrayLength(data); + + DrmRequestParam drmRequestParam{}; + drmRequestParam.mDrmType = "WideVine"; + + CicadaJSONItem param{}; + param.addValue("requestType", "key"); + param.addValue("url", std::string(cUrl.getChars())); + param.addValue("data", CicadaUtils::base64enc(cData, dataLen)); + drmRequestParam.mParam = ¶m; + + free(cData); + + DrmResponseData *drmResponseData = drmSessionManager->drmCallback(drmRequestParam); + + if (drmResponseData == nullptr) { + return nullptr; + } + + int responseDataSize = 0; + const char *responseData = drmResponseData->getData(&responseDataSize); + + jbyteArray mResult = nullptr; + if (responseData != nullptr && responseDataSize > 0) { + mResult = env->NewByteArray(responseDataSize); + env->SetByteArrayRegion(mResult, 0, responseDataSize, (jbyte *) (responseData)); + } + + delete drmResponseData; + + return mResult; +} + +int WideVineDrmHandler::getState() { + std::lock_guard lock(mDrmMutex); + return mState; +} + +int WideVineDrmHandler::getSessionId(char **session) { + std::lock_guard lock(mDrmMutex); + *session = mSessionId; + return mSessionSize; +} + +int WideVineDrmHandler::getErrorCode() { + std::lock_guard lock(mDrmMutex); + return mErrorCode; +} + +void WideVineDrmHandler::convertData(int naluLengthSize, uint8_t **new_data, int *new_size, + const uint8_t *data, + int size) { + if (data == nullptr || size == 0) { + return; + } + + if (naluLengthSize == 0) { + return; + } + + const int nalPrefixLen = 5; + char nalPrefixData[nalPrefixLen]; + nalPrefixData[0] = 0; + nalPrefixData[1] = 0; + nalPrefixData[2] = 0; + nalPrefixData[3] = 0; + nalPrefixData[4] = 0; + + int sampleBytesWritten = 0; + int sampleSize = size; + + int nalUnitPrefixLength = naluLengthSize + 1; + int nalUnitLengthFieldLengthDiff = 4 - naluLengthSize; + int sampleCurrentNalBytesRemaining = 0; + + *new_size = size; + *new_data = static_cast(malloc(*new_size)); + uint8_t *new_data_ptr = *new_data; + uint8_t *ori_data_ptr = const_cast(data); + + while (sampleBytesWritten < sampleSize) { + + if (sampleCurrentNalBytesRemaining == 0) { +//new nal + for (int i = 0; i < nalUnitPrefixLength; i++) { + nalPrefixData[nalUnitLengthFieldLengthDiff + i] = *ori_data_ptr; + ori_data_ptr++; + } + + int nalLengthInt = AV_RB32(nalPrefixData); + if (nalLengthInt < 1) { + AF_LOGE("Invalid NAL length"); + return; + } + + sampleCurrentNalBytesRemaining = nalLengthInt - 1; + + if (sampleBytesWritten + 5 > *new_size) { + + *new_size = sampleBytesWritten + 5; + + *new_data = static_cast(realloc(*new_data, *new_size)); + new_data_ptr = *new_data + sampleBytesWritten; + } + + //put start code + static uint8_t START_CODE[4] = {0, 0, 0, 1}; + memcpy(new_data_ptr, START_CODE, 4); + new_data_ptr += 4; + //nal type + *new_data_ptr = nalPrefixData[4]; + new_data_ptr++; + + sampleBytesWritten += 5; + sampleSize += nalUnitLengthFieldLengthDiff; + + } else { + if (sampleBytesWritten + sampleCurrentNalBytesRemaining > *new_size) { + *new_size = sampleBytesWritten + sampleCurrentNalBytesRemaining; + *new_data = static_cast(realloc(*new_data, *new_size)); + + new_data_ptr = *new_data + sampleBytesWritten; + } + memcpy(new_data_ptr, ori_data_ptr, sampleCurrentNalBytesRemaining); + ori_data_ptr = ori_data_ptr + sampleCurrentNalBytesRemaining; + new_data_ptr = new_data_ptr + sampleCurrentNalBytesRemaining; + + sampleBytesWritten += sampleCurrentNalBytesRemaining; + sampleCurrentNalBytesRemaining -= sampleCurrentNalBytesRemaining; + } + } + + assert(*new_size == sampleBytesWritten); + + *new_size = sampleBytesWritten; +} diff --git a/framework/drm/WideVineDrmHandler.h b/framework/drm/WideVineDrmHandler.h new file mode 100644 index 000000000..eab3535ae --- /dev/null +++ b/framework/drm/WideVineDrmHandler.h @@ -0,0 +1,89 @@ +// +// Created by SuperMan on 11/27/20. +// + +#ifndef SOURCE_WIDEVINEDRMHANDLER_H +#define SOURCE_WIDEVINEDRMHANDLER_H + +#include +#include +#include "DrmHandler.h" +#include "DrmHandlerPrototype.h" + +#define SESSION_STATE_ERROR (-1) +#define SESSION_STATE_IDLE (-2) +#define SESSION_STATE_OPENED (0) + +namespace Cicada { + class WideVineDrmHandler : public DrmHandler, private DrmHandlerPrototype { + public: + WideVineDrmHandler(const DrmInfo &drmInfo); + + ~WideVineDrmHandler() override; + + DrmHandler *clone(const DrmInfo &drmInfo) override; + + bool is_supported(const DrmInfo &drmInfo) override; + + bool isErrorState() override; + + public: + int getState(); + + int getErrorCode(); + + bool isForceInsecureDecoder(); + + void open(); + + int getSessionId(char** sessionId); + + static void convertData(int naluLengthSize, uint8_t **new_data, int *new_size, + const uint8_t *data, + int size); + public: + static void init(JNIEnv *env); + + static void unInit(JNIEnv *env); + + static int registerMethod(JNIEnv *env); + + static jbyteArray + requestProvision(JNIEnv *env, jobject instance, jlong nativeInstance, jstring url, + jbyteArray data); + + static jbyteArray + requestKey(JNIEnv *env, jobject instance, jlong nativeInstance, jstring url, + jbyteArray data); + + static void changeState(JNIEnv *env, jobject instance, jlong nativeInstance, + jint state, jint errorCode); + + static void updateSessionId(JNIEnv *env, jobject instance, jlong nativeInstance, + jbyteArray jSessionId); + + protected: + explicit WideVineDrmHandler(int dummy) + : DrmHandler(DrmInfo{}) { + addPrototype(this); + } + + static WideVineDrmHandler dummyWideVineHandler; + + private: + jobject mJDrmSessionManger = nullptr; + + std::mutex mDrmMutex{}; + + char *mSessionId = nullptr; + int mSessionSize{0}; + + int mState{SESSION_STATE_IDLE}; + int mErrorCode{0}; + + bool bSessionRequested{false}; + }; +} + + +#endif //SOURCE_WIDEVINEDRMHANDLER_H diff --git a/framework/filter/IAudioFilter.h b/framework/filter/IAudioFilter.h index b9762893b..f0be58ddb 100644 --- a/framework/filter/IAudioFilter.h +++ b/framework/filter/IAudioFilter.h @@ -12,6 +12,8 @@ using namespace std; namespace Cicada{ class IAudioFilter { +#define A_FILTER_FLAG_TEMPO (1 << 1) +#define A_FILTER_FLAG_VOLUME (1 << 2) public: typedef IAFFrame::audioInfo format; @@ -22,8 +24,7 @@ namespace Cicada{ // virtual bool beSupported(const char* capacity) = 0; virtual bool setOption(const string &key, const string &value, const string &capacity) = 0; - attribute_warn_unused_result - virtual int init() = 0; + attribute_warn_unused_result virtual int init(uint64_t flags) = 0; virtual int push(std::unique_ptr &frame, uint64_t timeOut) = 0; diff --git a/framework/filter/ffmpegAudioFilter.cpp b/framework/filter/ffmpegAudioFilter.cpp index eaaa5f22f..42dec5959 100644 --- a/framework/filter/ffmpegAudioFilter.cpp +++ b/framework/filter/ffmpegAudioFilter.cpp @@ -168,9 +168,10 @@ namespace Cicada { return err; } - int ffmpegAudioFilter::init() + int ffmpegAudioFilter::init(uint64_t flags) { int err; + mFlags = flags; m_pFilterGraph = avfilter_graph_alloc(); if (m_pFilterGraph == nullptr) { @@ -194,8 +195,10 @@ namespace Cicada { char options_str[1024]; bool needAFormat = false; // for now enlarge only - snprintf(options_str, sizeof(options_str), "volume=%f", std::max(mVolume, 1.0)); - err = addFilter(¤t, "volume", options_str); + if (flags & A_FILTER_FLAG_VOLUME) { + snprintf(options_str, sizeof(options_str), "volume=%f", std::max(mVolume, 1.0)); + err = addFilter(¤t, "volume", options_str); + } if (err == 0 //volume would add a aformat filter too || mSrcFormat.sample_rate != mDstFormat.sample_rate @@ -204,8 +207,10 @@ namespace Cicada { needAFormat = true; } - snprintf(options_str, sizeof(options_str), "tempo=%f", mRate.load()); - addFilter(¤t, "atempo", options_str); + if (flags & A_FILTER_FLAG_TEMPO) { + snprintf(options_str, sizeof(options_str), "tempo=%f", mRate.load()); + addFilter(¤t, "atempo", options_str); + } if (needAFormat) { snprintf(options_str, sizeof(options_str), @@ -290,7 +295,7 @@ namespace Cicada { std::lock_guard uMutex(mMutexRate); if (m_pFilterGraph == nullptr) { - ret = init(); + ret = init(mFlags); if (ret < 0) { AF_LOGE("init error\n"); @@ -301,6 +306,8 @@ namespace Cicada { int64_t pts = dynamic_cast(frame)->getInfo().pts; + int64_t timePosition = frame->getInfo().timePosition; + if (mFirstPts == INT64_MIN) { mFirstPts = pts; } @@ -373,6 +380,7 @@ namespace Cicada { // int plane_size = bps * filt_frame->nb_samples * (planar ? 1 : channels); // write(fd, filt_frame->extended_data[0], plane_size); #endif + frame->getInfo().timePosition = timePosition; mOutPut.push(frame); frame = nullptr; } diff --git a/framework/filter/ffmpegAudioFilter.h b/framework/filter/ffmpegAudioFilter.h index 6208b6e33..f74258dbf 100644 --- a/framework/filter/ffmpegAudioFilter.h +++ b/framework/filter/ffmpegAudioFilter.h @@ -24,7 +24,7 @@ namespace Cicada { bool setOption(const string &key, const string &value, const string &capacity) override; - int init() override; + int init(uint64_t flags) override; int push(std::unique_ptr &frame, uint64_t timeOut) override; @@ -60,6 +60,8 @@ namespace Cicada { std::atomic mLastInputPts {INT64_MIN}; std::atomic mLastInPutDuration {0}; + uint64_t mFlags{0}; + int addFilter(AVFilterContext **current, const char *name, const char *options_str); }; } diff --git a/framework/filter/ffmpegVideoFilter.cpp b/framework/filter/ffmpegVideoFilter.cpp index d08cc2184..d74544a0b 100644 --- a/framework/filter/ffmpegVideoFilter.cpp +++ b/framework/filter/ffmpegVideoFilter.cpp @@ -1,9 +1,10 @@ // // Created by moqi on 2020/5/28. // - +#define LOG_TAG "ffmpegVideoFilter" #include "ffmpegVideoFilter.h" #include +#include #include extern "C" { #include diff --git a/framework/macOSX.cmake b/framework/macOSX.cmake index 7c23e40ea..24f88deaa 100644 --- a/framework/macOSX.cmake +++ b/framework/macOSX.cmake @@ -38,6 +38,8 @@ find_library(COREFOUNDATION CoreFoundation) find_library(SECURITY Security) find_library(OPENGL OpenGL) find_library(APPKIT AppKit) +find_library(AVFOUNDATION AVFoundation) +find_library(QUARTZCORE QuartzCore) set(FRAMEWORK_LIBS ${VIDEO_TOOL_BOX} @@ -50,6 +52,8 @@ set(FRAMEWORK_LIBS ${COREFOUNDATION} ${OPENGL} ${APPKIT} + ${AVFOUNDATION} + ${QUARTZCORE} iconv z) diff --git a/framework/module_config.cmake b/framework/module_config.cmake index a3bc55ef7..dfc3558b2 100644 --- a/framework/module_config.cmake +++ b/framework/module_config.cmake @@ -7,4 +7,5 @@ option(ENABLE_MEDIA_CODEC_DECODER "enable android media codec decoder" ON) option(ENABLE_VTB_DECODER "enable apple video tool box decoder" ON) option(ENABLE_MUXER "enable muxer" ON) -option(ENABLE_CACHE_MODULE "enable cache module" ON) \ No newline at end of file +option(ENABLE_CACHE_MODULE "enable cache module" ON) +option(ENABLE_CODEC_HEVC "enable hevc codec" ON) \ No newline at end of file diff --git a/framework/muxer/ffmpegMuxer/FfmpegMuxer.cpp b/framework/muxer/ffmpegMuxer/FfmpegMuxer.cpp index ed5187bdf..913c89b47 100644 --- a/framework/muxer/ffmpegMuxer/FfmpegMuxer.cpp +++ b/framework/muxer/ffmpegMuxer/FfmpegMuxer.cpp @@ -52,7 +52,7 @@ int FfmpegMuxer::open() for (Stream_meta *item : *mStreamMetas) { AVStream *stream = nullptr; - if (item->type == Stream_type::STREAM_TYPE_VIDEO) { + if (item->type == Stream_type::STREAM_TYPE_VIDEO && item->attached_pic == 0) { stream = avformat_new_stream(mDestFormatContext, nullptr); MetaToCodec::videoMetaToStream(stream, item); check_codec_tag(stream); @@ -150,14 +150,14 @@ int FfmpegMuxer::writeFrame(unique_ptr packetPtr) return -1; } - AVPacket *avPacket = getAVPacket(packetPtr.get()); + AVPacket *pkt = getAVPacket(packetPtr.get()); - if (avPacket == nullptr) { + if (pkt == nullptr) { AF_LOGE("muxer packet is null.."); return -1; } - int pktStreamIndex = avPacket->stream_index; + int pktStreamIndex = pkt->stream_index; StreamInfo &streamInfo = mStreamInfoMap[pktStreamIndex]; if (mStreamInfoMap.count(pktStreamIndex) == 0) { @@ -165,10 +165,8 @@ int FfmpegMuxer::writeFrame(unique_ptr packetPtr) return -1; } - AVPacket *pkt = av_packet_clone(avPacket); - if (mFirstPts == INT64_MIN) { - mFirstPts = avPacket->pts; + mFirstPts = pkt->pts; } pkt->stream_index = streamInfo.targetIndex; @@ -206,7 +204,7 @@ int FfmpegMuxer::writeFrame(unique_ptr packetPtr) mDestFormatContext->max_interleave_delta = 0; int ret = av_interleaved_write_frame(mDestFormatContext, pkt); - av_packet_free(&pkt); + // av_packet_free(&pkt); if (ret < 0) { AF_LOGE("write packet failed . ret = %d. pktStreamIndex index = %d , stream index = %d ", ret, pktStreamIndex, streamInfo.targetIndex); diff --git a/framework/render/CMakeLists.txt b/framework/render/CMakeLists.txt index b056b7041..a7cd60c4e 100644 --- a/framework/render/CMakeLists.txt +++ b/framework/render/CMakeLists.txt @@ -7,6 +7,8 @@ list(APPEND SRC_FILES video/vsync/timedVSync.h video/vsync/VSyncFactory.cpp video/vsync/VSyncFactory.h + video/AFActiveVideoRender.cpp + video/AFActiveVideoRender.h renderFactory.cpp renderFactory.h audio/filterAudioRender.cpp @@ -33,6 +35,8 @@ if (ENABLE_SDL) list(APPEND SRC_FILES audio/SdlAFAudioRender.h audio/SdlAFAudioRender.cpp + audio/SdlAFAudioRender2.h + audio/SdlAFAudioRender2.cpp video/SdlAFVideoRender.cpp video/SdlAFVideoRender.h ) @@ -40,7 +44,6 @@ endif () if (ENABLE_CHEAT_RENDER) list(APPEND SRC_FILES - video/CheaterVideoRender.cpp audio/CheaterAudioRender.cpp ) endif () @@ -56,10 +59,17 @@ endif () if (APPLE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fobjc-arc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fobjc-arc") list(APPEND SRC_FILES audio/Apple/AudioRenderType.h - audio/Apple/AFAudioUnitRender.cpp - audio/Apple/AFAudioUnitRender.h + audio/Apple/AFAudioQueueRender.cpp + audio/Apple/AFAudioQueueRender.h + video/AVFoundation/AVFoundationVideoRender.mm + video/AVFoundation/AVFoundationVideoRender.h + video/AVFoundation/SampleDisplayLayerRender.mm + video/AVFoundation/SampleDisplayLayerRender.h + video/AVFoundation/DisplayLayerImpl-interface.h ) endif () diff --git a/framework/render/audio/Android/AudioTrackRender.cpp b/framework/render/audio/Android/AudioTrackRender.cpp index f52e8b710..14522e8a0 100644 --- a/framework/render/audio/Android/AudioTrackRender.cpp +++ b/framework/render/audio/Android/AudioTrackRender.cpp @@ -3,6 +3,7 @@ // #define LOG_TAG "AudioTrackRender" #include "AudioTrackRender.h" +#include #include #include @@ -336,6 +337,10 @@ int AudioTrackRender::write_loop() // TODO: How to decrease mMaxQueSize size, if use nonblock write, we can do this // mMaxQueSize = std::max(MIN_FRAME_QUEUE_SIZE,mMaxQueSize -1); } + if (mListener) { + mListener->onUpdateTimePosition(mFrameQueue.front()->getInfo().timePosition); + } + delete mFrameQueue.front(); mFrameQueue.pop(); } } diff --git a/framework/render/audio/Android/AudioTrackRender.h b/framework/render/audio/Android/AudioTrackRender.h index 9f2ad9d85..b691b5444 100644 --- a/framework/render/audio/Android/AudioTrackRender.h +++ b/framework/render/audio/Android/AudioTrackRender.h @@ -38,6 +38,16 @@ class AudioTrackRender : uint64_t device_get_que_duration() override; + uint64_t device_get_ability() override + { + return 0; + } + + void device_preClose() override + { + mRunning = false; + } + private: AudioTrackRender(int dummy) : mFrameQueue(1) { diff --git a/framework/render/audio/Apple/AFAudioQueueRender.cpp b/framework/render/audio/Apple/AFAudioQueueRender.cpp new file mode 100644 index 000000000..e02211dc5 --- /dev/null +++ b/framework/render/audio/Apple/AFAudioQueueRender.cpp @@ -0,0 +1,383 @@ +// +// Created by pingkai on 2020/10/9. +// + +#include "AFAudioQueueRender.h" + +#define LOG_TAG "AFAudioQueueRender" + +#include + +#include +#include +#include +#include + +using namespace Cicada; + +AFAudioQueueRender AFAudioQueueRender::se(1); + +//static int64_t gtime = INT64_MIN; + +void AFAudioQueueRender::OutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) +{ + auto *pThis = (AFAudioQueueRender *) inUserData; + + // if (gtime != INT64_MIN) { + // AF_LOGD("time delta is %lld\n",af_gettime_ms() - gtime); + // } + // gtime =af_gettime_ms(); + + + int size = inBuffer->mAudioDataByteSize; + + assert(pThis); + if (!pThis->mRunning) { + return; + } + pThis->mPlayedBufferSize += inBuffer->mAudioDataByteSize; + /* + * MUST copy buffer fully, otherwise lots of bug will come + */ + bool CopyFull = true; +#if TARGET_OS_IPHONE + if (IOSNotificationManager::Instance()->GetActiveStatus() == 0 || pThis->mQueueSpeed > 1.0) { + CopyFull = true; + size = inBuffer->mAudioDataBytesCapacity; + } +#endif + inBuffer->mAudioDataByteSize = pThis->copyAudioData(inBuffer, CopyFull); + if (inBuffer->mAudioDataByteSize == 0) { + // AF_LOGW("no audio data\n"); + inBuffer->mAudioDataByteSize = size; + + if (!pThis->mNeedFlush) { + pThis->mPlayedBufferSize -= inBuffer->mAudioDataByteSize; + } + memset((uint8_t *) inBuffer->mAudioData, 0, inBuffer->mAudioDataByteSize); + } + AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr); +} +UInt32 AFAudioQueueRender::copyAudioData(const AudioQueueBuffer *inBuffer, bool CopyFull) +{ + if (mNeedFlush) { + while (mInPut.size() > 0) { + delete mInPut.front(); + mInPut.pop(); + } + mReadOffset = 0; + mNeedFlush = false; + return 0; + } + if (!mPlaying) { + return 0; + } + UInt32 copySize = 0; + int retryCont = 0; + while (copySize < inBuffer->mAudioDataBytesCapacity) { + if (mInPut.size() > 0) { + bool frameClear = false; + size_t len = copyPCMDataWithOffset(getAVFrame(mInPut.front()), mReadOffset, (uint8_t *) inBuffer->mAudioData + copySize, + inBuffer->mAudioDataBytesCapacity - copySize, &frameClear); + assert(len > 0); + mReadOffset += len; + copySize += len; + if (frameClear) { + if (mListener) { + mListener->onUpdateTimePosition(mInPut.front()->getInfo().timePosition); + } + delete mInPut.front(); + mInPut.pop(); + mReadOffset = 0; + } + // FIXME: lowlatency mBufferCount == 4 + if (!CopyFull && (mBufferCount < 4 || copySize >= inBuffer->mAudioDataBytesCapacity / 4)) { + break; + } + } else { + if (retryCont++ < 3 && mRunning && !mNeedFlush) { + af_msleep(5); + } else { + break; + } + } + } + return copySize; +} + +AFAudioQueueRender::AFAudioQueueRender() +{ +#if TARGET_OS_IPHONE + AFAudioSessionWrapper::addObserver(this); +#endif +} + +AFAudioQueueRender::AFAudioQueueRender(int dummy) +{ +#if TARGET_OS_IPHONE + AFAudioSessionWrapper::init(); +#endif + addPrototype(this); +} + +AFAudioQueueRender::~AFAudioQueueRender() +{ +#if TARGET_OS_IPHONE + AFAudioSessionWrapper::removeObserver(this); +#endif + mRunning = false; + if (_audioQueueRef) { + AudioQueueStop(_audioQueueRef, true); + AudioQueueDispose(_audioQueueRef, true); + } + while (mInPut.size() > 0) { + delete mInPut.front(); + mInPut.pop(); + } +} + +void AFAudioQueueRender::fillAudioFormat() +{ + int bytesPerSample = 2; + /* + * There is a performance problem when use copyPCMData to copy a planer audio, + * use ffmpeg filter convert it to packed audio + */ + if (mOutputInfo.format >= AF_SAMPLE_FMT_U8P) { + mOutputInfo.format -= AF_SAMPLE_FMT_U8P; + needFilter = true; + } + + switch (mOutputInfo.format) { + case AF_SAMPLE_FMT_S16: + bytesPerSample = 2; + mAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + break; + + case AF_SAMPLE_FMT_S16P: + bytesPerSample = 2; + mAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger; + break; + + case AF_SAMPLE_FMT_FLT: + bytesPerSample = 4; + mAudioFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked; + break; + + case AF_SAMPLE_FMT_FLTP: + bytesPerSample = 4; + mAudioFormat.mFormatFlags = kAudioFormatFlagIsFloat; + break; + + default: + assert(0); + break; + } + + mAudioFormat.mSampleRate = mOutputInfo.sample_rate; + mAudioFormat.mChannelsPerFrame = static_cast(mOutputInfo.channels); + mAudioFormat.mBytesPerFrame = static_cast(mOutputInfo.channels * bytesPerSample); + mAudioFormat.mFramesPerPacket = 1; + mAudioFormat.mBitsPerChannel = static_cast(bytesPerSample * 8); + mAudioFormat.mFormatID = kAudioFormatLinearPCM; + mAudioFormat.mBytesPerPacket = mAudioFormat.mBytesPerFrame * mAudioFormat.mFramesPerPacket; + mAudioFormat.mReserved = 0; + + + // the min size is 84ms + mAudioDataByteSize = (mAudioFormat.mBytesPerFrame * mAudioFormat.mSampleRate) / 10; +} + +int AFAudioQueueRender::setSpeed(float speed) +{ + // return filterAudioRender::setSpeed(speed); + UInt32 timePitchBypass = 0; + if (fabsf(speed - 1.0f) <= 0.000001) { + timePitchBypass = 1; + speed = 1.0f; + } + AudioQueueSetProperty(_audioQueueRef, kAudioQueueProperty_TimePitchBypass, &timePitchBypass, sizeof(timePitchBypass)); + AudioQueueSetParameter(_audioQueueRef, kAudioQueueParam_PlayRate, speed); + mQueueSpeed = speed; + return 0; +} + +int AFAudioQueueRender::init_device() +{ + fillAudioFormat(); + OSStatus status = AudioQueueNewOutput(&mAudioFormat, OutputCallback, this, nullptr, kCFRunLoopCommonModes, 0, &_audioQueueRef); + if (status != noErr) { + AF_LOGE("AudioQueue: AudioQueueNewOutput failed (%d)\n", (int) status); + return status; + } + UInt32 propValue = 1; + AudioQueueSetProperty(_audioQueueRef, kAudioQueueProperty_EnableTimePitch, &propValue, sizeof(propValue)); + propValue = 1; + AudioQueueSetProperty(_audioQueueRef, kAudioQueueProperty_TimePitchBypass, &propValue, sizeof(propValue)); + propValue = kAudioQueueTimePitchAlgorithm_TimeDomain; + AudioQueueSetProperty(_audioQueueRef, kAudioQueueProperty_TimePitchAlgorithm, &propValue, sizeof(propValue)); + return 0; +} + +int AFAudioQueueRender::pause_device() +{ + if (mPlaying) { + mPlaying = false; + if (_audioQueueRef) { + AudioQueuePause(_audioQueueRef); + } + } + return 0; +} + +int AFAudioQueueRender::start_device() +{ + if (!mPlaying) { + mPlaying = true; + if (_audioQueueRef) { + mStartStatus = AudioQueueStart(_audioQueueRef, nullptr); + if (mStartStatus != AVAudioSessionErrorCodeNone) { + AF_LOGE("AudioQueue: AudioQueueStart failed (%d)\n", (int) mStartStatus); + } + } + } + return 0; +} + +void AFAudioQueueRender::flush_device() +{ + /* + * fix me: + * pause to avoid noise no flush, but it will spend too long time, so only do this when not mute + * , it will speed up player stop + */ + AudioQueueParameterValue volume = 0; + if (_audioQueueRef) { + AudioQueueGetParameter(_audioQueueRef, kAudioQueueParam_Volume, &volume); + } + if (mPlaying && volume > 0) { + AudioQueuePause(_audioQueueRef); + } + for (auto item : _audioQueueBufferRefArray) { + if (item && item->mAudioData) { + memset(item->mAudioData, 0, item->mAudioDataByteSize); + } + } + + if (mBufferCount == 0 || mBufferAllocatedCount < mBufferCount) { + while (mInPut.size() > 0) { + delete mInPut.front(); + mInPut.pop(); + } + mReadOffset = 0; + } else { + mNeedFlush = true; + } + mPlayedBufferSize = 0; + if (mPlaying && volume > 0) { + AudioQueueStart(_audioQueueRef, nullptr); + } +} + +void AFAudioQueueRender::device_setVolume(float gain) +{ + float aq_volume = gain; + if (fabsf(aq_volume - 1.0f) <= 0.000001) { + AudioQueueSetParameter(_audioQueueRef, kAudioQueueParam_Volume, 1.f); + } else { + AudioQueueSetParameter(_audioQueueRef, kAudioQueueParam_Volume, aq_volume); + } +} + +int64_t AFAudioQueueRender::device_get_position() +{ + return static_cast(mPlayedBufferSize / (mAudioFormat.mBytesPerFrame * (mAudioFormat.mSampleRate / 1000000))); +} + +int AFAudioQueueRender::device_write(unique_ptr &frame) +{ + if (mNeedFlush || mInPut.write_available() <= 0) { + return -EAGAIN; + } + + if (mBufferCount == 0) { + //FIXME: for low latency stream, queue another more buffer + assert(frame->getInfo().duration > 0); + if (frame->getInfo().duration < 20000) { + mBufferCount = 4; + } else { + mBufferCount = 3; + } + } + mInPut.push(frame.release()); + if (mBufferAllocatedCount < mBufferCount && mInPut.size() >= mBufferCount) { + assert(mAudioDataByteSize > 0); + assert(mBufferCount <= MAX_QUEUE_SIZE); + while (mBufferAllocatedCount < mBufferCount) { + AudioQueueBuffer *buffer = NULL; + AudioQueueAllocateBuffer(_audioQueueRef, mAudioDataByteSize, &buffer); + _audioQueueBufferRefArray[mBufferAllocatedCount] = buffer; + buffer->mAudioDataByteSize = copyAudioData(buffer, false); + AudioQueueEnqueueBuffer(_audioQueueRef, buffer, 0, nullptr); + mBufferAllocatedCount++; + } + } + return 0; +} + +uint64_t AFAudioQueueRender::device_get_que_duration() +{ + if (mInPut.empty()) { + return 0; + } + return mInPut.size() * mInPut.front()->getInfo().duration; +} + +#if TARGET_OS_IPHONE + +void AFAudioQueueRender::onInterrupted(Cicada::AF_AUDIO_SESSION_STATUS status) +{ + bool deal = false; + if (status != AFAudioSessionMediaServicesWereReset && mListener) { + deal = mListener->onInterrupt(status == AFAudioSessionBeginInterruption); + } + if (status == AFAudioSessionEndInterruption && mStartStatus != AVAudioSessionErrorCodeNone) { + /* + Start audioQueue would fail during interrupted, restart it on interrupt end. + */ + deal = false; + } + if (deal) { + return; + } + switch (status) { + case AFAudioSessionEndInterruption: + if (mPlaying && _audioQueueRef) { + AFAudioSessionWrapper::activeAudio(); + mStartStatus = AudioQueueStart(_audioQueueRef, nullptr); + if (mStartStatus != AVAudioSessionErrorCodeNone) { + AF_LOGE("AudioQueueStart error at interrupt end %d\n", mStartStatus); + } + } + break; + + case AFAudioSessionBeginInterruption: + if (mPlaying && _audioQueueRef) { + OSStatus status1 = AudioQueuePause(_audioQueueRef); + if (status1 != AVAudioSessionErrorCodeNone) { + AF_LOGE("AudioQueuePause error at interrupt %d\n", status1); + } + } + break; + + case AFAudioSessionMediaServicesWereReset: + assert(0); + AF_LOGE("AFAudioSessionMediaServicesWereReset\n"); + // closeDevice(); + // start_device(); + break; + default: + break; + } +} +#endif diff --git a/framework/render/audio/Apple/AFAudioQueueRender.h b/framework/render/audio/Apple/AFAudioQueueRender.h new file mode 100644 index 000000000..9b2718861 --- /dev/null +++ b/framework/render/audio/Apple/AFAudioQueueRender.h @@ -0,0 +1,102 @@ +// +// Created by pingkai on 2020/10/9. +// + +#ifndef CICADAMEDIA_AFAUDIOQUEUERENDER_H +#define CICADAMEDIA_AFAUDIOQUEUERENDER_H + +#include "../filterAudioRender.h" +#include +#include +#include + +#if TARGET_OS_IPHONE + +#include "AFAudioSessionWrapper.h" + +#endif +namespace Cicada { + class AFAudioQueueRender : public filterAudioRender, + private audioRenderPrototype +#if TARGET_OS_IPHONE + , + public AFAudioSessionWrapper::listener +#endif + + { + public: + AFAudioQueueRender(); + + ~AFAudioQueueRender() override; + + int setSpeed(float speed) override; + + private: + int init_device() override; + + int pause_device() override; + + int start_device() override; + + void flush_device() override; + + void device_setVolume(float gain) override; + + int64_t device_get_position() override; + + int device_write(unique_ptr &frame) override; + + uint64_t device_get_que_duration() override; + + uint64_t device_get_ability() override + { + return (uint64_t) A_FILTER_FLAG_TEMPO; + } + + private: + explicit AFAudioQueueRender(int dummy); + + Cicada::IAudioRender *clone() override + { + return new AFAudioQueueRender(); + } + + bool is_supported(AFCodecID codec) override + { + return true; + }; + + static AFAudioQueueRender se; + + private: + void fillAudioFormat(); + + static void OutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer); + + UInt32 copyAudioData(const AudioQueueBuffer *inBuffer, bool CopyFull); + +#if TARGET_OS_IPHONE + void onInterrupted(Cicada::AF_AUDIO_SESSION_STATUS status) override; + +#endif + private: +#define MAX_QUEUE_SIZE 4 + AudioQueueBufferRef _audioQueueBufferRefArray[MAX_QUEUE_SIZE]{}; + AudioQueueRef _audioQueueRef{nullptr}; + AudioStreamBasicDescription mAudioFormat{}; + SpscQueue mInPut{10}; + uint64_t mPlayedBufferSize{0}; + uint8_t mBufferAllocatedCount{0}; + bool mNeedFlush{false}; + bool mRunning{true}; + bool mPlaying{false}; + OSStatus mStartStatus{AVAudioSessionErrorCodeNone}; + unsigned int mReadOffset{0}; + UInt32 mAudioDataByteSize{0}; + float mQueueSpeed{1.0}; + uint8_t mBufferCount{0}; + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_AFAUDIOQUEUERENDER_H diff --git a/framework/render/audio/Apple/AFAudioSession.mm b/framework/render/audio/Apple/AFAudioSession.mm index f98064259..ad2e6c426 100644 --- a/framework/render/audio/Apple/AFAudioSession.mm +++ b/framework/render/audio/Apple/AFAudioSession.mm @@ -5,7 +5,7 @@ // Created by huang_jiafa on 2019/01/18. // Copyright (c) 2019 Aliyun. All rights reserved. // - +#define LOG_TAG "AFAudioSession" #import #import #import "AFAudioSession.h" @@ -61,7 +61,7 @@ -(void) setCallback:(std::function)func -(void) handleRouteChange:(NSNotification *)notification { - UInt8 reasonValue = [[notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey] intValue]; + NSInteger reasonValue = [[notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey] intValue]; AVAudioSessionRouteDescription *routeDescription = [notification.userInfo valueForKey:AVAudioSessionRouteChangePreviousRouteKey]; switch (reasonValue) { @@ -95,22 +95,32 @@ -(void) handleRouteChange:(NSNotification *)notification -(void) handleInterruption:(NSNotification *)notification { - UInt8 theInterruptionType = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] intValue]; - - AF_LOGI("Session %s\n", theInterruptionType == AVAudioSessionInterruptionTypeBegan ? "Begin Interruption" : "End Interruption"); - - AF_AUDIO_SESSION_STATUS nID = AFAudioSessionEndInterruption; - - if (theInterruptionType == AVAudioSessionInterruptionTypeBegan) { - nID = AFAudioSessionBeginInterruption; + NSDictionary *userInfo = notification.userInfo; + if (!userInfo || !userInfo[AVAudioSessionInterruptionTypeKey]) { + return; } - else if (theInterruptionType == AVAudioSessionInterruptionTypeEnded) { - //[[AVAudioSession sharedInstance] setActive:YES error:nil]; - nID = AFAudioSessionEndInterruption; + NSInteger interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] integerValue]; + NSInteger interruptionOption = [notification.userInfo[AVAudioSessionInterruptionOptionKey] integerValue]; + BOOL delayedSuspendedNotification = NO; + AF_LOGD("interruptionType is %d, interruptionOption is %d\n", interruptionType, interruptionOption); + + if (@available(iOS 10.3, *)) { + if ([notification.userInfo objectForKey:AVAudioSessionInterruptionWasSuspendedKey]) { + delayedSuspendedNotification = (long) [notification.userInfo objectForKey:AVAudioSessionInterruptionWasSuspendedKey] == 1; + } } + AF_LOGD("delayedSuspendedNotification is %d\n", delayedSuspendedNotification); - if (NULL != mFun) { - mFun(nID); + if (interruptionType == AVAudioSessionInterruptionTypeBegan && !delayedSuspendedNotification) { + // Playback interrupted by an incoming phone call. + if (mFun) { + mFun(AFAudioSessionBeginInterruption); + } + } + if (interruptionType == AVAudioSessionInterruptionTypeEnded && interruptionOption == AVAudioSessionInterruptionOptionShouldResume) { + if (mFun) { + mFun(AFAudioSessionEndInterruption); + } } } diff --git a/framework/render/audio/Apple/AFAudioSessionWrapper.mm b/framework/render/audio/Apple/AFAudioSessionWrapper.mm index b91b4f5bd..56aee40ef 100644 --- a/framework/render/audio/Apple/AFAudioSessionWrapper.mm +++ b/framework/render/audio/Apple/AFAudioSessionWrapper.mm @@ -7,6 +7,7 @@ #import #import "AFAudioSessionWrapper.h" #import "AFAudioSession.h" +#include std::mutex gAFRenderMutex; @@ -41,5 +42,8 @@ // call from main thread int AFAudioSessionWrapper::activeAudio() { - return [[AFAudioSession sharedInstance] activeAudio]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[AFAudioSession sharedInstance] activeAudio]; + }); + return 0; } diff --git a/framework/render/audio/Apple/AFAudioUnitRender.cpp b/framework/render/audio/Apple/AFAudioUnitRender.cpp index 9c6c66dc0..c97253abd 100644 --- a/framework/render/audio/Apple/AFAudioUnitRender.cpp +++ b/framework/render/audio/Apple/AFAudioUnitRender.cpp @@ -4,12 +4,13 @@ #define LOG_TAG "AFAudioUnitRender" +#include "AFAudioUnitRender.h" #include "AudioRenderType.h" #include -#include -#include "AFAudioUnitRender.h" -#include #include +#include +#include +#include #if TARGET_OS_OSX #define MAX_RETRY 3 @@ -453,6 +454,10 @@ namespace Cicada { void AFAudioUnitRender::device_setVolume(float gain) { + /* + * workaround setting mVolume to 0 before start can't effect immediately + */ + loopChecker(); mVolume = gain; #if AF_USE_MIX // AudioUnitSetParameter(mAudioUnit, kAUNBandEQParam_GlobalGain, kAudioUnitScope_Global, 0, mVolume, 0); diff --git a/framework/render/audio/CheaterAudioRender.h b/framework/render/audio/CheaterAudioRender.h index 264a0716b..977668d9a 100644 --- a/framework/render/audio/CheaterAudioRender.h +++ b/framework/render/audio/CheaterAudioRender.h @@ -41,6 +41,11 @@ namespace Cicada { uint64_t device_get_que_duration() override; + uint64_t device_get_ability() override + { + return 0; + } + private: af_clock mClock{}; int64_t mPCMDuration{0}; diff --git a/framework/render/audio/IAudioRender.h b/framework/render/audio/IAudioRender.h index 9fa6a5dbb..f43da1ac3 100644 --- a/framework/render/audio/IAudioRender.h +++ b/framework/render/audio/IAudioRender.h @@ -10,6 +10,9 @@ #include namespace Cicada{ + + typedef bool (*renderingFrameCB)(void *userData, IAFFrame *frame); + class IAFRenderFilter { public: virtual ~IAFRenderFilter() = default; @@ -29,7 +32,13 @@ namespace Cicada{ /** * playback interrupt */ - virtual void onInterrupt(bool interrupt) {}; + virtual bool onInterrupt(bool interrupt) + { + return false; + }; + + virtual void onUpdateTimePosition(int64_t pos) + {} }; class IAudioRender { @@ -37,6 +46,7 @@ namespace Cicada{ public: const static int FORMAT_NOT_SUPPORT = -201; + const static int OPEN_AUDIO_DEVICE_FAILED = -202; virtual ~IAudioRender() = default; @@ -164,9 +174,18 @@ namespace Cicada{ mListener = listener; } + virtual void setRenderingCb(renderingFrameCB cb, void *userData) + { + mRenderingCb = cb; + mRenderingCbUserData = userData; + } + virtual void prePause() = 0; + protected: IAudioRender *mExtFilter{}; IAudioRenderListener *mListener{}; + renderingFrameCB mRenderingCb{nullptr}; + void *mRenderingCbUserData{nullptr}; }; } diff --git a/framework/render/audio/SdlAFAudioRender.cpp b/framework/render/audio/SdlAFAudioRender.cpp index a56238410..bb2bbc668 100644 --- a/framework/render/audio/SdlAFAudioRender.cpp +++ b/framework/render/audio/SdlAFAudioRender.cpp @@ -4,9 +4,10 @@ #define LOG_TAG "SdlAFAudioRender" -#include -#include #include "SdlAFAudioRender.h" +#include +#include +#include namespace Cicada { @@ -16,6 +17,10 @@ namespace Cicada { SdlAFAudioRender::~SdlAFAudioRender() { + if (mDevID > 0) { + SDL_CloseAudioDevice(mDevID); + mDevID = 0; + } SDL_QuitSubSystem(SDL_INIT_AUDIO); if (pcmBuffer != nullptr) { @@ -71,25 +76,30 @@ namespace Cicada { bufferSize = getPCMDataLen(frame->getInfo().audio.channels, (enum AVSampleFormat) frame->getInfo().audio.format, frame->getInfo().audio.nb_samples); pcmBuffer = static_cast(malloc(bufferSize)); - if (SDL_OpenAudio(&mSpec, nullptr) < 0) { + mDevID = SDL_OpenAudioDevice(NULL, false, &mSpec, nullptr, 0); + if (mDevID == 0) { AF_LOGE("SdlAFAudioRender could not openAudio! Error: %s\n", SDL_GetError()); - return -1; + return OPEN_AUDIO_DEVICE_FAILED; } - - mDevID = 1; + mInfo = frame->getInfo().audio; SDL_PauseAudioDevice(mDevID, 0); // start play audio } - if (frame->getInfo().duration < 0 && frame->getInfo().audio.sample_rate > 0) { - frame->getInfo().duration = frame->getInfo().audio.nb_samples * 100000 / frame->getInfo().audio.sample_rate; - } + // if (frame->getInfo().duration < 0 && frame->getInfo().audio.sample_rate > 0) { + // frame->getInfo().duration = (uint64_t) frame->getInfo().audio.nb_samples * 1000000 / frame->getInfo().audio.sample_rate; + // } // mAudioFrames.push(std::move(frame)); if (getQueDuration() > 500 * 1000) { return -EAGAIN; } + if (frame->getInfo().audio != mInfo) { + return FORMAT_NOT_SUPPORT; + } + if (mFiler != nullptr) { int ret; + int origin_samples = frame->getInfo().audio.nb_samples; unique_ptr filter_frame{}; ret = mFiler->pull(filter_frame, 0); @@ -102,13 +112,17 @@ namespace Cicada { bufferSize = pcmDataLength; pcmBuffer = static_cast(realloc(pcmBuffer, bufferSize)); } - if (!mMute) { + bool rendered = false; + if (mRenderingCb) { + rendered = mRenderingCb(mRenderingCbUserData, filter_frame.get()); + } + if (!mMute && !rendered) { copyPCMData(getAVFrame(filter_frame.get()), pcmBuffer); } else { memset(pcmBuffer, 0, pcmDataLength); } SDL_QueueAudio(mDevID, pcmBuffer, pcmDataLength); - mPlayedDuration += filter_frame->getInfo().duration; + mPlayedDuration += (uint64_t) origin_samples * 1000000 / filter_frame->getInfo().audio.sample_rate; } return mFiler->push(frame, 0); @@ -122,22 +136,30 @@ namespace Cicada { bufferSize = pcmDataLength; pcmBuffer = static_cast(realloc(pcmBuffer, bufferSize)); } - if (!mMute) { + bool rendered = false; + if (mRenderingCb) { + rendered = mRenderingCb(mRenderingCbUserData, frame.get()); + } + if (!mMute && !rendered) { copyPCMData(getAVFrame(frame.get()), pcmBuffer); } else { memset(pcmBuffer, 0, pcmDataLength); } SDL_QueueAudio(mDevID, pcmBuffer, pcmDataLength); assert(frame->getInfo().duration > 0); - mPlayedDuration += frame->getInfo().duration; -// AF_LOGD("queued duration is %llu\n", getQueDuration()); + mPlayedDuration += (uint64_t) frame->getInfo().audio.nb_samples * 1000000 / frame->getInfo().audio.sample_rate; + // AF_LOGD("queued duration is %llu\n", getQueDuration()); frame = nullptr; return 0; } inline uint64_t SdlAFAudioRender::getQueDuration() { - double byteRate = mSpec.channels * 4 * mSpec.freq; + int sampleSize = 4; + if (mSpec.format == AUDIO_S16SYS) { + sampleSize = 2; + } + double byteRate = mSpec.channels * sampleSize * mSpec.freq; return (uint64_t) ((SDL_GetQueuedAudioSize(mDevID) / byteRate) * 1000000); } @@ -182,9 +204,6 @@ namespace Cicada { void SdlAFAudioRender::mute(bool bMute) { - if (bMute) { - SDL_ClearQueuedAudio(mDevID); - } mMute = bMute; } @@ -196,13 +215,10 @@ namespace Cicada { if (mFiler == nullptr) { mFiler = std::unique_ptr(new ffmpegAudioFilter(mInfo, mInfo, true)); - mFiler->init(); + mFiler->init(A_FILTER_FLAG_VOLUME | A_FILTER_FLAG_TEMPO); } mFiler->setOption("volume", std::to_string(volume), "volume"); - if (volume < 0.001f) { - SDL_ClearQueuedAudio(mDevID); - } mVolume = volume; return 0; } @@ -215,7 +231,7 @@ namespace Cicada { if (mFiler == nullptr) { mFiler = std::unique_ptr(new ffmpegAudioFilter(mInfo, mInfo, true)); - mFiler->init(); + mFiler->init(A_FILTER_FLAG_VOLUME | A_FILTER_FLAG_TEMPO); } mFiler->setOption("rate", std::to_string(speed), "atempo"); @@ -232,4 +248,9 @@ namespace Cicada { { mPlayedDuration = 0; } + + void SdlAFAudioRender::prePause() + { + // do nothing + } } diff --git a/framework/render/audio/SdlAFAudioRender.h b/framework/render/audio/SdlAFAudioRender.h index ba9802fc3..2ad7be2ef 100644 --- a/framework/render/audio/SdlAFAudioRender.h +++ b/framework/render/audio/SdlAFAudioRender.h @@ -42,6 +42,8 @@ namespace Cicada { void flush() override; + void prePause() override; + private: static void SDLAudioCallback(void *userdata, Uint8 *stream, int len); diff --git a/framework/render/audio/SdlAFAudioRender2.cpp b/framework/render/audio/SdlAFAudioRender2.cpp new file mode 100644 index 000000000..4dc13de0d --- /dev/null +++ b/framework/render/audio/SdlAFAudioRender2.cpp @@ -0,0 +1,188 @@ +// +// Created by yuyuan on 2020/12/03. +// + +#define LOG_TAG "SdlAFAudioRender2" + +#include "SdlAFAudioRender2.h" +#include +#include +#include +#include + +namespace Cicada { + + SdlAFAudioRender2::SdlAFAudioRender2() + {} + + SdlAFAudioRender2::~SdlAFAudioRender2() + { + if (mDevID > 0) { + SDL_CloseAudioDevice(mDevID); + mDevID = 0; + } + SDL_QuitSubSystem(SDL_INIT_AUDIO); + + if (mPcmBuffer) { + free(mPcmBuffer); + } + if (mMixedBuffer) { + free(mMixedBuffer); + } + } + + int SdlAFAudioRender2::init_device() + { + needFilter = true; + // init sdl audio subsystem + if (!mSdlAudioInited) { + int initRet = SDL_Init(SDL_INIT_AUDIO); + if (initRet < 0) { + AF_LOGE("SdlAFAudioRender could not initialize! Error: %s\n", SDL_GetError()); + return initRet; + } + mSdlAudioInited = true; + } + + // init sdl audio device + if (mDevID == 0) { + SDL_AudioSpec inputSpec{0}; + int format = mInputInfo.format; + if (format == AF_SAMPLE_FMT_S16 || format == AF_SAMPLE_FMT_S16P) { + inputSpec.format = AUDIO_S16SYS; + } else if (format == AF_SAMPLE_FMT_FLT || format == AF_SAMPLE_FMT_FLTP) { + inputSpec.format = AUDIO_F32SYS; + } + inputSpec.freq = mInputInfo.sample_rate; + inputSpec.channels = mInputInfo.channels; + inputSpec.silence = 0; + inputSpec.samples = mInputInfo.nb_samples; + inputSpec.userdata = this; + inputSpec.callback = nullptr; + mDevID = SDL_OpenAudioDevice(NULL, false, &inputSpec, &mSpec, 0); + if (mDevID == 0) { + AF_LOGE("SdlAFAudioRender could not openAudio! Error: %s\n", SDL_GetError()); + return OPEN_AUDIO_DEVICE_FAILED; + } + mOutputInfo.channels = mSpec.channels; + mOutputInfo.nb_samples = mSpec.samples; + mOutputInfo.sample_rate = mSpec.freq; + } + + return 0; + } + + int SdlAFAudioRender2::pause_device() + { + if (mDevID != 0) { + SDL_PauseAudioDevice(mDevID, 1); + } + return 0; + } + + int SdlAFAudioRender2::start_device() + { + if (mDevID != 0) { + SDL_PauseAudioDevice(mDevID, 0); + } + return 0; + } + + void SdlAFAudioRender2::flush_device() + { + if (mRunning) { + pause_device(); + } + if (mDevID != 0) { + SDL_ClearQueuedAudio(mDevID); + } + mPlayedDuration = 0; + if (mRunning) { + start_device(); + } + } + + void SdlAFAudioRender2::device_setVolume(float gain) + { + mOutputVolume = std::min(int(SDL_MIX_MAXVOLUME * gain), SDL_MIX_MAXVOLUME); + if (mOutputVolume == 0 && mDevID != 0) { + // mute all queued audio buffer + uint32_t queuedAudioSize = SDL_GetQueuedAudioSize(mDevID); + uint8_t *muteAudioBuffer = (uint8_t *) malloc(queuedAudioSize); + memset(muteAudioBuffer, 0, queuedAudioSize); + SDL_ClearQueuedAudio(mDevID); + SDL_QueueAudio(mDevID, muteAudioBuffer, queuedAudioSize); + free(muteAudioBuffer); + } + } + + int64_t SdlAFAudioRender2::device_get_position() + { + return mPlayedDuration - device_get_que_duration(); + } + + int SdlAFAudioRender2::device_write(unique_ptr &frame) + { + if (device_get_que_duration() > 500 * 1000) { + return -EAGAIN; + } + int pcmDataLength = getPCMDataLen(frame->getInfo().audio.channels, (enum AVSampleFormat) frame->getInfo().audio.format, + frame->getInfo().audio.nb_samples); + if (mPcmBufferSize < pcmDataLength) { + mPcmBufferSize = pcmDataLength; + mPcmBuffer = static_cast(realloc(mPcmBuffer, mPcmBufferSize)); + } + bool rendered = false; + if (mRenderingCb) { + rendered = mRenderingCb(mRenderingCbUserData, frame.get()); + } + if (!rendered) { + copyPCMData(getAVFrame(frame.get()), mPcmBuffer); + } else { + memset(mPcmBuffer, 0, pcmDataLength); + } + if (mOutputVolume < 128 && !rendered) { + if (mMixedBufferSize < mPcmBufferSize) { + mMixedBufferSize = mPcmBufferSize; + mMixedBuffer = static_cast(realloc(mMixedBuffer, mMixedBufferSize)); + } + memset(mMixedBuffer, 0, pcmDataLength); + SDL_MixAudioFormat(mMixedBuffer, mPcmBuffer, mSpec.format, pcmDataLength, mOutputVolume); + SDL_QueueAudio(mDevID, mMixedBuffer, pcmDataLength); + } else { + SDL_QueueAudio(mDevID, mPcmBuffer, pcmDataLength); + } + assert(frame->getInfo().duration > 0); + if (mListener) { + mListener->onUpdateTimePosition(frame->getInfo().timePosition); + } + mPlayedDuration += (uint64_t) frame->getInfo().audio.nb_samples * 1000000 / frame->getInfo().audio.sample_rate; + //AF_LOGD("queued duration is %llu\n", getQueDuration()); + frame = nullptr; + return 0; + } + + uint64_t SdlAFAudioRender2::device_get_que_duration() + { + int sampleSize = 4; + if (mSpec.format == AUDIO_S16SYS) { + sampleSize = 2; + } + double byteRate = mOutputInfo.channels * sampleSize * mOutputInfo.sample_rate; + return (uint64_t)((SDL_GetQueuedAudioSize(mDevID) / byteRate) * 1000000); + } + + int SdlAFAudioRender2::loopChecker() + { + return 0; + } + + void SdlAFAudioRender2::device_preClose() + {} + + uint64_t SdlAFAudioRender2::device_get_ability() + { + return 0; + } + +}// namespace Cicada diff --git a/framework/render/audio/SdlAFAudioRender2.h b/framework/render/audio/SdlAFAudioRender2.h new file mode 100644 index 000000000..857d04627 --- /dev/null +++ b/framework/render/audio/SdlAFAudioRender2.h @@ -0,0 +1,46 @@ +// +// Created by yuyuan on 2020/12/01. +// + +#ifndef FRAMEWORK_SDLAFAUDIORENDER2_H +#define FRAMEWORK_SDLAFAUDIORENDER2_H + +#include "filterAudioRender.h" +#include + +using namespace std; + +namespace Cicada { + class SdlAFAudioRender2 : public filterAudioRender { + public: + SdlAFAudioRender2(); + ~SdlAFAudioRender2() override; + + private: + int init_device() override; + int pause_device() override; + int start_device() override; + void flush_device() override; + void device_setVolume(float gain) override; + int64_t device_get_position() override; + int device_write(unique_ptr &frame) override; + uint64_t device_get_que_duration() override; + int loopChecker() override; + void device_preClose() override; + uint64_t device_get_ability() override; + + private: + bool mSdlAudioInited = false; + SDL_AudioDeviceID mDevID{0}; + uint64_t mPlayedDuration = 0; + uint8_t *mPcmBuffer = nullptr; + int mPcmBufferSize = 0; + uint8_t *mMixedBuffer = nullptr; + int mMixedBufferSize = 0; + std::atomic_int mOutputVolume{0}; + SDL_AudioSpec mSpec{0}; + }; +}// namespace Cicada + + +#endif//FRAMEWORK_SDLAFAUDIORENDER2_H diff --git a/framework/render/audio/filterAudioRender.cpp b/framework/render/audio/filterAudioRender.cpp index 1ba1a3cdf..54926e61a 100644 --- a/framework/render/audio/filterAudioRender.cpp +++ b/framework/render/audio/filterAudioRender.cpp @@ -3,12 +3,15 @@ // #define LOG_TAG "AudioRender" +#include + +#include "filterAudioRender.h" +#include +#include #include +#include #include #include -#include -#include -#include "filterAudioRender.h" #include namespace Cicada { @@ -23,7 +26,7 @@ namespace Cicada { filterAudioRender::~filterAudioRender() { unique_lock lock(mFrameQueMutex); - mState = State::state_uninit; + mRunning = false; mFrameQueCondition.notify_one(); if (mRenderThread) { @@ -48,21 +51,34 @@ namespace Cicada { mOutputInfo.nb_samples = 0; int ret = init_device(); + uint64_t device_ability = device_get_ability(); + + if (!(device_ability & A_FILTER_FLAG_TEMPO)) { + mFilterFlags |= A_FILTER_FLAG_TEMPO; + } + + if (!(device_ability & A_FILTER_FLAG_VOLUME)) { + mFilterFlags |= A_FILTER_FLAG_VOLUME; + } + if (ret < 0) { AF_LOGE("subInit failed , ret = %d ", ret); return ret; } + if (mOutputInfo.nb_samples > 0) { + float rate = (float) mInputInfo.sample_rate / mOutputInfo.sample_rate; + float nb_samples = mOutputInfo.nb_samples /= rate; + mOutputInfo.nb_samples = nb_samples; + } if (needFilter) { mFilter = std::unique_ptr(filterFactory::createAudioFilter(mInputInfo, mOutputInfo, mUseActiveFilter)); - ret = mFilter->init(); + ret = mFilter->init(mFilterFlags); if (ret < 0) { return ret; } } - - mState = State::state_init; mRenderThread = std::unique_ptr(NEW_AF_THREAD(renderLoop)); return 0; } @@ -82,7 +98,8 @@ namespace Cicada { } if (mOutputInfo.nb_samples == 0) { - mOutputInfo.nb_samples = frame->getInfo().audio.nb_samples; + float rate = (float) mInputInfo.sample_rate / (float) mOutputInfo.sample_rate; + mOutputInfo.nb_samples = frame->getInfo().audio.nb_samples / rate; } mFrameQue.push(move(frame)); @@ -114,10 +131,11 @@ namespace Cicada { void filterAudioRender::mute(bool bMute) { + mMute = bMute; if (bMute) { device_setVolume(0); } else { - device_setVolume(mVolume); + device_setVolume(mVolume * mVolume * mVolume); } } @@ -125,7 +143,10 @@ namespace Cicada { { if (mVolume != volume) { mVolume = volume; - mVolumeChanged = true; + + if (!(mFilterFlags & A_FILTER_FLAG_VOLUME)) { + device_setVolume(mVolume * mVolume * mVolume); + } } return 0; @@ -133,11 +154,10 @@ namespace Cicada { int filterAudioRender::setSpeed(float speed) { + assert(mFilterFlags & A_FILTER_FLAG_TEMPO); if (mSpeed != speed) { mSpeed = speed; - mSpeedChanged = true; } - return 0; } @@ -154,42 +174,28 @@ namespace Cicada { int filterAudioRender::pauseThread() { - if (mState.load() != state_running) { - AF_LOGE("Pause occur error state %d", mState.load()); - return -1; - } - - { - unique_lock lock(mFrameQueMutex); - mState = state_pause; - mFrameQueCondition.notify_all(); + mRunning = false; + if (mRenderThread) { + mRenderThread->pause(); } - - mRenderThread->pause(); return 0; } int filterAudioRender::startThread() { - if (mState != State::state_init && mState != State::state_pause) { - AF_LOGE("Start occur error state %d", mState.load()); - return -1; + mRunning = true; + if (mRenderThread) { + mRenderThread->start(); } - - mState = state_running; - mRenderThread->start(); return 0; } void filterAudioRender::flush() { - State currentState = mState; - - if (currentState == state_running) { - pauseThread(); - } + bool running = mRunning; + pauseThread(); while (!mFrameQue.empty()) { mFrameQue.pop(); @@ -205,38 +211,42 @@ namespace Cicada { mSpeedDeltaDuration = 0; mRenderFrame = nullptr; - if (currentState == state_running) { + if (running) { startThread(); } } int filterAudioRender::renderLoop() { - if (mState != State::state_running) { + if (!mRunning) { return 0; } if (mRenderFrame == nullptr) { mRenderFrame = getFrame(); } + int ret = 0; while (mRenderFrame != nullptr) { - if (mState != State::state_running) { + if (!mRunning) { return 0; } - if (mSpeedChanged) { + + float speed = mSpeed.load(); + if (speed != mFilterSpeed) { applySpeed(); - mSpeedChanged = false; + mFilterSpeed = speed; } - if (mVolumeChanged) { + float volume = mVolume.load(); + if (volume != mFilterVolume) { applyVolume(); - mVolumeChanged = false; + mFilterVolume = volume; } loopChecker(); int nb_samples = mRenderFrame->getInfo().audio.nb_samples; - int ret = device_write(mRenderFrame); + ret = device_write(mRenderFrame); if (ret == -EAGAIN) { @@ -250,7 +260,7 @@ namespace Cicada { } mRenderFrame = getFrame(); } - if (mFrameQue.empty()) { + if (mFrameQue.empty() || ret == -EAGAIN) { // TODO: only on Android xiaomi miui 9? mMaxQueSize = std::min(mMaxQueSize.load() + 1, MAX_INPUT_BUFFER_COUNT); @@ -298,7 +308,7 @@ namespace Cicada { if (mFilter == nullptr) { mFilter = std::unique_ptr(filterFactory::createAudioFilter(mInputInfo, mOutputInfo, mUseActiveFilter)); mFilter->setOption("rate", AfString::to_string(mSpeed), "atempo"); - int ret = mFilter->init(); + int ret = mFilter->init(mFilterFlags); if (ret < 0) { return ret; @@ -320,7 +330,7 @@ namespace Cicada { if (mFilter == nullptr) { mFilter = std::unique_ptr(filterFactory::createAudioFilter(mInputInfo, mOutputInfo, mUseActiveFilter)); mFilter->setOption("volume", AfString::to_string(gain), "volume"); - int ret = mFilter->init(); + int ret = mFilter->init(mFilterFlags); if (ret < 0) { return ret; @@ -337,8 +347,17 @@ namespace Cicada { } } - float gain = volume * volume * volume; - device_setVolume(gain); + if(!mMute) { + float gain = volume * volume * volume; + device_setVolume(gain); + } return 0; } + void filterAudioRender::prePause() + { + if (mRenderThread) { + mRenderThread->prePause(); + } + device_preClose(); + } } diff --git a/framework/render/audio/filterAudioRender.h b/framework/render/audio/filterAudioRender.h index dfee3fd0c..6654d3aa5 100644 --- a/framework/render/audio/filterAudioRender.h +++ b/framework/render/audio/filterAudioRender.h @@ -14,13 +14,6 @@ namespace Cicada { class filterAudioRender : public IAudioRender { - public: - enum State { - state_uninit, // 0:constructor - state_init, // 1:inited: Demuxer output streams, - state_pause, // 2:pause : - state_running, // 3:running - }; public: filterAudioRender(); @@ -47,6 +40,8 @@ namespace Cicada { void flush() override; + void prePause() override; + private: virtual int init_device() = 0; @@ -72,6 +67,11 @@ namespace Cicada { return 0; }; + virtual void device_preClose() + {} + + virtual uint64_t device_get_ability() = 0; + private: int renderLoop(); @@ -90,15 +90,15 @@ namespace Cicada { IAFFrame::audioInfo mInputInfo{}; IAFFrame::audioInfo mOutputInfo{}; bool needFilter = false; - atomic mState{State::state_uninit}; + atomic mRunning{false}; private: - volatile std::atomic mSpeed{1}; - std::atomic_bool mSpeedChanged{false}; + std::atomic mFilterSpeed{1}; std::atomic mSpeedDeltaDuration{0}; volatile std::atomic mVolume{1}; - std::atomic_bool mVolumeChanged{false}; + std::atomic mFilterVolume{1}; + volatile std::atomic_bool mMute{false}; std::unique_ptr mFilter{}; std::mutex mFrameQueMutex; std::condition_variable mFrameQueCondition; @@ -106,6 +106,7 @@ namespace Cicada { std::unique_ptr mRenderFrame{nullptr}; bool mUseActiveFilter{false}; std::atomic_int mMaxQueSize{2}; + uint64_t mFilterFlags{}; protected: std::unique_ptr mRenderThread = nullptr; diff --git a/framework/render/renderFactory.cpp b/framework/render/renderFactory.cpp index a562965b4..9cbcf470b 100644 --- a/framework/render/renderFactory.cpp +++ b/framework/render/renderFactory.cpp @@ -8,7 +8,8 @@ #endif #ifdef __APPLE__ - #include +#include +#include #endif @@ -17,7 +18,7 @@ #endif #ifdef ENABLE_SDL -#include "audio/SdlAFAudioRender.h" +#include "audio/SdlAFAudioRender2.h" #include "video/SdlAFVideoRender.h" #endif @@ -28,12 +29,6 @@ #endif -#ifdef ENABLE_CHEAT_RENDER - -#include "video/CheaterVideoRender.h" - -#endif - #include "audio/audioRenderPrototype.h" #ifdef ENABLE_CHEAT_RENDER @@ -42,6 +37,8 @@ #endif +#include "video/DummyVideoRender.h" + using namespace Cicada; std::unique_ptr AudioRenderFactory::create() @@ -59,22 +56,32 @@ std::unique_ptr AudioRenderFactory::create() return std::unique_ptr(new AudioTrackRender()); #endif #ifdef __APPLE__ - return std::unique_ptr(new AFAudioUnitRender()); -#endif -#ifdef ENABLE_SDL - return std::unique_ptr(new SdlAFAudioRender()); + return std::unique_ptr(new AFAudioQueueRender()); +#elif defined(ENABLE_SDL) + return std::unique_ptr(new SdlAFAudioRender2()); #endif return nullptr; } -std::unique_ptr videoRenderFactory::create() +unique_ptr videoRenderFactory::create(uint64_t flags) { + if (flags & IVideoRender::FLAG_DUMMY) { + return std::unique_ptr(new DummyVideoRender()); + } + + if (flags & IVideoRender::FLAG_HDR) { +#if defined(__APPLE__) && !defined(ENABLE_SDL) + return std::unique_ptr(new AVFoundationVideoRender()); +#endif + return nullptr; + } + #if defined(GLRENDER) return std::unique_ptr(new GLRender()); #elif defined(ENABLE_SDL) return std::unique_ptr(new SdlAFVideoRender()); #elif defined(ENABLE_CHEAT_RENDER) - return std::unique_ptr(new CheaterVideoRender()); + return std::unique_ptr(new DummyVideoRender()); #endif return nullptr; } diff --git a/framework/render/renderFactory.h b/framework/render/renderFactory.h index a3bd03242..b17fb5429 100644 --- a/framework/render/renderFactory.h +++ b/framework/render/renderFactory.h @@ -7,7 +7,7 @@ #include "render/audio/IAudioRender.h" #include "render/video/IVideoRender.h" -namespace Cicada{ +namespace Cicada { class AudioRenderFactory { public: static std::unique_ptr create(); @@ -15,8 +15,8 @@ namespace Cicada{ class videoRenderFactory { public: - static std::unique_ptr create(); + static std::unique_ptr create(uint64_t flags = 0); }; -} +}// namespace Cicada -#endif //CICADA_PLAYER_RENDERFACTORY_H +#endif//CICADA_PLAYER_RENDERFACTORY_H diff --git a/framework/render/video/AFActiveVideoRender.cpp b/framework/render/video/AFActiveVideoRender.cpp index 7e7516686..2cfc0c412 100644 --- a/framework/render/video/AFActiveVideoRender.cpp +++ b/framework/render/video/AFActiveVideoRender.cpp @@ -2,19 +2,148 @@ // Created by moqi on 2019-08-02. // +#define LOG_TAG "AFActiveVideoRender" #include "AFActiveVideoRender.h" #include "render/video/vsync/timedVSync.h" +#include + +#define MAX_FRAME_QUEUE_SIZE 100 +#define MAX_IN_SIZE 3 +using namespace std; AFActiveVideoRender::AFActiveVideoRender(float Hz) + : mInputQueue(MAX_FRAME_QUEUE_SIZE) { - mVSync = std::unique_ptr(new timedVSync( - [this](int64_t tick) { - onVSync(tick); - }, Hz)); + mVSync = std::unique_ptr(new timedVSync(*this, Hz)); mVSync->start(); } +AFActiveVideoRender::~AFActiveVideoRender() +{ + if (mRendingFrame) { + mRendingFrame->setDiscard(true); + } + while (mInputQueue.size() > 0) { + dropFrame(); + } +} + int AFActiveVideoRender::setHz(float Hz) { return mVSync->setHz(Hz); } +void AFActiveVideoRender::setSpeed(float speed) +{ + mRenderClock.setSpeed(speed); +} +int AFActiveVideoRender::renderFrame(std::unique_ptr &frame) +{ + if (frame == nullptr) { + // flush the mRendingFrame, when mInputQueue is empty + mNeedFlushSize = std::max(mInputQueue.size(), (size_t) 1); + return 0; + } + + if (mNeedFlushSize > 0) { + // FIXME: can't drop this frame + AF_LOGW("renderFrame before flush finish\n"); + } + + if (mInputQueue.size() >= MAX_FRAME_QUEUE_SIZE) { + AF_LOGE("too many frames...\n"); + return 0; + } + // std::unique_lock locker(mFrameMutex); + mInputQueue.push(frame.release()); + return 0; +} + +void AFActiveVideoRender::dropFrame() +{ + int64_t framePts = mInputQueue.front()->getInfo().pts; + AF_LOGI("drop a frame pts = %lld ", framePts); + mInputQueue.front()->setDiscard(true); + delete mInputQueue.front(); + mInputQueue.pop(); + + if (mRenderResultCallback != nullptr) { + mRenderResultCallback(framePts, false); + } +} + +void AFActiveVideoRender::calculateFPS(int64_t tick) +{ + if ((tick / uint64_t(mVSync->getHz())) != mRendertimeS) { + mRendertimeS = tick / uint64_t(mVSync->getHz()); + AF_LOGD("video fps is %llu\n", mRenderCount); + mFps = mRenderCount; + mRenderCount = 0; + } +} +int AFActiveVideoRender::onVSync(int64_t tick) +{ + while (mNeedFlushSize > 0) { + if (mRendingFrame) { + mRendingFrame->setDiscard(true); + mRendingFrame = nullptr; + } + if (mInputQueue.empty()) { + break; + } + dropFrame(); + mNeedFlushSize--; + } + mNeedFlushSize = 0; + + if (mInputQueue.size() >= MAX_IN_SIZE) { + while (mInputQueue.size() >= MAX_IN_SIZE) { + dropFrame(); + } + mRendingFrame = static_cast>(mInputQueue.front()); + mInputQueue.pop(); + mRenderClock.set(mRendingFrame->getInfo().pts); + mRenderClock.start(); + } + + if (mInputQueue.empty() && mRendingFrame == nullptr) { + calculateFPS(tick); + return 0; + } + + if (mRendingFrame == nullptr) { + mRendingFrame = static_cast>(mInputQueue.front()); + mInputQueue.pop(); + } + + if (mRenderClock.get() == 0) { + mRenderClock.set(mRendingFrame->getInfo().pts); + mRenderClock.start(); + } + + int64_t late = mRendingFrame->getInfo().pts - mRenderClock.get(); + + if (llabs(late) > 100000) { + mRenderClock.set(mRendingFrame->getInfo().pts); + } else if (late - mVSync->getPeriod() * mRenderClock.getSpeed() > 0) { + // AF_LOGD("mVSyncPeriod is %lld\n", mVSync->getPeriod()); + // AF_LOGD("mRenderClock.get() is %lld\n", mRenderClock.get()); + // AF_LOGD("frame->getInfo().pts is %lld\n", mRendingFrame->getInfo().pts); + calculateFPS(tick); + return 0; + } + + mFrameInfo = mRendingFrame->getInfo(); + if (deviceRenderFrame(mRendingFrame.get())) { + mRenderCount++; + } + mRendingFrame = nullptr; + calculateFPS(tick); + + if (mRenderResultCallback != nullptr) { + mRenderResultCallback(mFrameInfo.pts, true); + } + if (mListener) { + mListener->onFrameInfoUpdate(mFrameInfo); + } + return 0; +} diff --git a/framework/render/video/AFActiveVideoRender.h b/framework/render/video/AFActiveVideoRender.h index b04561104..5cbb62e65 100644 --- a/framework/render/video/AFActiveVideoRender.h +++ b/framework/render/video/AFActiveVideoRender.h @@ -8,24 +8,63 @@ #include "IVideoRender.h" #include "render/video/vsync/IVSync.h" +#include +#include +#include +#include -class AFActiveVideoRender : public IVideoRender { +class AFActiveVideoRender : public IVideoRender, private IVSync::Listener { public: explicit AFActiveVideoRender(float Hz = 60); - ~AFActiveVideoRender() override = default; + ~AFActiveVideoRender() override; - virtual void onVSync(int64_t tick) = 0; + void setSpeed(float speed) final; + + int renderFrame(std::unique_ptr &frame) final; + + float getRenderFPS() final + { + return mFps; + } + +private: + int VSyncOnInit() override + { + return 0; + } + + int onVSync(int64_t tick) override; + + void VSyncOnDestroy() override + {} virtual int setHz(float Hz); +private: + virtual bool deviceRenderFrame(IAFFrame *frame) = 0; + + +private: + void dropFrame(); + void calculateFPS(int64_t tick); -protected: +private: std::unique_ptr mVSync{}; + Cicada::SpscQueue mInputQueue; + // std::mutex mFrameMutex; + // std::queue> mInputQueue; + af_scalable_clock mRenderClock; + IAFFrame::AFFrameInfo mFrameInfo{}; + uint64_t mRenderCount{}; + uint64_t mRendertimeS{0}; + uint8_t mFps{0}; + size_t mNeedFlushSize{0}; + std::unique_ptr mRendingFrame{nullptr}; }; -#endif //FRAMEWORK_AFACTIVEVIDEORENDER_H +#endif//FRAMEWORK_AFACTIVEVIDEORENDER_H diff --git a/framework/render/video/AVFoundation/AVFoundationVideoRender.h b/framework/render/video/AVFoundation/AVFoundationVideoRender.h new file mode 100644 index 000000000..99e5cbcbd --- /dev/null +++ b/framework/render/video/AVFoundation/AVFoundationVideoRender.h @@ -0,0 +1,44 @@ +// +// Created by pingkai on 2020/11/27. +// + +#ifndef CICADAMEDIA_AVFOUNDATIONVIDEORENDER_H +#define CICADAMEDIA_AVFOUNDATIONVIDEORENDER_H + +#include "../AFActiveVideoRender.h" +#include "../IVideoRender.h" +#include "../vsync/IVSync.h" +#include +#include +class DisplayLayerImpl; +class AVFoundationVideoRender : public AFActiveVideoRender { +public: + AVFoundationVideoRender(); + ~AVFoundationVideoRender() override; + int init() override; + + int clearScreen() override; + + int setRotate(Rotate rotate) override; + + int setFlip(Flip flip) override; + + int setScale(Scale scale) override; + + int setDisPlay(void *view) override; + + uint64_t getFlags() override + { + return FLAG_HDR; + } + +private: + bool deviceRenderFrame(IAFFrame *frame) override; + +private: + std::unique_ptr mRender; + Cicada::pixelBufferConvertor *mConvertor{nullptr}; +}; + + +#endif//CICADAMEDIA_AVFOUNDATIONVIDEORENDER_H diff --git a/framework/render/video/AVFoundation/AVFoundationVideoRender.mm b/framework/render/video/AVFoundation/AVFoundationVideoRender.mm new file mode 100644 index 000000000..e1447f141 --- /dev/null +++ b/framework/render/video/AVFoundation/AVFoundationVideoRender.mm @@ -0,0 +1,74 @@ +// +// Created by pingkai on 2020/11/27. +// +#define LOG_TAG "AVFoundationVideoRender" +#include "AVFoundationVideoRender.h" +#include "DisplayLayerImpl-interface.h" +#include +#include + +using namespace std; +using namespace Cicada; + +AVFoundationVideoRender::AVFoundationVideoRender() +{ + mRender = static_cast>(new DisplayLayerImpl()); + mRender->init(); + mRender->createLayer(); +} +AVFoundationVideoRender::~AVFoundationVideoRender() +{ + delete mConvertor; +} +int AVFoundationVideoRender::init() +{ + return 0; +} +int AVFoundationVideoRender::clearScreen() +{ + mRender->clearScreen(); + return 0; +} +int AVFoundationVideoRender::setRotate(IVideoRender::Rotate rotate) +{ + return 0; +} +int AVFoundationVideoRender::setFlip(IVideoRender::Flip flip) +{ + return 0; +} +int AVFoundationVideoRender::setScale(IVideoRender::Scale scale) +{ + mRender->setScale(scale); + return 0; +} + +int AVFoundationVideoRender::setDisPlay(void *view) +{ + mRender->setDisplay(view); + return 0; +} +bool AVFoundationVideoRender::deviceRenderFrame(IAFFrame *frame) +{ + bool rendered = false; + bool converted = false; + if (frame) { + if (mConvertor == nullptr) { + mConvertor = new pixelBufferConvertor(); + } + if (dynamic_cast(frame) == nullptr) { + IAFFrame *pbafFrame = mConvertor->convert(frame); + frame = pbafFrame; + converted = true; + } + } + if (frame) { + mRender->renderFrame(frame); + rendered = true; + } + if (converted) { + delete frame; + } + + return rendered; +} diff --git a/framework/render/video/AVFoundation/DisplayLayerImpl-interface.h b/framework/render/video/AVFoundation/DisplayLayerImpl-interface.h new file mode 100644 index 000000000..bb7aa7e23 --- /dev/null +++ b/framework/render/video/AVFoundation/DisplayLayerImpl-interface.h @@ -0,0 +1,25 @@ +// +// Created by pingkai on 2020/11/27. +// + +#ifndef CICADAMEDIA_DISPLAYLAYERIMPL_H +#define CICADAMEDIA_DISPLAYLAYERIMPL_H + +#include "../IVideoRender.h" +#import +class DisplayLayerImpl { +public: + DisplayLayerImpl(); + ~DisplayLayerImpl(); + void init(); + int createLayer(); + void setDisplay(void *display); + int renderFrame(IAFFrame *frame); + void clearScreen(); + void setScale(IVideoRender::Scale scale); + +private: + void *renderHandle{nullptr}; +}; + +#endif//CICADAMEDIA_DISPLAYLAYERIMPL_H diff --git a/framework/render/video/AVFoundation/SampleDisplayLayerRender.h b/framework/render/video/AVFoundation/SampleDisplayLayerRender.h new file mode 100644 index 000000000..ea6d72337 --- /dev/null +++ b/framework/render/video/AVFoundation/SampleDisplayLayerRender.h @@ -0,0 +1,23 @@ +// +// Created by pingkai on 2020/11/27. +// + +#ifndef CICADAMEDIA_SAMPLEDISPLAYLAYERRENDER_H +#define CICADAMEDIA_SAMPLEDISPLAYLAYERRENDER_H + +#import +#import + +@interface SampleDisplayLayerRender : NSObject { + // AVSampleBufferDisplayLayer* displayLayer; +} +@property(nonatomic, strong) AVSampleBufferDisplayLayer *displayLayer; + +- (int)createLayer; + +- (void)setDisplay:(void *)layer; + +@end + + +#endif//CICADAMEDIA_SAMPLEDISPLAYLAYERRENDER_H diff --git a/framework/render/video/AVFoundation/SampleDisplayLayerRender.mm b/framework/render/video/AVFoundation/SampleDisplayLayerRender.mm new file mode 100644 index 000000000..f1965137a --- /dev/null +++ b/framework/render/video/AVFoundation/SampleDisplayLayerRender.mm @@ -0,0 +1,214 @@ +// +// Created by pingkai on 2020/11/27. +// + +#import "SampleDisplayLayerRender.h" +#include "DisplayLayerImpl-interface.h" +#include +#if TARGET_OS_IPHONE +#import +#endif + +@implementation SampleDisplayLayerRender { + CALayer *parentLayer; + AVLayerVideoGravity videoGravity; +} +DisplayLayerImpl::DisplayLayerImpl() +{} +DisplayLayerImpl::~DisplayLayerImpl() +{ + if (renderHandle) { + CFRelease(renderHandle); + } +} +void DisplayLayerImpl::init() +{ + SampleDisplayLayerRender *object = [[SampleDisplayLayerRender alloc] init]; + renderHandle = (__bridge_retained void *) object; +} +int DisplayLayerImpl::createLayer() +{ + return [(__bridge id) renderHandle createLayer]; +} + +int DisplayLayerImpl::renderFrame(IAFFrame *frame) +{ + auto *pbafFrame = dynamic_cast(frame); + if (pbafFrame) { + [(__bridge id) renderHandle displayPixelBuffer:pbafFrame->getPixelBuffer()]; + } + + return 0; +} +void DisplayLayerImpl::setDisplay(void *display) +{ + return [(__bridge id) renderHandle setDisplay:display]; +} +void DisplayLayerImpl::clearScreen() +{ + [(__bridge id) renderHandle clearScreen]; +} + +void DisplayLayerImpl::setScale(IVideoRender::Scale scale) +{ + [(__bridge id) renderHandle setVideoScale:scale]; +} + +- (instancetype)init +{ + videoGravity = AVLayerVideoGravityResizeAspect; + parentLayer = nil; + return self; +} + +- (void)setDisplay:(void *)layer +{ + if (layer != (__bridge void *) parentLayer) { + parentLayer = (__bridge CALayer *) layer; + dispatch_async(dispatch_get_main_queue(), ^{ + if (!self.displayLayer) { + self.displayLayer = [AVSampleBufferDisplayLayer layer]; + self.displayLayer.videoGravity = videoGravity; + } + [parentLayer addSublayer:self.displayLayer]; + parentLayer.masksToBounds = YES; + self.displayLayer.frame = parentLayer.bounds; + [parentLayer addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew context:nil]; + }); + } +} + +- (void)setVideoScale:(IVideoRender::Scale)scale +{ + switch (scale) { + case IVideoRender::Scale_AspectFit: + videoGravity = AVLayerVideoGravityResizeAspect; + break; + case IVideoRender::Scale_AspectFill: + videoGravity = AVLayerVideoGravityResizeAspectFill; + break; + case IVideoRender::Scale_Fill: + videoGravity = AVLayerVideoGravityResize; + break; + default: + return; + } + + if (self.displayLayer) { + dispatch_async(dispatch_get_main_queue(), ^{ + self.displayLayer.videoGravity = videoGravity; + CGRect bounds = self.displayLayer.bounds; + self.displayLayer.bounds = CGRectZero; + self.displayLayer.bounds = bounds; + }); + } +} + +- (void)clearScreen +{ + [self.displayLayer flushAndRemoveImage]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if ([keyPath isEqualToString:@"bounds"]) { + CGRect bounds = [change[NSKeyValueChangeNewKey] CGRectValue]; + self.displayLayer.frame = CGRectMake(0, 0, bounds.size.width, bounds.size.height); + } +} + +- (int)createLayer +{ + NSLog(@"createLayer"); + return 0; +}; + +#if 0 +- (CVPixelBufferRef)copyPixelBuffer:(CVPixelBufferRef)pixelBuffer +{ + CVPixelBufferRef copyBuffer = nil; + NSDictionary *pixelAttributes = @{(id) kCVPixelBufferIOSurfacePropertiesKey: @{}}; + + + size_t width = CVPixelBufferGetWidth(pixelBuffer); + size_t height = CVPixelBufferGetHeight(pixelBuffer); + OSType pix_fmt = CVPixelBufferGetPixelFormatType(pixelBuffer); + + CVReturn result = + CVPixelBufferCreate(kCFAllocatorDefault, width, height, pix_fmt, (__bridge CFDictionaryRef)(pixelAttributes), ©Buffer); + + if (result != kCVReturnSuccess) { + return nullptr; + } + uint8_t *DestPlane; + uint8_t *SrcPlane; + size_t line_size; + + for (int j = 0; j < CVPixelBufferGetPlaneCount(pixelBuffer); j++) { + CVPixelBufferLockBaseAddress(copyBuffer, j); + CVPixelBufferLockBaseAddress(pixelBuffer, j); + DestPlane = (uint8_t *) CVPixelBufferGetBaseAddressOfPlane(copyBuffer, j); + SrcPlane = (uint8_t *) CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, j); + line_size = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, j); + height = CVPixelBufferGetHeightOfPlane(pixelBuffer, j); + width = CVPixelBufferGetWidthOfPlane(pixelBuffer, j); + + + for (int i = 0; i < height; i++) { + memcpy(DestPlane + i * line_size, SrcPlane + i * line_size, line_size); + } + CVPixelBufferUnlockBaseAddress(copyBuffer, j); + CVPixelBufferUnlockBaseAddress(pixelBuffer, j); + } + + return copyBuffer; +} +#endif + +- (void)displayPixelBuffer:(CVPixelBufferRef)pixelBuffer +{ + if (!pixelBuffer || !self.displayLayer) { + return; + } + + CMSampleTimingInfo timing = {kCMTimeInvalid, kCMTimeInvalid, kCMTimeInvalid}; + + CMVideoFormatDescriptionRef videoInfo = NULL; + OSStatus result = CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo); + NSParameterAssert(result == 0 && videoInfo != NULL); + + CMSampleBufferRef sampleBuffer = NULL; + result = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, true, NULL, NULL, videoInfo, &timing, &sampleBuffer); + NSParameterAssert(result == 0 && sampleBuffer != NULL); + CFRelease(videoInfo); + + CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, YES); + CFMutableDictionaryRef dict = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(attachments, 0); + CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue); + + if (self.displayLayer.status == AVQueuedSampleBufferRenderingStatusFailed) { + [self.displayLayer flush]; + } + [self.displayLayer enqueueSampleBuffer:sampleBuffer]; + CFRelease(sampleBuffer); +} + +- (void)dealloc +{ + if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) { + if (self.displayLayer) { + [self.displayLayer removeFromSuperlayer]; + [parentLayer removeObserver:self forKeyPath:@"bounds" context:nil]; + } + } else { + // FIXME: use async + dispatch_sync(dispatch_get_main_queue(), ^{ + if (self.displayLayer) { + [self.displayLayer removeFromSuperlayer]; + [parentLayer removeObserver:self forKeyPath:@"bounds" context:nil]; + } + }); + } +} + +@end diff --git a/framework/render/video/CheaterVideoRender.cpp b/framework/render/video/CheaterVideoRender.cpp index be8616219..fd1f50f7e 100644 --- a/framework/render/video/CheaterVideoRender.cpp +++ b/framework/render/video/CheaterVideoRender.cpp @@ -18,11 +18,6 @@ int Cicada::CheaterVideoRender::init() return 0; } -void Cicada::CheaterVideoRender::setVideoRotate(IVideoRender::Rotate rotate) -{ - -} - void Cicada::CheaterVideoRender::setWindowSize(int windWith, int mWindHeight) { IVideoRender::setWindowSize(windWith, mWindHeight); diff --git a/framework/render/video/CheaterVideoRender.h b/framework/render/video/CheaterVideoRender.h index 2eafb3cca..71458b17c 100644 --- a/framework/render/video/CheaterVideoRender.h +++ b/framework/render/video/CheaterVideoRender.h @@ -19,8 +19,6 @@ namespace Cicada { int init() override; - void setVideoRotate(Rotate rotate) override; - void setWindowSize(int windWith, int mWindHeight) override; int clearScreen() override; diff --git a/framework/render/video/DummyVideoRender.h b/framework/render/video/DummyVideoRender.h new file mode 100644 index 000000000..a272e321b --- /dev/null +++ b/framework/render/video/DummyVideoRender.h @@ -0,0 +1,50 @@ +// +// Created by pingkai on 2021/2/2. +// + +#ifndef CICADAMEDIA_DUMMYVIDEORENDER_H +#define CICADAMEDIA_DUMMYVIDEORENDER_H + +#include "AFActiveVideoRender.h" + +namespace Cicada { + class DummyVideoRender : public AFActiveVideoRender { + + public: + ~DummyVideoRender() override = default; + + bool deviceRenderFrame(IAFFrame *frame) override + { + return true; + } + + int init() override + { + return 0; + } + int clearScreen() override + { + return 0; + } + int setRotate(Rotate rotate) override + { + return 0; + } + int setFlip(Flip flip) override + { + return 0; + } + int setScale(Scale scale) override + { + return 0; + } + + uint64_t getFlags() override + { + return FLAG_DUMMY; + } + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_DUMMYVIDEORENDER_H diff --git a/framework/render/video/IVideoRender.h b/framework/render/video/IVideoRender.h index 15ec18f45..4677ca923 100644 --- a/framework/render/video/IVideoRender.h +++ b/framework/render/video/IVideoRender.h @@ -12,6 +12,8 @@ class IVideoRender { public: + static const uint64_t FLAG_HDR = (1 << 0); + static const uint64_t FLAG_DUMMY = (1 << 1); enum Rotate { Rotate_None = 0, @@ -33,6 +35,20 @@ class IVideoRender { Scale_Fill }; + static Rotate getRotate(int value) + { + switch (value) { + case 90: + return Rotate_90; + case 180: + return Rotate_180; + case 270: + return Rotate_270; + default: + return Rotate_None; + } + }; + class ScreenShotInfo { public: enum Format { @@ -56,7 +72,6 @@ class IVideoRender { } }; -public: class IVideoRenderFilter { public: virtual ~IVideoRenderFilter() = default; @@ -68,6 +83,13 @@ class IVideoRender { }; + class IVideoRenderListener { + public: + virtual void onFrameInfoUpdate(IAFFrame::AFFrameInfo &info) = 0; + virtual ~IVideoRenderListener() = default; + }; + +public: virtual ~IVideoRender() = default; /** @@ -76,8 +98,6 @@ class IVideoRender { */ virtual int init() = 0; - virtual void setVideoRotate(Rotate rotate) = 0; - /** * clear screen to black. */ @@ -100,7 +120,15 @@ class IVideoRender { * NOTE: will callback in render thread. * @param renderedCallback */ - virtual void setRenderResultCallback(std::function renderedCallback) = 0; + virtual void setRenderResultCallback(std::function renderedCallback) + { + mRenderResultCallback = renderedCallback; + } + + virtual void setListener(IVideoRenderListener *listener) + { + mListener = listener; + } /** * set render rotate. @@ -168,16 +196,27 @@ class IVideoRender { } - virtual void *getSurface() + virtual void *getSurface(bool cached) { return nullptr; } virtual float getRenderFPS() = 0; + virtual void invalid(bool invalid) + { + mInvalid = invalid; + } + virtual uint64_t getFlags() = 0; + protected: IVideoRenderFilter *mFilter{}; + bool mInvalid{false}; + + // TODO: delete this + std::function mRenderResultCallback = nullptr; + IVideoRenderListener *mListener{nullptr}; }; diff --git a/framework/render/video/SdlAFVideoRender.cpp b/framework/render/video/SdlAFVideoRender.cpp index 40b11b2ef..c9db0c150 100644 --- a/framework/render/video/SdlAFVideoRender.cpp +++ b/framework/render/video/SdlAFVideoRender.cpp @@ -7,17 +7,26 @@ #include "SdlAFVideoRender.h" #include #include +#include +#include #ifdef __APPLE__ #include #endif static int SDLCALL SdlWindowSizeEventWatch(void *userdata, SDL_Event *event); +static void sdlLogCb(void *userdata, int category, SDL_LogPriority priority, const char *message) +{ + AF_LOGI("sdl log: %d %d %s", category, priority, message); +} + SdlAFVideoRender::SdlAFVideoRender() { mVSync = VSyncFactory::create(*this, 60); // mHz = 0; SDL_InitSubSystem(SDL_INIT_VIDEO); + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); + SDL_LogSetOutputFunction(sdlLogCb, nullptr); mVSync->start(); }; @@ -25,10 +34,19 @@ SdlAFVideoRender::~SdlAFVideoRender() { if (mVideoTexture != nullptr) { SDL_DestroyTexture(mVideoTexture); + mVideoTexture = nullptr; + mInited = false; } if (mRenderNeedRelease) { SDL_DelEventWatch(SdlWindowSizeEventWatch, this); SDL_DestroyRenderer(mVideoRender); + mVideoRender = nullptr; + mRenderNeedRelease = false; + } + if (mWindowNeedRelease && mVideoWindow) { + SDL_DestroyWindow(mVideoWindow); + mVideoWindow = nullptr; + mWindowNeedRelease = false; } SDL_QuitSubSystem(SDL_INIT_VIDEO); } @@ -67,17 +85,39 @@ int SdlAFVideoRender::init() int SdlAFVideoRender::refreshScreen() { + bool needClearScreen = false; { std::unique_lock lock(mRenderMutex); if (mLastVideoFrame == nullptr && mBackFrame != nullptr) { mLastVideoFrame = mBackFrame->clone(); } + if (mLastVideoFrame == nullptr) { + needClearScreen = true; + } + } + if (needClearScreen) { + clearScreen(); + } else { + onVSync(-1); } - onVSync(-1); return 0; } +void SdlAFVideoRender::delayRefreshScreen() +{ + std::thread thread([=]() { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + { + std::unique_lock lock(mRenderMutex); + if (mLastVideoFrame == nullptr && mBackFrame != nullptr) { + mLastVideoFrame = mBackFrame->clone(); + } + } + }); + thread.detach(); +} + int SdlAFVideoRender::clearScreen() { @@ -88,6 +128,7 @@ int SdlAFVideoRender::clearScreen() SDL_RenderClear(mVideoRender); SDL_RenderPresent(mVideoRender); } + mBackFrame = nullptr; return 0; } @@ -96,8 +137,10 @@ int SdlAFVideoRender::renderFrame(std::unique_ptr &frame) { { + bool paused = false; if (frame == nullptr) { mVSync->pause(); + paused = true; } { std::unique_lock lock(mRenderMutex); @@ -105,9 +148,15 @@ int SdlAFVideoRender::renderFrame(std::unique_ptr &frame) mLastVideoFrame->setDiscard(true); mRenderResultCallback(mLastVideoFrame->getInfo().pts, false); } + if (mListener && mLastVideoFrame) { + mListener->onFrameInfoUpdate(mLastVideoFrame->getInfo()); + } + mLastVideoFrame = std::move(frame); } - mLastVideoFrame = std::move(frame); - if (frame == nullptr) { + if (mLastVideoFrame && mVideoRotate != getRotate(mLastVideoFrame->getInfo().video.rotate)) { + mVideoRotate = getRotate(mLastVideoFrame->getInfo().video.rotate); + } + if (paused) { mVSync->start(); } } @@ -191,7 +240,7 @@ int SdlAFVideoRender::onVSyncInner(int64_t tick) srcRect.y = 0; srcRect.w = mVideoWidth; srcRect.h = mVideoHeight; - int angle = mRotate; + int angle = (mRotate + mVideoRotate) % 360; SDL_RendererFlip flip = convertFlip(); SDL_Rect dstRect = getDestRet(); { @@ -226,23 +275,21 @@ int SdlAFVideoRender::onVSyncInner(int64_t tick) int SdlAFVideoRender::setRotate(Rotate rotate) { mRotate = rotate; + refreshScreen(); return 0; } -void SdlAFVideoRender::setVideoRotate(Rotate rotate) -{ - mVideoRotate = rotate; -} - int SdlAFVideoRender::setFlip(Flip flip) { mFlip = flip; + refreshScreen(); return 0; } int SdlAFVideoRender::setScale(Scale scale) { mScale = scale; + refreshScreen(); return 0; } @@ -344,15 +391,6 @@ SDL_RendererFlip SdlAFVideoRender::convertFlip() return flip; } -void SdlAFVideoRender::setWindowSize(int windWith, int windHeight) -{ - if (mWindowWidth != windWith || mWindowHeight != windHeight) { - mWindowWidth = windWith; - mWindowHeight = windHeight; - refreshScreen(); - } -} - void SdlAFVideoRender::captureScreen(std::function func) { if (func == nullptr) { @@ -372,7 +410,7 @@ void SdlAFVideoRender::captureScreen(std::functionformat->format; { std::unique_lock lock(mRenderMutex); @@ -391,10 +429,10 @@ void SdlAFVideoRender::captureScreen(std::functiontype == CicadaSDLViewType_NATIVE_WINDOW) { mVideoWindow = SDL_CreateWindowFrom(display->view); SDL_ShowWindow(mVideoWindow); + mWindowNeedRelease = true; } else { mVideoWindow = static_cast(display->view); + mWindowNeedRelease = false; } if (mVideoWindow) { mVideoRender = SDL_GetRenderer(mVideoWindow); if (mVideoRender == nullptr) { + // log all render name + int renderCount = SDL_GetNumRenderDrivers(); + for (int i = 0; i < renderCount; i++) { + SDL_RendererInfo renderDriverInfo; + SDL_GetRenderDriverInfo(i, &renderDriverInfo); + std::string renderDriverName; + if (renderDriverInfo.name) { + renderDriverName = renderDriverInfo.name; + } + AF_LOGI("sdl render%d: %s\n", i, renderDriverName.c_str()); + } + + // add before renderer created, so this callback will be called before renderer's window size change callback SDL_AddEventWatch(SdlWindowSizeEventWatch, this); Uint32 renderFlags = 0; #ifdef __WINDOWS__ @@ -460,9 +523,20 @@ int SdlAFVideoRender::setDisPlay(void *view) renderFlags = SDL_RENDERER_SOFTWARE; #endif mVideoRender = SDL_CreateRenderer(mVideoWindow, -1, renderFlags); + + // log the render name + SDL_RendererInfo renderInfo; + SDL_GetRendererInfo(mVideoRender, &renderInfo); + std::string renderName; + if (renderInfo.name) { + renderName = renderInfo.name; + } + AF_LOGI("create sdl render: %s\n", renderName.c_str()); + mRenderNeedRelease = true; - } else + } else { mRenderNeedRelease = false; + } } return 0; @@ -477,6 +551,12 @@ int SDLCALL SdlWindowSizeEventWatch(void *userdata, SDL_Event *event) SDL_Window *window = SDL_GetWindowFromID(event->window.windowID); pSelf->onWindowSizeChange(window); } + } else if (event->window.event == SDL_WINDOWEVENT_RESIZED) { + // after SDL_WINDOWEVENT_SIZE_CHANGED event, d3d11 recreate resource, then in this event refresh use new d3d11 resource + SdlAFVideoRender *pSelf = (SdlAFVideoRender *) userdata; + if (pSelf) { + pSelf->delayRefreshScreen(); + } } } return 0; @@ -489,6 +569,7 @@ void SdlAFVideoRender::onWindowSizeChange(SDL_Window *window) if (!mInited) { return; } + // block the renderer's window size change callback(d3d11 recreate resource) until render complete std::unique_lock lock(mWindowSizeChangeMutex); mWindowSizeChangeCon.wait(lock); #endif diff --git a/framework/render/video/SdlAFVideoRender.h b/framework/render/video/SdlAFVideoRender.h index 51a4858da..4c156358b 100644 --- a/framework/render/video/SdlAFVideoRender.h +++ b/framework/render/video/SdlAFVideoRender.h @@ -24,10 +24,6 @@ class SdlAFVideoRender : public IVideoRender, private IVSync::Listener { int init() override; - void setVideoRotate(Rotate rotate) override ; - - void setWindowSize(int windWith, int mWindHeight) override; - int clearScreen() override; int renderFrame(std::unique_ptr &frame) override; @@ -59,6 +55,15 @@ class SdlAFVideoRender : public IVideoRender, private IVSync::Listener { void onWindowSizeChange(SDL_Window *window); + int refreshScreen(); + + void delayRefreshScreen(); + + uint64_t getFlags() override + { + return 0; + } + private: int VSyncOnInit() override { @@ -82,13 +87,13 @@ class SdlAFVideoRender : public IVideoRender, private IVSync::Listener { void recreateTextureIfNeed(int videoWidth, int videoHeight); - int refreshScreen(); private: bool mInited = false; SDL_Window *mVideoWindow = nullptr; SDL_Texture *mVideoTexture = nullptr; SDL_Renderer *mVideoRender = nullptr; + bool mWindowNeedRelease{false}; bool mRenderNeedRelease{false}; void* mCurrentView = nullptr; diff --git a/framework/render/video/glRender/CV420PProgramContext.cpp b/framework/render/video/glRender/CV420PProgramContext.cpp index c94b64211..404b187bf 100644 --- a/framework/render/video/glRender/CV420PProgramContext.cpp +++ b/framework/render/video/glRender/CV420PProgramContext.cpp @@ -121,6 +121,10 @@ int CV420PProgramContext::initProgram() { return 0; } +void CV420PProgramContext::useProgram(){ + glUseProgram(mCVProgram); +} + void CV420PProgramContext::updateScale(IVideoRender::Scale scale) { if (mScale != scale) { mScale = scale; diff --git a/framework/render/video/glRender/CV420PProgramContext.h b/framework/render/video/glRender/CV420PProgramContext.h index 10e09672e..fe6ecb2db 100644 --- a/framework/render/video/glRender/CV420PProgramContext.h +++ b/framework/render/video/glRender/CV420PProgramContext.h @@ -27,6 +27,8 @@ class CV420PProgramContext : public IProgramContext { int initProgram() override; + void useProgram() override; + void updateScale(IVideoRender::Scale scale) override; void updateRotate(IVideoRender::Rotate rotate) override; diff --git a/framework/render/video/glRender/GLRender.cpp b/framework/render/video/glRender/GLRender.cpp index a58ba5a02..91613aa24 100644 --- a/framework/render/video/glRender/GLRender.cpp +++ b/framework/render/video/glRender/GLRender.cpp @@ -83,21 +83,7 @@ int GLRender::renderFrame(std::unique_ptr &frame) } if (frame == nullptr) { - // do flush - mVSync->pause(); - { - std::unique_lock locker(mFrameMutex); - - while (!mInputQueue.empty()) { - dropFrame(); - } - } - std::unique_lock locker(mInitMutex); - - if (!mInBackground) { - mVSync->start(); - } - + bFlushAsync = true; return 0; } @@ -125,11 +111,6 @@ int GLRender::setRotate(IVideoRender::Rotate rotate) return 0; } -void GLRender::setVideoRotate(Rotate rotate) -{ - mVideoRotate = rotate; -} - int GLRender::setFlip(IVideoRender::Flip flip) { AF_LOGD("-----> setFlip"); @@ -188,6 +169,13 @@ int GLRender::onVsyncInner(int64_t tick) { std::unique_lock locker(mFrameMutex); + if (bFlushAsync) { + while (!mInputQueue.empty()) { + dropFrame(); + } + bFlushAsync = false; + } + if (!mInputQueue.empty()) { if (mInputQueue.size() >= MAX_IN_SIZE) { while (mInputQueue.size() >= MAX_IN_SIZE) { @@ -264,9 +252,12 @@ void GLRender::VSyncOnDestroy() { mPrograms.clear(); - if(mContext == nullptr) { + if (mContext == nullptr) { return; } + if (mClearScreenOn) { + glClearScreen(); + } mContext->DestroyView(); mContext->DestroySurface(mGLSurface); @@ -276,14 +267,25 @@ void GLRender::VSyncOnDestroy() mContext = nullptr; } +void GLRender::glClearScreen() +{ + glViewport(0, 0, mWindowWidth, mWindowHeight); + unsigned int backgroundColor = mBackgroundColor; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + cicada::convertToGLColor(backgroundColor, color); + glClearColor(color[0], color[1], color[2], color[3]); + glClear(GL_COLOR_BUFFER_BIT); + mContext->Present(mGLSurface); +} + bool GLRender::renderActually() { - if(mContext == nullptr) { + if (mContext == nullptr) { return false; } if (mInBackground) { -// AF_LOGD("renderActurally .. InBackground .."); + // AF_LOGD("renderActurally .. InBackground .."); return false; } @@ -301,12 +303,15 @@ bool GLRender::renderActually() } #endif - + if (mInvalid) { + return false; + } bool displayViewChanged = false; + bool viewSizeChanged = false; { unique_lock viewLock(mViewMutex); displayViewChanged = mContext->SetView(mDisplayView); - bool viewSizeChanged = mContext->IsViewSizeChanged(); + viewSizeChanged = mContext->IsViewSizeChanged(); if (viewSizeChanged || displayViewChanged || (mGLSurface == nullptr && mDisplayView != nullptr)) { @@ -318,8 +323,13 @@ bool GLRender::renderActually() mWindowWidth = mContext->GetWidth(); mWindowHeight = mContext->GetHeight(); - if (mGLSurface == nullptr) { -// AF_LOGE("0918 renderActurally return mGLSurface = null.."); + if (mGLSurface == nullptr || mInvalid) { + + std::unique_lock locker(mFrameMutex); + if (!mInputQueue.empty()) { + dropFrame(); + } + return false; } @@ -349,6 +359,8 @@ bool GLRender::renderActually() if (frame != nullptr) { framePts = frame->getInfo().pts; + mVideoInfo = frame->getInfo(); + mVideoRotate = getRotate(frame->getInfo().video.rotate); } Rotate finalRotate = Rotate_None; @@ -369,7 +381,16 @@ bool GLRender::renderActually() mProgramContext->updateWindowSize(mWindowWidth, mWindowHeight, displayViewChanged); mProgramContext->updateFlip(mFlip); mProgramContext->updateBackgroundColor(mBackgroundColor); - int ret = mProgramContext->updateFrame(frame); + int ret = -1; + if (mScreenCleared && frame == nullptr) { + //do not draw last frame when need clear screen. + if (viewSizeChanged || displayViewChanged) { + glClearScreen(); + } + } else { + mScreenCleared = false; + ret = mProgramContext->updateFrame(frame); + } //work around for glReadPixels is upside-down. { std::unique_lock locker(mCaptureMutex); @@ -407,22 +428,15 @@ bool GLRender::renderActually() if ((INT64_MIN != framePts) && (mRenderResultCallback != nullptr)) { mRenderResultCallback(framePts, true); } - } - - if (mClearScreenOn) { - glViewport(0, 0, mWindowWidth, mWindowHeight); - unsigned int backgroundColor = mBackgroundColor; - float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - cicada::convertToGLColor(backgroundColor, color); - glClearColor(color[0], color[1], color[2], color[3]); - glClear(GL_COLOR_BUFFER_BIT); - mContext->Present(mGLSurface); - if (mProgramContext != nullptr) { - mProgramFormat = -1; - mProgramContext = nullptr; + if (mListener) { + mListener->onFrameInfoUpdate(mVideoInfo); } + } + if (mClearScreenOn) { + glClearScreen(); + mScreenCleared = true; mClearScreenOn = false; } @@ -503,23 +517,23 @@ void GLRender::captureScreen(std::function func) } } -void *GLRender::getSurface() +void *GLRender::getSurface(bool cached) { #ifdef __ANDROID__ - { + IProgramContext *programContext = getProgram(AF_PIX_FMT_CICADA_MEDIA_CODEC); + + if (programContext == nullptr || programContext->getSurface() == nullptr || !cached) { std::unique_lock locker(mCreateOutTextureMutex); needCreateOutTexture = true; mCreateOutTextureCondition.wait(locker, [this]() -> int { return !needCreateOutTexture; }); } - IProgramContext *programContext = getProgram(AF_PIX_FMT_CICADA_MEDIA_CODEC); - if (programContext == nullptr) { - return nullptr; + programContext = getProgram(AF_PIX_FMT_CICADA_MEDIA_CODEC); + if (programContext) { + return programContext->getSurface(); } - - return programContext->getSurface(); #endif return nullptr; } @@ -527,7 +541,9 @@ void *GLRender::getSurface() IProgramContext *GLRender::getProgram(int frameFormat, IAFFrame *frame) { if (mPrograms.count(frameFormat) > 0) { - return mPrograms[frameFormat].get(); + IProgramContext *pContext = mPrograms[frameFormat].get(); + pContext->useProgram(); + return pContext; } unique_ptr targetProgram{nullptr}; @@ -601,11 +617,6 @@ float GLRender::getRenderFPS() return mFps; } -void GLRender::setRenderResultCallback(function renderResultCallback) -{ - mRenderResultCallback = renderResultCallback; -} - void GLRender::surfaceChanged() { #ifdef __ANDROID__ @@ -618,4 +629,3 @@ void GLRender::surfaceChanged() mRenderCallbackCon.wait(lock); #endif } - diff --git a/framework/render/video/glRender/GLRender.h b/framework/render/video/glRender/GLRender.h index 4e1bc29d9..f27dff146 100644 --- a/framework/render/video/glRender/GLRender.h +++ b/framework/render/video/glRender/GLRender.h @@ -39,8 +39,6 @@ class GLRender : public IVideoRender, private IVSync::Listener { int init() override; - void setVideoRotate(Rotate rotate) override; - int setDisPlay(void *view) override; int clearScreen() override; @@ -49,8 +47,6 @@ class GLRender : public IVideoRender, private IVSync::Listener { int renderFrame(std::unique_ptr &frame) override; - void setRenderResultCallback(std::function renderResultCallback) override; - int setRotate(Rotate rotate) override; int setFlip(Flip flip) override; @@ -61,11 +57,15 @@ class GLRender : public IVideoRender, private IVSync::Listener { void captureScreen(std::function func) override; - void *getSurface() override; + void *getSurface(bool cached) override; float getRenderFPS() override; void surfaceChanged() override; + uint64_t getFlags() override + { + return 0; + }; private: @@ -93,6 +93,8 @@ class GLRender : public IVideoRender, private IVSync::Listener { void captureScreen(); + void glClearScreen(); + void calculateFPS(int64_t tick); IProgramContext *getProgram(int frameFormat, IAFFrame *frame = nullptr); @@ -142,8 +144,10 @@ class GLRender : public IVideoRender, private IVSync::Listener { int mProgramFormat = -1; bool mClearScreenOn = false; + bool mScreenCleared = false; + IAFFrame::AFFrameInfo mVideoInfo{}; - std::function mRenderResultCallback = nullptr; + std::atomic_bool bFlushAsync{false}; #ifdef __ANDROID__ std::mutex mRenderCallbackMutex{}; diff --git a/framework/render/video/glRender/IProgramContext.h b/framework/render/video/glRender/IProgramContext.h index ec0580d65..eea834946 100644 --- a/framework/render/video/glRender/IProgramContext.h +++ b/framework/render/video/glRender/IProgramContext.h @@ -42,6 +42,8 @@ class IProgramContext { virtual int initProgram() = 0; + virtual void useProgram() = 0; + virtual void createSurface() { } diff --git a/framework/render/video/glRender/OESProgramContext.cpp b/framework/render/video/glRender/OESProgramContext.cpp index 5904b5b07..14454a685 100644 --- a/framework/render/video/glRender/OESProgramContext.cpp +++ b/framework/render/video/glRender/OESProgramContext.cpp @@ -97,6 +97,10 @@ int OESProgramContext::initProgram() { return 0; } +void OESProgramContext::useProgram(){ + glUseProgram(mOESProgram); +} + void OESProgramContext::updateScale(IVideoRender::Scale scale) { if (mScale != scale) { mScale = scale; @@ -382,7 +386,7 @@ int OESProgramContext::updateFrame(std::unique_ptr &frame) { float color[4] = {0.0f,0.0f,0.0f,1.0f}; cicada::convertToGLColor(mBackgroundColor , color); glClearColor(color[0], color[1], color[2], color[3]); - mBackgroundColorChanged = true; + mBackgroundColorChanged = false; } glClear(GL_COLOR_BUFFER_BIT); diff --git a/framework/render/video/glRender/OESProgramContext.h b/framework/render/video/glRender/OESProgramContext.h index c3d1515f4..6357e96a9 100644 --- a/framework/render/video/glRender/OESProgramContext.h +++ b/framework/render/video/glRender/OESProgramContext.h @@ -18,6 +18,8 @@ class OESProgramContext : public IProgramContext , private DecoderSurfaceCallbac private: int initProgram() override; + void useProgram() override; + void createSurface() override; void *getSurface() override; diff --git a/framework/render/video/glRender/YUVProgramContext.cpp b/framework/render/video/glRender/YUVProgramContext.cpp index 86c21613a..0b898517b 100644 --- a/framework/render/video/glRender/YUVProgramContext.cpp +++ b/framework/render/video/glRender/YUVProgramContext.cpp @@ -110,6 +110,10 @@ int YUVProgramContext::initProgram() { return 0; } +void YUVProgramContext::useProgram(){ + glUseProgram(mProgram); +} + void YUVProgramContext::updateScale(IVideoRender::Scale scale) { if (mScale != scale) { mScale = scale; diff --git a/framework/render/video/glRender/YUVProgramContext.h b/framework/render/video/glRender/YUVProgramContext.h index 3f62e2481..a77fab9a1 100644 --- a/framework/render/video/glRender/YUVProgramContext.h +++ b/framework/render/video/glRender/YUVProgramContext.h @@ -17,6 +17,8 @@ class YUVProgramContext : public IProgramContext { private: int initProgram() override ; + void useProgram() override ; + void updateScale(IVideoRender::Scale scale) override ; void updateFlip(IVideoRender::Flip flip) override ; diff --git a/framework/render/video/glRender/platform/ios/eagl_context.mm b/framework/render/video/glRender/platform/ios/eagl_context.mm index 075148f04..f0fba30a6 100644 --- a/framework/render/video/glRender/platform/ios/eagl_context.mm +++ b/framework/render/video/glRender/platform/ios/eagl_context.mm @@ -12,13 +12,15 @@ if (sharedContext == nullptr) { mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; } else { - mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:((EAGLContext *) sharedContext).sharegroup]; + mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 + sharegroup:((__bridge EAGLContext *) sharedContext).sharegroup]; } if (!mContext) { if (sharedContext == nullptr) { mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; } else { - mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:((EAGLContext *) sharedContext).sharegroup]; + mContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:((__bridge EAGLContext *) sharedContext).sharegroup]; } mGLVersion = kOpenGLES2_0; } @@ -64,7 +66,7 @@ glBindRenderbuffer(GL_RENDERBUFFER, surface->renderbuffer); CHECK_GL_ERROR_DEBUG(); - [mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *) surface->surface]; + [mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(__bridge CAEAGLLayer *) surface->surface]; glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, surface->renderbuffer); CHECK_GL_ERROR_DEBUG(); @@ -74,7 +76,7 @@ glBindFramebuffer(GL_FRAMEBUFFER, fbo); CHECK_GL_ERROR_DEBUG(); - CAEAGLLayer* layer = (CAEAGLLayer *) surface->surface; + CAEAGLLayer *layer = (__bridge CAEAGLLayer *) surface->surface; mLayerWidth = layer.frame.size.width; mLayerHeight = layer.frame.size.height; @@ -146,7 +148,7 @@ CHECK_GL_ERROR_DEBUG(); glBindRenderbuffer(GL_RENDERBUFFER, surface->renderbuffer); CHECK_GL_ERROR_DEBUG(); - [mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *) surface->surface]; + [mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:(__bridge CAEAGLLayer *) surface->surface]; glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, surface->renderbuffer); CHECK_GL_ERROR_DEBUG(); @@ -167,7 +169,7 @@ void CicadaEAGLContext::Destroy() { [EAGLContext setCurrentContext:nil]; - [mContext release]; + // [mContext release]; } int CicadaEAGLContext::GetVisualFormat() { return 0; } @@ -178,7 +180,10 @@ int CicadaEAGLContext::GetHeight() { return mHeight; } - void *CicadaEAGLContext::GetContext() { return mContext; } + void *CicadaEAGLContext::GetContext() + { + return (__bridge void *) mContext; + } bool CicadaEAGLContext::IsViewSizeChanged(){ @@ -186,7 +191,7 @@ return false; } - CAEAGLLayer* layer = (CAEAGLLayer *) mCurrentSurface->surface; + CAEAGLLayer *layer = (__bridge CAEAGLLayer *) mCurrentSurface->surface; if(layer == nullptr){ return false; } diff --git a/framework/render/video/glRender/platform/mac/mgl_context.mm b/framework/render/video/glRender/platform/mac/mgl_context.mm index 0967424d5..0e99b54e5 100644 --- a/framework/render/video/glRender/platform/mac/mgl_context.mm +++ b/framework/render/video/glRender/platform/mac/mgl_context.mm @@ -19,7 +19,7 @@ mContext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:(__bridge NSOpenGLContext *) sharedContext]; } - [pf release]; + // [pf release]; // GLint major; // GLint minor; // NSOpenGLGetVersion(&major, &minor); @@ -40,8 +40,8 @@ } GLSurface *surface = new GLSurface(); mCurrentSurface = surface; - mCurrentSurface->nativeWindow = mSetView; - mCurrentSurface->surface = mSetView; + mCurrentSurface->nativeWindow = (__bridge void *) mSetView; + mCurrentSurface->surface = (__bridge void *) mSetView; return mCurrentSurface; } @@ -109,13 +109,16 @@ [NSOpenGLContext clearCurrentContext]; - [mContext release]; + //[mContext release]; mContext = nil; } int CicadaMGLContext::GetVisualFormat() { return 0; } - void *CicadaMGLContext::GetContext() { return mContext; } + void *CicadaMGLContext::GetContext() + { + return (__bridge void *) mContext; + } int CicadaMGLContext::GetWidth() { return mWidth; } @@ -126,7 +129,7 @@ bool CicadaMGLContext::SetView(void *view) { std::lock_guard lock(mViewMutex); - if (mSetView == view) { + if (((__bridge void *) mSetView) == view) { return false; } else { mViewInited = false; diff --git a/framework/render/video/vsync/IVSync.h b/framework/render/video/vsync/IVSync.h index 23b1142d0..afd8fccfd 100644 --- a/framework/render/video/vsync/IVSync.h +++ b/framework/render/video/vsync/IVSync.h @@ -35,12 +35,14 @@ class IVSync { return -ENOTSUP; }; - virtual float getHz() = 0; - virtual void start() = 0; virtual void pause() = 0; + virtual float getHz() = 0; + + virtual int getPeriod() = 0; + protected: Listener &mListener; diff --git a/framework/render/video/vsync/timedVSync.h b/framework/render/video/vsync/timedVSync.h index e833b90c1..e164d7431 100644 --- a/framework/render/video/vsync/timedVSync.h +++ b/framework/render/video/vsync/timedVSync.h @@ -21,9 +21,17 @@ class timedVSync : public IVSync { void start() override; void pause() override; - float getHz()override{ + + + float getHz() override + { return mHz; - }; + } + + int getPeriod() override + { + return mPeriod; + } private: diff --git a/framework/tests/communication/CMakeLists.txt b/framework/tests/communication/CMakeLists.txt index 0816063f6..6f48771b8 100644 --- a/framework/tests/communication/CMakeLists.txt +++ b/framework/tests/communication/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable(communicationTest test.cpp) target_link_libraries(communicationTest PRIVATE communication framework_utils + framework_drm avformat avcodec avutil diff --git a/framework/tests/dataSource/CMakeLists.txt b/framework/tests/dataSource/CMakeLists.txt index 41c36bef7..1701c8986 100644 --- a/framework/tests/dataSource/CMakeLists.txt +++ b/framework/tests/dataSource/CMakeLists.txt @@ -34,6 +34,7 @@ target_link_libraries(dataSourceTest PRIVATE data_source # plugin framework_utils + framework_drm avformat avcodec swresample diff --git a/framework/tests/dataSource/dataSourceUnitTest.cpp b/framework/tests/dataSource/dataSourceUnitTest.cpp index 96717d201..db57b093f 100644 --- a/framework/tests/dataSource/dataSourceUnitTest.cpp +++ b/framework/tests/dataSource/dataSourceUnitTest.cpp @@ -3,14 +3,15 @@ // #include "gtest/gtest.h" +#include #include #include -#include -#include -#include +#include #include #include -#include +#include +#include +#include using namespace std; using namespace Cicada; @@ -189,6 +190,18 @@ TEST(http, keep_alive) free(buf); } +TEST(http, post) +{ + string url = "https://ptsv2.com/t/50oow-1602229322"; + CurlDataSource source(url); + uint8_t c = 'c'; + source.setPost(true, 1, &c); + int ret = source.Open(0); + ASSERT_GE(ret, 0); + ret = source.Seek(0, SEEK_SIZE); + AF_LOGD("size is %d\n", ret); +} + #include "ipList.h" TEST(https, ipList) diff --git a/framework/tests/decoder/CMakeLists.txt b/framework/tests/decoder/CMakeLists.txt index c3f0d9f78..d7444f4a4 100644 --- a/framework/tests/decoder/CMakeLists.txt +++ b/framework/tests/decoder/CMakeLists.txt @@ -31,6 +31,7 @@ target_link_libraries( videodec data_source framework_utils + framework_drm avformat avcodec swresample diff --git a/framework/tests/decoder/decoderUnitTest.cpp b/framework/tests/decoder/decoderUnitTest.cpp index 400b3289f..5337c0532 100644 --- a/framework/tests/decoder/decoderUnitTest.cpp +++ b/framework/tests/decoder/decoderUnitTest.cpp @@ -15,21 +15,21 @@ using namespace std; static void test_codec(const string &url, AFCodecID codec, int flags) { - unique_ptr decoder = decoderFactory::create(codec, flags, 0); - ASSERT_TRUE(decoder); auto source = dataSourcePrototype::create(url); source->Open(0); auto *demuxer = new demuxer_service(source); int ret = demuxer->initOpen(); Stream_meta smeta{}; unique_ptr meta = unique_ptr(new streamMeta(&smeta)); - + unique_ptr decoder{nullptr}; for (int i = 0; i < demuxer->GetNbStreams(); ++i) { demuxer->GetStreamMeta(meta, i, false); if (((Stream_meta *) (*meta))->codec == codec) { + decoder = decoderFactory::create(*((Stream_meta *) (*meta)), flags, 0 , nullptr); + ASSERT_TRUE(decoder); demuxer->OpenStream(i); - ret = decoder->open(((Stream_meta *) (*meta)), nullptr, 0); + ret = decoder->open(((Stream_meta *) (*meta)), nullptr, 0, nullptr); ASSERT_TRUE(ret >= 0); break; } diff --git a/framework/tests/demuxer/CMakeLists.txt b/framework/tests/demuxer/CMakeLists.txt index b5fae0e34..41a437fb3 100644 --- a/framework/tests/demuxer/CMakeLists.txt +++ b/framework/tests/demuxer/CMakeLists.txt @@ -34,6 +34,7 @@ target_link_libraries( data_source # plugin framework_utils + framework_drm avformat avcodec swresample diff --git a/framework/tests/demuxer/demuxerUnitTest.cpp b/framework/tests/demuxer/demuxerUnitTest.cpp index a67d08b65..ef3d1205c 100644 --- a/framework/tests/demuxer/demuxerUnitTest.cpp +++ b/framework/tests/demuxer/demuxerUnitTest.cpp @@ -2,12 +2,13 @@ // Created by moqi on 2019/11/15. // +#include "demuxerUtils.h" #include "gtest/gtest.h" -#include #include +#include #include #include -#include "demuxerUtils.h" +#include using namespace Cicada; @@ -145,38 +146,133 @@ TEST(first_seek, hls) TEST(mergeHeader, mp4) { std::string url = "http://player.alicdn.com/video/aliyunmedia.mp4"; - test_mergeHeader(url, true); - test_mergeHeader(url, false); + test_mergeHeader(url, header_type_merge); + test_mergeHeader(url, header_type_extract); } TEST(mergeHeader, ts) { std::string url = "https://alivc-demo-vod.aliyuncs.com/ddb0c76ce153450081cd4c45118371a7/d30995ad97bc4643bf0a8c4cedd0c81f-007b1abb398f0e4c6f46d30b0125da41-sd-00001.ts"; - test_mergeHeader(url, true); - test_mergeHeader(url, false); + test_mergeHeader(url, header_type_merge); + test_mergeHeader(url, header_type_extract); } TEST(mergeHeader, hls) { std::string url = "https://alivc-demo-vod.aliyuncs.com/ddb0c76ce153450081cd4c45118371a7/d30995ad97bc4643bf0a8c4cedd0c81f-e16b4635a4cb03424234c3a3d0e7f7e1-sd.m3u8"; - test_mergeHeader(url, true); - test_mergeHeader(url, false); + test_mergeHeader(url, header_type_merge); + test_mergeHeader(url, header_type_extract); } TEST(mergeHeader, hls_aes) { std::string url = "https://alivc-demo-vod.aliyuncs.com/d2c89d7210d443109434685f45ed607b/45ed0cccd8092bf25ee33764b5a52be4-sd-encrypt-stream.m3u8"; - test_mergeHeader(url, true); - test_mergeHeader(url, false); + test_mergeHeader(url, header_type_merge); + test_mergeHeader(url, header_type_extract); } TEST(mergeHeader, hls_multi) { std::string url = "https://alivc-demo-vod.aliyuncs.com/59f748948daa4438b42e42db755ae01e/9d44b2b86d334c6b9df649e35ad0240f.m3u8"; - test_mergeHeader(url, true); - test_mergeHeader(url, false); + test_mergeHeader(url, header_type_merge); + test_mergeHeader(url, header_type_extract); +} + +//TEST(mergeAudioHeader, mp4) +//{ +// std::string url = "http://player.alicdn.com/video/aliyunmedia.mp4"; +// test_mergeAudioHeader(url, header_type_merge); +// test_mergeAudioHeader(url, header_type_extract); +//} +// +//TEST(mergeAudioHeader, ts) +//{ +// std::string url = +// "https://alivc-demo-vod.aliyuncs.com/ddb0c76ce153450081cd4c45118371a7/d30995ad97bc4643bf0a8c4cedd0c81f-007b1abb398f0e4c6f46d30b0125da41-sd-00001.ts"; +// test_mergeAudioHeader(url, header_type_merge); +// test_mergeAudioHeader(url, header_type_extract); +//} +// +//TEST(mergeAudioHeader, hls) +//{ +// std::string url = +// "https://alivc-demo-vod.aliyuncs.com/ddb0c76ce153450081cd4c45118371a7/d30995ad97bc4643bf0a8c4cedd0c81f-e16b4635a4cb03424234c3a3d0e7f7e1-sd.m3u8"; +// test_mergeAudioHeader(url, header_type_merge); +// test_mergeAudioHeader(url, header_type_extract); +//} +// +//TEST(mergeAudioHeader, hls_wideWine) +//{ +// std::string url = +// "http://30.26.143.3:8080/MultiDRM/master.m3u8"; +// test_mergeAudioHeader(url, header_type_merge); +// test_mergeAudioHeader(url, header_type_extract); +//} +// +// +//TEST(mergeAudioHeader, hls_aes) +//{ +// std::string url = +// "https://alivc-demo-vod.aliyuncs.com/d2c89d7210d443109434685f45ed607b/45ed0cccd8092bf25ee33764b5a52be4-sd-encrypt-stream.m3u8"; +// test_mergeAudioHeader(url, header_type_merge); +// test_mergeAudioHeader(url, header_type_extract); +//} +// +//TEST(mergeAudioHeader, hls_multi) +//{ +// std::string url = +// "https://alivc-demo-vod.aliyuncs.com/59f748948daa4438b42e42db755ae01e/9d44b2b86d334c6b9df649e35ad0240f.m3u8"; +// test_mergeAudioHeader(url, header_type_merge); +// test_mergeAudioHeader(url, header_type_extract); +//} + + +//TEST(enctryptionInfo, hls_wideWine) +//{ +// std::string url = +// "http://30.26.143.3:8080/MultiDRM/master.m3u8"; +// test_encryptionInfo(url, Stream_type::STREAM_TYPE_VIDEO , header_type_extract); +// test_encryptionInfo(url, Stream_type::STREAM_TYPE_VIDEO , header_type_merge); +//} +// +// +//TEST(enctryptionInfoAudio, hls_wideWine) +//{ +// std::string url = +// "http://30.26.143.3:8080/MultiDRM/master.m3u8"; +// test_encryptionInfo(url,Stream_type::STREAM_TYPE_AUDIO , header_type_merge); +// test_encryptionInfo(url,Stream_type::STREAM_TYPE_AUDIO , header_type_extract); +//} +// +//TEST(metaKeyInfo, hls_wideWine) +//{ +// std::string url = +// "http://30.26.143.3:8080/MultiDRM/master.m3u8"; +// test_metaKeyInfo(url,Stream_type::STREAM_TYPE_VIDEO); +//} +// +//TEST(metaKeyInfoAudio, hls_wideWine) +//{ +// std::string url = +// "http://30.26.143.3:8080/MultiDRM/master.m3u8"; +// test_metaKeyInfo(url,Stream_type::STREAM_TYPE_AUDIO); +//} + +TEST(scd_h264 , mp4) +{std::string url = + "http://player.alicdn.com/video/aliyunmedia.mp4"; + test_csd(url , header_type_merge); + test_csd(url , header_type_extract); +} + + +TEST(scd_h265 , mp4) +{std::string url = + "https://alivc-demo-vod.aliyuncs.com/a2b7103c0bd049ecb7689472027cad2d/20144bf04b0e4f3c82ea2a7425a0a345-c4f5aabdcc7ba2861e8f0092d94db3bc-sd.mp4"; + test_csd(url , header_type_merge); + test_csd(url , header_type_extract); } \ No newline at end of file diff --git a/framework/tests/demuxer/demuxerUtils.cpp b/framework/tests/demuxer/demuxerUtils.cpp index 3c26e8084..7cf41463a 100644 --- a/framework/tests/demuxer/demuxerUtils.cpp +++ b/framework/tests/demuxer/demuxerUtils.cpp @@ -3,15 +3,20 @@ // #define LOG_TAG "demuxerUtils" #include "demuxerUtils.h" - -#include #include +#include #include +#include + +extern "C" { +#include +#include +}; #include "gtest/gtest.h" using namespace Cicada; -void test_mergeHeader(std::string url, bool merge) +void test_demuxUrl(const string &url, header_type merge, Stream_type type, const std::function &judgeFunc) { auto source = dataSourcePrototype::create(url); source->Open(0); @@ -21,14 +26,13 @@ void test_mergeHeader(std::string url, bool merge) service->getDemuxerHandle()->setBitStreamFormat(merge, merge); ret = service->initOpen(); ret = service->GetNbStreams(); - int videoStreamId = -1; + int streamId = -1; for (int i = 0; i < ret; i++) { std::unique_ptr meta; service->GetStreamMeta(meta, i, false); - if (((Stream_meta *) (*(meta.get())))->type == STREAM_TYPE_VIDEO - || ((Stream_meta *) (*(meta.get())))->type == STREAM_TYPE_MIXED) { + if (((Stream_meta *) (*(meta.get())))->type == type || ((Stream_meta *) (*(meta.get())))->type == STREAM_TYPE_MIXED) { service->OpenStream(i); if (((Stream_meta *) (*(meta.get())))->type == STREAM_TYPE_MIXED) { @@ -41,12 +45,12 @@ void test_mergeHeader(std::string url, bool merge) meta = (Stream_meta *) (pMeta.get()); AF_LOGD("get a stream %d\n", meta->type); - if (meta->type == STREAM_TYPE_VIDEO) { - videoStreamId = GEN_STREAM_ID(i, j); + if (meta->type == type) { + streamId = GEN_STREAM_ID(i, j); } } } else { - videoStreamId = i; + streamId = i; } break; @@ -60,7 +64,7 @@ void test_mergeHeader(std::string url, bool merge) ret = service->readPacket(packet); if (packet) { - if (videoStreamId < 0) { + if (streamId < 0) { int i = GEN_STREAM_INDEX(packet->getInfo().streamIndex); unique_ptr pMeta; Stream_meta *meta{}; @@ -71,30 +75,52 @@ void test_mergeHeader(std::string url, bool merge) meta = (Stream_meta *) (pMeta.get()); AF_LOGD("get a stream %d\n", meta->type); - if (meta->type == STREAM_TYPE_VIDEO) { - videoStreamId = GEN_STREAM_ID(i, j); + if (meta->type == type) { + streamId = GEN_STREAM_ID(i, j); // break; } } } - if (packet->getInfo().streamIndex == videoStreamId && packet->getInfo().flags) { - if (merge) { - ASSERT_EQ(memcmp(packet->getData(), "\0\0\0\1", 4), 0); - } else { - ASSERT_NE(memcmp(packet->getData(), "\0\0\0\1", 4), 0); - } - + if (packet->getInfo().streamIndex == streamId && judgeFunc != nullptr) { + judgeFunc(service.get(), packet.get()); break; } } - if (ret < 0) - break; - } while (ret != 0); + + } while (ret >= 0 || ret == -EAGAIN); service->close(); delete source; } + + +void test_mergeHeader(std::string url, header_type merge) +{ + test_demuxUrl(url, merge, STREAM_TYPE_VIDEO, [merge](demuxer_service* demuxer,IAFPacket *packet) -> void { + if (merge == header_type::header_type_merge) { + ASSERT_EQ(memcmp(packet->getData(), "\0\0\0\1", 4), 0); + } else if(merge == header_type::header_type_extract){ + ASSERT_NE(memcmp(packet->getData(), "\0\0\0\1", 4), 0); + } + }); +} + +void test_mergeAudioHeader(const std::string &url, header_type merge) +{ + test_demuxUrl(url, merge, STREAM_TYPE_AUDIO, [merge](demuxer_service* demuxer,IAFPacket *packet) -> void { + uint8_t *data = packet->getData(); + if (merge == header_type::header_type_merge) { + ASSERT_EQ(AV_RB16(data) & 0xfff0, 0xfff0); + int mFrameLength = + (data[3] & 0x03) << 11 | (data[4] & 0xFF) << 3 | (data[5] & 0xE0) >> 5; + ASSERT_EQ(packet->getSize() ,mFrameLength); + } else if(merge == header_type::header_type_extract){ + ASSERT_NE(AV_RB16(data) & 0xfff0, 0xfff0); + } + }); +} + void testFirstSeek(const string &url, int64_t time, int64_t abs_error) { auto source = dataSourcePrototype::create(url); @@ -124,4 +150,79 @@ void testFirstSeek(const string &url, int64_t time, int64_t abs_error) service->close(); delete source; -} \ No newline at end of file +} + + +void test_encryptionInfo(const std::string &url , Stream_type type ,header_type merge) +{ + test_demuxUrl(url, merge, STREAM_TYPE_VIDEO, [](demuxer_service* demuxer,IAFPacket *pkt) -> void { + IAFPacket::EncryptionInfo info{}; + bool ret = pkt->getEncryptionInfo(&info); + ASSERT_EQ(ret, true); + ASSERT_EQ(info.subsamples.size(), 1); + ASSERT_EQ(pkt->getSize() , info.subsamples.front().bytes_of_clear_data + info.subsamples.front().bytes_of_protected_data); + }); +} + +void test_metaKeyInfo(const std::string& url, Stream_type type ) { + test_demuxUrl(url, header_type_no_touch, type, [](demuxer_service *demuxer, IAFPacket *pkt) -> void { + int index = pkt->getInfo().streamIndex; + unique_ptr mCurrentStreamMeta{}; + demuxer->GetStreamMeta(mCurrentStreamMeta, index, false); + auto *meta = (Stream_meta *) (mCurrentStreamMeta.get()); + ASSERT_NE(meta->keyUrl, nullptr); + ASSERT_NE(meta->keyFormat, nullptr); + }); +} + +void test_csd( const std::string& url , header_type merge) { + test_demuxUrl(url, header_type_no_touch, STREAM_TYPE_VIDEO, [merge](demuxer_service *demuxer, IAFPacket *pkt) -> void { + int index = pkt->getInfo().streamIndex; + unique_ptr mCurrentStreamMeta{}; + demuxer->GetStreamMeta(mCurrentStreamMeta, index, false); + auto *meta = (Stream_meta *) (mCurrentStreamMeta.get()); + + if (meta->codec == AF_CODEC_ID_H264) { + + uint8_t *sps_data = nullptr; + uint8_t *pps_data = nullptr; + int sps_data_size = 0; + int pps_data_size = 0; + int naluLengthSize = 0; + + int ret = parse_h264_extraData(CodecID2AVCodecID(AF_CODEC_ID_H264),meta->extradata, meta->extradata_size, + &sps_data,&sps_data_size, + &pps_data,&pps_data_size, + &naluLengthSize); + ASSERT_GE(ret, 0); + ASSERT_NE(sps_data, nullptr); + ASSERT_NE(pps_data, nullptr); + + free(sps_data); + free(pps_data); + }else if(meta->codec == AF_CODEC_ID_HEVC){ + + uint8_t *vps_data = nullptr; + uint8_t *sps_data = nullptr; + uint8_t *pps_data = nullptr; + int vps_data_size = 0; + int sps_data_size = 0; + int pps_data_size = 0; + int naluLengthSize = 0; + + int ret = parse_h265_extraData(CodecID2AVCodecID(AF_CODEC_ID_HEVC),meta->extradata, meta->extradata_size, + &vps_data,&vps_data_size, + &sps_data,&sps_data_size, + &pps_data,&pps_data_size, + &naluLengthSize); + ASSERT_GE(ret, 0); + ASSERT_NE(vps_data, nullptr); + ASSERT_NE(sps_data, nullptr); + ASSERT_NE(pps_data, nullptr); + + free(sps_data); + free(pps_data); + free(vps_data); + } + }); +} diff --git a/framework/tests/demuxer/demuxerUtils.h b/framework/tests/demuxer/demuxerUtils.h index 7b3e20cda..992244116 100644 --- a/framework/tests/demuxer/demuxerUtils.h +++ b/framework/tests/demuxer/demuxerUtils.h @@ -6,9 +6,19 @@ #define CICADAMEDIA_DEMUXERUTILS_H #include +#include +#include -void test_mergeHeader(std::string url, bool merge); +void test_mergeHeader(std::string url, Cicada::header_type merge); + +void test_mergeAudioHeader(const std::string& url , Cicada::header_type merge); void testFirstSeek(const std::string &url, int64_t time, int64_t abs_error); +void test_encryptionInfo(const std::string& url, Stream_type streamType, Cicada::header_type merge); + +void test_metaKeyInfo(const std::string& url, Stream_type streamType); + +void test_csd(const std::string& url , Cicada::header_type merge); + #endif //CICADAMEDIA_DEMUXERUTILS_H diff --git a/framework/tests/render/CMakeLists.txt b/framework/tests/render/CMakeLists.txt index ddf995589..67adc843a 100644 --- a/framework/tests/render/CMakeLists.txt +++ b/framework/tests/render/CMakeLists.txt @@ -32,6 +32,7 @@ target_link_libraries(renderTest PRIVATE render framework_utils framework_filter + framework_drm avformat avfilter avcodec diff --git a/framework/utils/AFMediaType.h b/framework/utils/AFMediaType.h index 630815eb6..398e9c25a 100755 --- a/framework/utils/AFMediaType.h +++ b/framework/utils/AFMediaType.h @@ -27,57 +27,58 @@ enum AFCodecID { AF_CODEC_ID_H264, AF_CODEC_ID_MPEG4, -// AF_CODEC_ID_RV30, -// AF_CODEC_ID_RV40, -// AF_CODEC_ID_MPEG2VIDEO, -// AF_CODEC_ID_VC1, -// AF_CODEC_ID_WMV3, -// AF_CODEC_ID_WMV1, -// AF_CODEC_ID_WMV2, -// AF_CODEC_ID_MSMPEG4V2, -// AF_CODEC_ID_DIV311, -// AF_CODEC_ID_FLV1, -// AF_CODEC_ID_SVQ3, -// AF_CODEC_ID_MPEG1VIDEO, -// AF_CODEC_ID_VP6, + // AF_CODEC_ID_RV30, + // AF_CODEC_ID_RV40, + // AF_CODEC_ID_MPEG2VIDEO, + // AF_CODEC_ID_VC1, + // AF_CODEC_ID_WMV3, + // AF_CODEC_ID_WMV1, + // AF_CODEC_ID_WMV2, + // AF_CODEC_ID_MSMPEG4V2, + // AF_CODEC_ID_DIV311, + // AF_CODEC_ID_FLV1, + // AF_CODEC_ID_SVQ3, + // AF_CODEC_ID_MPEG1VIDEO, + // AF_CODEC_ID_VP6, AF_CODEC_ID_VP8, AF_CODEC_ID_VP9, -// AF_CODEC_ID_MJPEG, -// AF_CODEC_ID_H263, + // AF_CODEC_ID_MJPEG, + // AF_CODEC_ID_H263, AF_CODEC_ID_AV1, AF_CODEC_ID_HEVC, AF_CODEC_ID_AAC, AF_CODEC_ID_AC3, AF_CODEC_ID_EAC3, -// AF_CODEC_ID_DTS, -// AF_CODEC_ID_DTSE, + // AF_CODEC_ID_DTS, + // AF_CODEC_ID_DTSE, AF_CODEC_ID_MP3, -// AF_CODEC_ID_APE, -// AF_CODEC_ID_COOK, -// AF_CODEC_ID_SIPR, -// AF_CODEC_ID_QDM2, + // AF_CODEC_ID_APE, + // AF_CODEC_ID_COOK, + // AF_CODEC_ID_SIPR, + // AF_CODEC_ID_QDM2, AF_CODEC_ID_MP2, AF_CODEC_ID_MP1, AF_CODEC_ID_OPUS, -// AF_CODEC_ID_AMR_NB, -// AF_CODEC_ID_WMAV2, -// AF_CODEC_ID_WMAPRO, + // AF_CODEC_ID_AMR_NB, + // AF_CODEC_ID_WMAV2, + // AF_CODEC_ID_WMAPRO, AF_CODEC_ID_PCM_S16LE, AF_CODEC_ID_PCM_S16BE, -// AF_CODEC_ID_PCM_BLURAY, -// AF_CODEC_ID_ADPCM, -// AF_CODEC_ID_PCM_S24LE, + // AF_CODEC_ID_PCM_BLURAY, + // AF_CODEC_ID_ADPCM, + // AF_CODEC_ID_PCM_S24LE, AF_CODEC_ID_PCM_U8, -// AF_CODEC_ID_PCM_MULAW, -// AF_CODEC_ID_ATRAC3, -// AF_CODEC_ID_VORBIS, -// AF_CODEC_ID_ALAC, -// AF_CODEC_ID_FLAC, - -// AF_CODEC_ID_TEXT, -// AF_CODEC_ID_SSA, -// AF_CODEC_ID_SRT, + // AF_CODEC_ID_PCM_MULAW, + // AF_CODEC_ID_ATRAC3, + // AF_CODEC_ID_VORBIS, + // AF_CODEC_ID_ALAC, + // AF_CODEC_ID_FLAC, + + AF_CODEC_ID_WEBVTT, + // AF_CODEC_ID_TEXT, + // AF_CODEC_ID_SSA, + // AF_CODEC_ID_SRT, }; @@ -99,40 +100,157 @@ enum AFSampleFormat { }; enum AFPixelFormat { AF_PIX_FMT_NONE = -1, - AF_PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) - AF_PIX_FMT_YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr - AF_PIX_FMT_RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB... - AF_PIX_FMT_BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR... - AF_PIX_FMT_YUV422P, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) - AF_PIX_FMT_YUV444P, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) - AF_PIX_FMT_YUV410P, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) - AF_PIX_FMT_YUV411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) - AF_PIX_FMT_GRAY8, ///< Y , 8bpp - AF_PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb - AF_PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb - AF_PIX_FMT_PAL8, ///< 8 bits with AV_PIX_FMT_RGB32 palette - AF_PIX_FMT_YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range - AF_PIX_FMT_YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting color_range - AF_PIX_FMT_YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting color_range - AF_PIX_FMT_UYVY422, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 - AF_PIX_FMT_UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 - AF_PIX_FMT_BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) - AF_PIX_FMT_BGR4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits - AF_PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) - AF_PIX_FMT_RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) - AF_PIX_FMT_RGB4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits - AF_PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) - AF_PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) - AF_PIX_FMT_NV21, ///< as above, but U and V bytes are swapped + AF_PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) + AF_PIX_FMT_YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr + AF_PIX_FMT_RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB... + AF_PIX_FMT_BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR... + AF_PIX_FMT_YUV422P, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples) + AF_PIX_FMT_YUV444P, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples) + AF_PIX_FMT_YUV410P, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples) + AF_PIX_FMT_YUV411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) + AF_PIX_FMT_GRAY8, ///< Y , 8bpp + AF_PIX_FMT_MONOWHITE,///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb + AF_PIX_FMT_MONOBLACK,///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb + AF_PIX_FMT_PAL8, ///< 8 bits with AV_PIX_FMT_RGB32 palette + AF_PIX_FMT_YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range + AF_PIX_FMT_YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting color_range + AF_PIX_FMT_YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting color_range + AF_PIX_FMT_UYVY422, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1 + AF_PIX_FMT_UYYVYY411,///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3 + AF_PIX_FMT_BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) + AF_PIX_FMT_BGR4,///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits + AF_PIX_FMT_BGR4_BYTE,///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) + AF_PIX_FMT_RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) + AF_PIX_FMT_RGB4,///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits + AF_PIX_FMT_RGB4_BYTE,///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) + AF_PIX_FMT_NV12,///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) + AF_PIX_FMT_NV21,///< as above, but U and V bytes are swapped + + + AF_PIX_FMT_YUV420P10BE = 63,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian + AF_PIX_FMT_YUV420P10LE, ///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian AF_PIX_FMT_D3D11 = 900, AF_PIX_FMT_DXVA2_VLD, AF_PIX_FMT_APPLE_PIXEL_BUFFER = 1000, - AF_PIX_FMT_CICADA_AF,//framework VideoFrame + AF_PIX_FMT_CICADA_AF, //framework VideoFrame AF_PIX_FMT_CICADA_MEDIA_CODEC,//Android mediacodec buffer index }; +/** + * Chromaticity coordinates of the source primaries. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.1. + */ +enum AFColorPrimaries { + AFCOL_PRI_RESERVED0 = 0, + AFCOL_PRI_BT709 = 1,///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B + AFCOL_PRI_UNSPECIFIED = 2, + AFCOL_PRI_RESERVED = 3, + AFCOL_PRI_BT470M = 4,///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + + AFCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM + AFCOL_PRI_SMPTE170M = 6,///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + AFCOL_PRI_SMPTE240M = 7,///< functionally identical to above + AFCOL_PRI_FILM = 8, ///< colour filters using Illuminant C + AFCOL_PRI_BT2020 = 9, ///< ITU-R BT2020 + AFCOL_PRI_SMPTE428 = 10,///< SMPTE ST 428-1 (CIE 1931 XYZ) + AFCOL_PRI_SMPTEST428_1 = AFCOL_PRI_SMPTE428, + AFCOL_PRI_SMPTE431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3 + AFCOL_PRI_SMPTE432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3 + AFCOL_PRI_JEDEC_P22 = 22,///< JEDEC P22 phosphors + AFCOL_PRI_NB ///< Not part of ABI +}; + +/** + * Color Transfer Characteristic. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.2. + */ +enum AFColorTransferCharacteristic { + AFCOL_TRC_RESERVED0 = 0, + AFCOL_TRC_BT709 = 1,///< also ITU-R BT1361 + AFCOL_TRC_UNSPECIFIED = 2, + AFCOL_TRC_RESERVED = 3, + AFCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM + AFCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG + AFCOL_TRC_SMPTE170M = 6,///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC + AFCOL_TRC_SMPTE240M = 7, + AFCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics" + AFCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)" + AFCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)" + AFCOL_TRC_IEC61966_2_4 = 11,///< IEC 61966-2-4 + AFCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut + AFCOL_TRC_IEC61966_2_1 = 13,///< IEC 61966-2-1 (sRGB or sYCC) + AFCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10-bit system + AFCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12-bit system + AFCOL_TRC_SMPTE2084 = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems + AFCOL_TRC_SMPTEST2084 = AFCOL_TRC_SMPTE2084, + AFCOL_TRC_SMPTE428 = 17,///< SMPTE ST 428-1 + AFCOL_TRC_SMPTEST428_1 = AFCOL_TRC_SMPTE428, + AFCOL_TRC_ARIB_STD_B67 = 18,///< ARIB STD-B67, known as "Hybrid log-gamma" + AFCOL_TRC_NB ///< Not part of ABI +}; + +/** + * YUV colorspace type. + * These values match the ones defined by ISO/IEC 23001-8_2013 § 7.3. + */ +enum AFColorSpace { + AFCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB) + AFCOL_SPC_BT709 = 1,///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B + AFCOL_SPC_UNSPECIFIED = 2, + AFCOL_SPC_RESERVED = 3, + AFCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20) + AFCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601 + AFCOL_SPC_SMPTE170M = 6,///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC + AFCOL_SPC_SMPTE240M = 7,///< functionally identical to above + AFCOL_SPC_YCGCO = 8, ///< Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16 + AFCOL_SPC_YCOCG = AFCOL_SPC_YCGCO, + AFCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system + AFCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system + AFCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x + AFCOL_SPC_CHROMA_DERIVED_NCL = 12,///< Chromaticity-derived non-constant luminance system + AFCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system + AFCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp + AFCOL_SPC_NB ///< Not part of ABI +}; + +/** + * MPEG vs JPEG YUV range. + */ +enum AFColorRange { + AFCOL_RANGE_UNSPECIFIED = 0, + AFCOL_RANGE_MPEG = 1,///< the normal 219*2^(n-8) "MPEG" YUV ranges + AFCOL_RANGE_JPEG = 2,///< the normal 2^n-1 "JPEG" YUV ranges + AFCOL_RANGE_NB ///< Not part of ABI +}; + +/** + * Location of chroma samples. + * + * Illustration showing the location of the first (top left) chroma sample of the + * image, the left shows only luma, the right + * shows the location of the chroma sample, the 2 could be imagined to overlay + * each other but are drawn separately due to limitations of ASCII + * + * 1st 2nd 1st 2nd horizontal luma sample positions + * v v v v + * ______ ______ + *1st luma line > |X X ... |3 4 X ... X are luma samples, + * | |1 2 1-6 are possible chroma positions + *2nd luma line > |X X ... |5 6 X ... 0 is undefined/unknown position + */ +enum AFChromaLocation { + AFCHROMA_LOC_UNSPECIFIED = 0, + AFCHROMA_LOC_LEFT = 1, ///< MPEG-2/4 4:2:0, H.264 default for 4:2:0 + AFCHROMA_LOC_CENTER = 2, ///< MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0 + AFCHROMA_LOC_TOPLEFT = 3,///< ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2 + AFCHROMA_LOC_TOP = 4, + AFCHROMA_LOC_BOTTOMLEFT = 5, + AFCHROMA_LOC_BOTTOM = 6, + AFCHROMA_LOC_NB///< Not part of ABI +}; + typedef enum InterlacedType_t { InterlacedType_UNKNOWN = -1, InterlacedType_NO, @@ -183,6 +301,15 @@ typedef struct Source_meta { } Source_meta; +typedef struct VideoColorInfo { + enum AFColorRange color_range; + enum AFColorPrimaries color_primaries; + enum AFColorTransferCharacteristic color_trc; + enum AFColorSpace color_space; + enum AFChromaLocation chroma_location; +} VideoColorInfo; + + typedef struct { Stream_type type; int64_t duration; @@ -217,6 +344,10 @@ typedef struct { int displayHeight; double avg_fps; enum AFPixelFormat pixel_fmt; + /** + * Video only. Additional colorspace characteristics. + */ + VideoColorInfo color_info; int pid; int no_program; @@ -231,6 +362,13 @@ typedef struct { char *description; Source_meta *meta; + + //add for stand drm(WideVine,FairPlay...) + char* keyUrl; + char* keyFormat; + + float ptsTimeBase; + } Stream_meta; @@ -264,6 +402,7 @@ enum dec_flag { dec_flag_out, dec_flag_direct, dec_flag_adaptive, + dec_flag_passthrough_info, // adjust setting to output frames as soon as possiable. dec_flag_output_frame_asap, }; @@ -273,6 +412,7 @@ enum dec_flag { #define DECFLAG_OUT (1u << dec_flag_out) #define DECFLAG_DIRECT (1u << dec_flag_direct) #define DECFLAG_ADAPTIVE (1u << dec_flag_adaptive) +#define DECFLAG_PASSTHROUGH_INFO (1 << dec_flag_passthrough_info) #define DECFLAG_OUTPUT_FRAME_ASAP (1u << dec_flag_output_frame_asap) typedef struct mediaFrame_t mediaFrame; diff --git a/framework/utils/Android/JniUtils.cpp b/framework/utils/Android/JniUtils.cpp index e860e9d5a..0a0bce52e 100644 --- a/framework/utils/Android/JniUtils.cpp +++ b/framework/utils/Android/JniUtils.cpp @@ -11,31 +11,44 @@ #include "CallObjectMethod.h" #include "GetStringUTFChars.h" -char *JniUtils::jByteArrayToChars(JNIEnv *env, jbyteArray bytearray) +char *JniUtils::jByteArrayToChars(JNIEnv *env, jbyteArray bytearray) { + char *chars = nullptr; + jByteArrayToChars(env, bytearray, &chars); + return chars; +} + +int JniUtils::jByteArrayToChars(JNIEnv *env, jbyteArray bytearray, char **dst) { - jbyte *bytes = env->GetByteArrayElements(bytearray, 0); + if (bytearray == nullptr || env == nullptr) { + return 0; + } + int chars_len = env->GetArrayLength(bytearray); - char *chars = static_cast(malloc(chars_len)); - memcpy(chars, bytes, chars_len); + if (*dst == nullptr) { + *dst = static_cast(malloc(chars_len)); + } + jbyte *bytes = env->GetByteArrayElements(bytearray, 0); + memcpy(*dst, bytes, chars_len); env->ReleaseByteArrayElements(bytearray, bytes, 0); JniException::clearException(env); - return chars; + return chars_len; } -char *JniUtils::jByteArrayToChars_New(JNIEnv *env, jbyteArray bytearray) -{ +char *JniUtils::jByteArrayToChars_New(JNIEnv *env, jbyteArray bytearray) { + if (bytearray == nullptr || env == nullptr) { + return nullptr; + } + jbyte *bytes = env->GetByteArrayElements(bytearray, 0); int chars_len = env->GetArrayLength(bytearray); - char *chars = new char[chars_len + 1](); + char *chars = new char[chars_len](); memcpy(chars, bytes, chars_len); env->ReleaseByteArrayElements(bytearray, bytes, 0); JniException::clearException(env); return chars; } - -jobject JniUtils::cmap2Jmap(JNIEnv *env, std::map cmap) -{ +jobject JniUtils::cmap2Jmap(JNIEnv *env, std::map cmap) { FindClass jmapclass(env, "java/util/HashMap"); jmethodID initMethod = env->GetMethodID(jmapclass.getClass(), "", "()V"); jmethodID putMethod = env->GetMethodID(jmapclass.getClass(), "put", @@ -54,12 +67,52 @@ jobject JniUtils::cmap2Jmap(JNIEnv *env, std::map cmap return jmap; } +std::map JniUtils::jmap2cmap(JNIEnv *env, jobject jobj) { + std::map cmap; + if (jobj == nullptr || env == nullptr) { + return cmap; + } + + FindClass jmapclass(env, "java/util/HashMap"); + jmethodID jkeysetmid = env->GetMethodID(jmapclass.getClass(), "keySet", "()Ljava/util/Set;"); + jmethodID jgetmid = env->GetMethodID(jmapclass.getClass(), "get", + "(Ljava/lang/Object;)Ljava/lang/Object;"); + jobject jsetkey = env->CallObjectMethod(jobj, jkeysetmid); + FindClass jsetclass(env, "java/util/Set"); + jmethodID jtoArraymid = env->GetMethodID(jsetclass.getClass(), "toArray", + "()[Ljava/lang/Object;"); + jobjectArray jobjArray = (jobjectArray) env->CallObjectMethod(jsetkey, jtoArraymid); + if (jobjArray != nullptr) { + jsize arraysize = env->GetArrayLength(jobjArray); + int i = 0; + for (i = 0; i < arraysize; i++) { + jstring jkey = (jstring) env->GetObjectArrayElement(jobjArray, i); + jstring jvalue = (jstring) env->CallObjectMethod(jobj, jgetmid, jkey); + GetStringUTFChars key(env, jkey); + GetStringUTFChars value(env, jvalue); + cmap[key.getChars()] = value.getChars(); + } + } + if (jobjArray != nullptr) { + env->DeleteLocalRef(jobjArray); + } + + if (jsetkey != nullptr) { + env->DeleteLocalRef(jsetkey); + } + + return cmap; +} + + +std::string JniUtils::callStringMethod(JNIEnv *env, jobject jObj, jmethodID method) { + if (env == nullptr || jObj == nullptr || method == nullptr) { + return ""; + } -std::string JniUtils::callStringMethod(JNIEnv *env, jobject jObj, jmethodID method) -{ CallObjectMethod tmpGetObject(env, jObj, method); auto objec = (jstring) tmpGetObject.getValue(); GetStringUTFChars tmpObj(env, objec); char *ch_Obj = tmpObj.getChars(); return ch_Obj == nullptr ? "" : std::string(ch_Obj); -} \ No newline at end of file +} diff --git a/framework/utils/Android/JniUtils.h b/framework/utils/Android/JniUtils.h index 7e242ef99..95538e876 100644 --- a/framework/utils/Android/JniUtils.h +++ b/framework/utils/Android/JniUtils.h @@ -12,10 +12,15 @@ class JniUtils { public: - static char* jByteArrayToChars(JNIEnv *env, jbyteArray bytearray); - static char* jByteArrayToChars_New(JNIEnv *env, jbyteArray bytearray); + static char *jByteArrayToChars(JNIEnv *env, jbyteArray bytearray); - static jobject cmap2Jmap(JNIEnv *env , std::map theMap); + static int jByteArrayToChars(JNIEnv *env, jbyteArray bytearray, char **dst); + + static char *jByteArrayToChars_New(JNIEnv *env, jbyteArray bytearray); + + static jobject cmap2Jmap(JNIEnv *env, std::map theMap); + + static std::map jmap2cmap(JNIEnv *env, jobject jobj); static std::string callStringMethod(JNIEnv *pEnv, jobject jObj, jmethodID method); }; diff --git a/framework/utils/Android/NewByteArray.cpp b/framework/utils/Android/NewByteArray.cpp new file mode 100644 index 000000000..3994df5f9 --- /dev/null +++ b/framework/utils/Android/NewByteArray.cpp @@ -0,0 +1,36 @@ +// +// Created by SuperMan on 2020/10/19. +// + +#include "NewByteArray.h" +#include +#include "JniException.h" + + +NewByteArray::NewByteArray(JNIEnv *pEnv, const void *source, int len) +{ + if (source == nullptr || pEnv == nullptr) { + mResult = nullptr; + mEnv = nullptr; + } else { + mEnv = pEnv; + mResult = pEnv->NewByteArray(len); + pEnv->SetByteArrayRegion(mResult, 0, len, (jbyte *) (source)); + JniException::clearException(mEnv); + } +} + +NewByteArray::~NewByteArray() +{ + if (mResult != nullptr) { + mEnv->DeleteLocalRef(mResult); + JniException::clearException(mEnv); + } + + mResult = nullptr; +} + +jbyteArray NewByteArray::getArray() +{ + return mResult; +} \ No newline at end of file diff --git a/framework/utils/Android/NewByteArray.h b/framework/utils/Android/NewByteArray.h new file mode 100644 index 000000000..19b0eecf0 --- /dev/null +++ b/framework/utils/Android/NewByteArray.h @@ -0,0 +1,36 @@ +// +// Created by SuperMan on 2020/10/19. +// + +#ifndef SOURCE_NEWBYTEARRAY_H +#define SOURCE_NEWBYTEARRAY_H + +#include + +class NewByteArray { +public: + NewByteArray(JNIEnv *pEnv, const void*source , int len); + + ~NewByteArray(); + +public: + jbyteArray getArray(); + +private: + JNIEnv *mEnv; + jbyteArray mResult; + + +private: + NewByteArray(NewByteArray &) + { + + } + + const NewByteArray &operator=(const NewByteArray &); + +}; + + + +#endif //SOURCE_NEWBYTEARRAY_H diff --git a/framework/utils/Android/NewHashMap.cpp b/framework/utils/Android/NewHashMap.cpp new file mode 100644 index 000000000..350f138f7 --- /dev/null +++ b/framework/utils/Android/NewHashMap.cpp @@ -0,0 +1,50 @@ +// +// Created by SuperMan on 2020/10/23. +// + +#include "NewHashMap.h" + + +#include "FindClass.h" + +static const char *HashMapPath = "java/util/LinkedHashMap"; + +static jclass gj_HashMap_class = nullptr; +static jmethodID gj_HashMap_construct = nullptr; +static jmethodID gj_HashMap_put = nullptr; + +NewHashMap::NewHashMap(JNIEnv *env) { + if (env == nullptr) { + return; + } + + + if (gj_HashMap_class == nullptr) { + FindClass mapClass(env, HashMapPath); + gj_HashMap_class = static_cast(env->NewGlobalRef(mapClass.getClass())); + gj_HashMap_construct = env->GetMethodID(gj_HashMap_class, "", "()V"); + gj_HashMap_put = env->GetMethodID(gj_HashMap_class, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + } + + mEnv = env; + jobject object = env->NewObject(gj_HashMap_class, gj_HashMap_construct); + mResult = env->NewGlobalRef(object); + env->DeleteLocalRef(object); + +} + +NewHashMap::~NewHashMap() { + if (mEnv != nullptr && mResult != nullptr) { + mEnv->DeleteGlobalRef(mResult); + } +} + +jobject NewHashMap::getMap() { + return mResult; +} + +void NewHashMap::put(jobject key , jobject value ) { + if (mEnv != nullptr && mResult != nullptr) { + mEnv->CallObjectMethod(mResult, gj_HashMap_put, key, value); + } +} \ No newline at end of file diff --git a/framework/utils/Android/NewHashMap.h b/framework/utils/Android/NewHashMap.h new file mode 100644 index 000000000..8a9f5c6ef --- /dev/null +++ b/framework/utils/Android/NewHashMap.h @@ -0,0 +1,36 @@ +// +// Created by SuperMan on 2020/10/23. +// + +#ifndef SOURCE_NEWHASHMAP_H +#define SOURCE_NEWHASHMAP_H + +#include +#include + +class NewHashMap { + +public: + NewHashMap(JNIEnv *pEnv); + + ~NewHashMap(); + +public: + jobject getMap(); + + void put(jobject key , jobject value); + +private: + JNIEnv *mEnv{nullptr}; + jobject mResult{nullptr}; + +private: + NewHashMap(NewHashMap &) { + + } + + const NewHashMap &operator=(const NewHashMap &); + +}; + +#endif //SOURCE_NEWHASHMAP_H diff --git a/framework/utils/Android/NewLinkedList.cpp b/framework/utils/Android/NewLinkedList.cpp new file mode 100644 index 000000000..0cd29d88c --- /dev/null +++ b/framework/utils/Android/NewLinkedList.cpp @@ -0,0 +1,49 @@ +// +// Created by SuperMan on 2020/10/19. +// + +#include "NewLinkedList.h" + +#include "NewLinkedList.h" +#include "FindClass.h" + +static const char *LinkedListPath = "java/util/LinkedList"; + +static jclass gj_linkedList_class = nullptr; +static jmethodID gj_linkedList_construct = nullptr; +static jmethodID gj_linkedList_add = nullptr; + +NewLinkedList::NewLinkedList(JNIEnv *env) { + if (env == nullptr) { + return; + } + + if (gj_linkedList_class == nullptr) { + FindClass listClass(env, LinkedListPath); + gj_linkedList_class = static_cast(env->NewGlobalRef(listClass.getClass())); + gj_linkedList_construct = env->GetMethodID(gj_linkedList_class, "", "()V"); + gj_linkedList_add = env->GetMethodID(gj_linkedList_class, "add", "(Ljava/lang/Object;)Z"); + } + + mEnv = env; + jobject object = env->NewObject(gj_linkedList_class, gj_linkedList_construct, ""); + mResult = env->NewGlobalRef(object); + env->DeleteLocalRef(object); + +} + +NewLinkedList::~NewLinkedList() { + if (mEnv != nullptr && mResult != nullptr) { + mEnv->DeleteGlobalRef(mResult); + } +} + +jobject NewLinkedList::getList() { + return mResult; +} + +void NewLinkedList::add(jobject value) { + if (mEnv != nullptr && mResult != nullptr) { + mEnv->CallBooleanMethod(mResult, gj_linkedList_add, value); + } +} \ No newline at end of file diff --git a/framework/utils/Android/NewLinkedList.h b/framework/utils/Android/NewLinkedList.h new file mode 100644 index 000000000..8f90d982c --- /dev/null +++ b/framework/utils/Android/NewLinkedList.h @@ -0,0 +1,39 @@ +// +// Created by SuperMan on 2020/10/19. +// + +#ifndef SOURCE_NEWLINKEDLIST_H +#define SOURCE_NEWLINKEDLIST_H + + +#include +#include + +class NewLinkedList { + + +public: + NewLinkedList(JNIEnv *pEnv); + + ~NewLinkedList(); + +public: + jobject getList(); + + void add(jobject value); + +private: + JNIEnv *mEnv{nullptr}; + jobject mResult{nullptr}; + +private: + NewLinkedList(NewLinkedList &) { + + } + + const NewLinkedList &operator=(const NewLinkedList &); + +}; + + +#endif //SOURCE_NEWLINKEDLIST_H diff --git a/framework/utils/Android/NewStringUTF.cpp b/framework/utils/Android/NewStringUTF.cpp index 36d58f41e..8da589da0 100644 --- a/framework/utils/Android/NewStringUTF.cpp +++ b/framework/utils/Android/NewStringUTF.cpp @@ -13,12 +13,17 @@ NewStringUTF::NewStringUTF(JNIEnv *pEnv, const char *source) mEnv = nullptr; } else { mEnv = pEnv; - mResult = pEnv->NewStringUTF(source); + const char *errorKind = nullptr; + checkUtfBytes(source, &errorKind); + if (errorKind == nullptr) { + mResult = pEnv->NewStringUTF(source); + } else { + mResult = pEnv->NewStringUTF(""); + } JniException::clearException(mEnv); } } - NewStringUTF::~NewStringUTF() { if (mResult != nullptr) { @@ -33,3 +38,53 @@ jstring NewStringUTF::getString() { return mResult; } + +char NewStringUTF::checkUtfBytes(const char *bytes, const char **errorKind) +{ + while (*bytes != '\0') { + char utf8 = *(bytes++); + // Switch on the high four bits. + switch (utf8 >> 4) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + // Bit pattern 0xxx. No need for any extra bytes. + break; + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0f: + /* + * Bit pattern 10xx or 1111, which are illegal start bytes. + * Note: 1111 is valid for normal UTF-8, but not the + * modified UTF-8 used here. + */ + *errorKind = "start"; + return utf8; + case 0x0e: + // Bit pattern 1110, so there are two additional bytes. + utf8 = *(bytes++); + if ((utf8 & 0xc0) != 0x80) { + *errorKind = "continuation"; + return utf8; + } + // Fall through to take care of the final byte. + case 0x0c: + case 0x0d: + // Bit pattern 110x, so there is one additional byte. + utf8 = *(bytes++); + if ((utf8 & 0xc0) != 0x80) { + *errorKind = "continuation"; + return utf8; + } + break; + } + } + return 0; +} \ No newline at end of file diff --git a/framework/utils/Android/NewStringUTF.h b/framework/utils/Android/NewStringUTF.h index aeccedaba..743f816d5 100644 --- a/framework/utils/Android/NewStringUTF.h +++ b/framework/utils/Android/NewStringUTF.h @@ -32,6 +32,8 @@ class CICADA_CPLUS_EXTERN NewStringUTF { const NewStringUTF &operator=(const NewStringUTF &); +private: + static char checkUtfBytes(const char *bytes, const char **errorKind); }; diff --git a/framework/utils/AsyncJob.cpp b/framework/utils/AsyncJob.cpp index 89404241a..50daa477a 100644 --- a/framework/utils/AsyncJob.cpp +++ b/framework/utils/AsyncJob.cpp @@ -11,16 +11,23 @@ #define LOG_TAG "AsyncJob" #include "timer.h" +static int AsyncJobLive = -1; + namespace Cicada { AsyncJob AsyncJob::sInstance{}; AsyncJob::AsyncJob() { mThread = NEW_AF_THREAD(runJobs); + AsyncJobLive = 1; } AsyncJob::~AsyncJob() { + AsyncJobLive = 0; +#ifdef WIN32 + mThread->forceStop(); +#endif delete mThread; while (0 < mFuncs.size()) { @@ -32,6 +39,7 @@ namespace Cicada { AsyncJob *AsyncJob::Instance(void) { + if (AsyncJobLive == 0) return nullptr; return &sInstance; } diff --git a/framework/utils/CMakeLists.txt b/framework/utils/CMakeLists.txt index c579075d6..6162c7f3d 100644 --- a/framework/utils/CMakeLists.txt +++ b/framework/utils/CMakeLists.txt @@ -1,6 +1,12 @@ cmake_minimum_required(VERSION 3.6) project(framework_utils) set(CMAKE_CXX_STANDARD 11) + +include(../module_config.cmake) +if(ENABLE_CODEC_HEVC) + add_definitions(-DENABLE_CODEC_HEVC) +endif() + set(SOURCE_FILES AutoAVFrame.h CicadaJSON.h @@ -55,6 +61,8 @@ set(SOURCE_FILES ../base/options.h UrlUtils.cpp UrlUtils.h + DrmUtils.cpp + DrmUtils.h ) list(APPEND SOURCE_FILES @@ -68,7 +76,8 @@ if (APPLE) list(APPEND SOURCE_FILES ../base/media/PBAFFrame.cpp ../base/media/PBAFFrame.h - ../base/media/avFrame2pixelBuffer.c + appleLog.m + pixelBufferConvertor.cpp ) endif () @@ -112,11 +121,18 @@ if (ANDROID) Android/GetObjectField.h Android/JniUtils.cpp Android/JniUtils.h + Android/NewByteArray.cpp + Android/NewByteArray.h + Android/NewLinkedList.cpp + Android/NewLinkedList.h + Android/NewHashMap.cpp + Android/NewHashMap.h ) elseif (IOS) set(SOURCE_FILES ${SOURCE_FILES} oscl/ios_utils.c - ../base/media/subTitlePacket.cpp ../base/media/subTitlePacket.h) + ../base/media/subTitlePacket.cpp + ../base/media/subTitlePacket.h) endif () include_directories( diff --git a/framework/utils/CicadaThumbnailParser.cpp b/framework/utils/CicadaThumbnailParser.cpp index 1a68c7be7..567bf634c 100644 --- a/framework/utils/CicadaThumbnailParser.cpp +++ b/framework/utils/CicadaThumbnailParser.cpp @@ -66,18 +66,25 @@ bool CicadaThumbnailParser::getLine(const std::string &input, std::size_t &start bool CicadaThumbnailParser::getText(const std::string &line, ThumbnailInfo &info) { - std::size_t pos = line.find('#'); + if (line.empty()) { + return false; + } - if (pos == std::string::npos) { + std::size_t pos = line.find("#"); + if (pos == 0) { return false; } + //TODO move this, not standard syntax + pos = line.find("#xywh="); + if (pos == std::string::npos) { + info.URI = line; + return true; + } + info.URI = line.substr(0, pos); pos = line.find('=', pos); - if (pos == std::string::npos) { - return false; - } string rect = line.substr(pos+1); AfString::trimString(rect); std::replace(rect.begin(), rect.end(), ',', ' '); diff --git a/framework/utils/CicadaUtils.cpp b/framework/utils/CicadaUtils.cpp index ef412f79c..5d22d9a45 100644 --- a/framework/utils/CicadaUtils.cpp +++ b/framework/utils/CicadaUtils.cpp @@ -99,10 +99,11 @@ string CicadaUtils::base64dec(const string &str) int CicadaUtils::base64dec(const string &str, char **dst) { int out_size = AV_BASE64_DECODE_SIZE(str.size()); - uint8_t *out = (uint8_t *) malloc(out_size); + uint8_t *out = (uint8_t *) malloc(out_size + 1); int ret = av_base64_decode(out, str.c_str(), out_size); if (ret > 0) { + out[ret] = 0; *dst = reinterpret_cast(out); return ret; } else { diff --git a/framework/utils/DrmUtils.cpp b/framework/utils/DrmUtils.cpp new file mode 100644 index 000000000..9ec6e83b5 --- /dev/null +++ b/framework/utils/DrmUtils.cpp @@ -0,0 +1,12 @@ +// +// Created by SuperMan on 12/2/20. +// + +#include "DrmUtils.h" + +bool DrmUtils::isSupport(const std::string &drmFormat) { +#ifdef ANDROID + return drmFormat == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"; +#endif + return false; +} diff --git a/framework/utils/DrmUtils.h b/framework/utils/DrmUtils.h new file mode 100644 index 000000000..7dff25b0c --- /dev/null +++ b/framework/utils/DrmUtils.h @@ -0,0 +1,17 @@ +// +// Created by SuperMan on 12/2/20. +// + +#ifndef SOURCE_DRMUTILS_H +#define SOURCE_DRMUTILS_H + + +#include + +class DrmUtils { +public: + static bool isSupport(const std::string& drmFormat); +}; + + +#endif //SOURCE_DRMUTILS_H diff --git a/framework/utils/afThread.cpp b/framework/utils/afThread.cpp index 0aa9fc353..139583a4c 100644 --- a/framework/utils/afThread.cpp +++ b/framework/utils/afThread.cpp @@ -177,6 +177,15 @@ void afThread::stop() AF_TRACE; } +void afThread::forceStop() +{ + if (mThreadPtr) { + mThreadPtr->detach(); + delete mThreadPtr; + mThreadPtr = nullptr; + } +} + //void afThread::detach() //{ // std::unique_lock uMutex(mMutex); @@ -186,19 +195,22 @@ void afThread::stop() afThread::~afThread() { - std::lock_guard guard(mMutex); - mTryPaused = false; - { - std::unique_lock sleepMutex(mSleepMutex); - mThreadStatus = THREAD_STATUS_IDLE; - } - mSleepCondition.notify_one(); + if (mThreadPtr) { + std::lock_guard guard(mMutex); + mTryPaused = false; + { + std::unique_lock sleepMutex(mSleepMutex); + mThreadStatus = THREAD_STATUS_IDLE; + } + mSleepCondition.notify_one(); - if (mThreadPtr && mThreadPtr->joinable()) { - mThreadPtr->join(); - } + if (mThreadPtr && mThreadPtr->joinable()) { + mThreadPtr->join(); + } - delete mThreadPtr; + delete mThreadPtr; + mThreadPtr = nullptr; + } } void afThread::setBeginCallback(const thread_beginCallback &callback) diff --git a/framework/utils/afThread.h b/framework/utils/afThread.h index bc239b4d6..da8daa957 100755 --- a/framework/utils/afThread.h +++ b/framework/utils/afThread.h @@ -42,6 +42,8 @@ class CICADA_CPLUS_EXTERN afThread { void stop(); + void forceStop(); + AF_THREAD_STATUS getStatus() { return mThreadStatus; diff --git a/framework/utils/appleLog.h b/framework/utils/appleLog.h new file mode 100644 index 000000000..ee52846cb --- /dev/null +++ b/framework/utils/appleLog.h @@ -0,0 +1,10 @@ +// +// Created by pingkai on 2020/9/8. +// + +#ifndef CICADAMEDIA_APPLELOG_H +#define CICADAMEDIA_APPLELOG_H + +void appleNSlogC(const char level, const char *fmt); + +#endif//CICADAMEDIA_APPLELOG_H diff --git a/framework/utils/appleLog.m b/framework/utils/appleLog.m new file mode 100644 index 000000000..48ec39e1f --- /dev/null +++ b/framework/utils/appleLog.m @@ -0,0 +1,11 @@ +// +// Created by pingkai on 2020/9/8. +// + +#include "appleLog.h" +#import + +void appleNSlogC(const char level, const char *fmt) +{ + NSLog(@"AliFrameWork %c %s", level, fmt); +} \ No newline at end of file diff --git a/framework/utils/errors/framework_error.c b/framework/utils/errors/framework_error.c index bde651fa7..6f529a908 100644 --- a/framework/utils/errors/framework_error.c +++ b/framework/utils/errors/framework_error.c @@ -2,9 +2,10 @@ // Created by moqi on 2018/6/11. // -#include -#include "../frame_work_log.h" #include "framework_error.h" +#include "../frame_work_log.h" +#include +#include const char *network_err2_string(uint8_t Errno) { @@ -21,6 +22,9 @@ const char *network_err2_string(uint8_t Errno) case network_errno_could_not_connect: return "Couldn't connect to server"; + case network_errno_url_malformat: + return "URL using bad/illegal format or missing URL"; + case network_errno_http_403: return "Server returned 403 Forbidden (access denied)"; @@ -61,6 +65,30 @@ const char *codec_err2_string(uint8_t Errno) } } +const char *drm_err2_string(uint8_t Errno) +{ + switch (Errno) { + case drm_error_denied_by_server: + return "denied by server"; + case drm_error_key_response_null: + return "key response is null"; + case drm_error_provision_response_null: + return "provision response is null"; + case drm_error_resource_busy: + return "resource busy"; + case drm_error_unsupport_scheme: + return "unsupport scheme"; + case drm_error_released: + return "drm released"; + case drm_error_provision_fail: + return "drm provision fail"; + case drm_error_unknow: + return "unknow drm error"; + default: + return "Unknown drm error"; + } +} + const char *strerror_ext(uint8_t Errno) { if (Errno < 200) @@ -110,6 +138,9 @@ const char *framework_err2_string(error_type err) case error_class_internal: return internal_err2_string(Errno); + case error_class_drm: + return drm_err2_string(Errno); + default: return "Unknown Error"; } @@ -134,6 +165,11 @@ bool isHttpError(error_type err) return false; } +bool isLocalFileError(error_type err) +{ + return err == -ENOENT || err == -EACCES; +} + error_type gen_framework_http_errno(int httpCode) { uint8_t errorno = 0; diff --git a/framework/utils/errors/framework_error.h b/framework/utils/errors/framework_error.h index 3b382604e..7e906b834 100644 --- a/framework/utils/errors/framework_error.h +++ b/framework/utils/errors/framework_error.h @@ -18,6 +18,7 @@ enum error_class { error_class_network, error_class_codec, error_class_format, + error_class_drm, error_class_internal = 0x10, }; @@ -34,6 +35,7 @@ enum network_errno { network_errno_resolve, network_errno_connect_timeout, network_errno_could_not_connect, + network_errno_url_malformat, network_errno_http_400 = 100, network_errno_http_403, @@ -51,6 +53,19 @@ enum codec_error { codec_error_video_device_error, }; +enum drm_error { + drm_error_none = 0, + drm_error_unsupport_scheme, + drm_error_resource_busy, + drm_error_key_response_null, + drm_error_provision_response_null, + drm_error_denied_by_server, + drm_error_released, + drm_error_provision_fail, + + drm_error_unknow = 99, +}; + // TODO: SEGEND define here? enum internal_errno { internal_errno_exit = 1, //interrupted by user @@ -75,6 +90,8 @@ static inline uint8_t get_errno(error_type err) bool isHttpError(error_type err); +bool isLocalFileError(error_type err); + error_type gen_framework_http_errno(int httpCode); const char *framework_err2_string(error_type err); diff --git a/framework/utils/ffmpeg_utils.c b/framework/utils/ffmpeg_utils.c index 75e28ed78..934295497 100644 --- a/framework/utils/ffmpeg_utils.c +++ b/framework/utils/ffmpeg_utils.c @@ -2,16 +2,26 @@ // Created by moqi on 2019-07-08. // +#include "ffmpeg_utils.h" +#include #include -#include +#include +#include +#include +#include +#include #include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include "ffmpeg_utils.h" static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; @@ -90,6 +100,7 @@ static void ffmpeg_log_back(void *ptr, int level, const char *fmt, va_list vl) static void ffmpeg_init_once() { + AF_LOGI("Ffmpeg version %s", av_version_info()); av_lockmgr_register(lockmgr); av_log_set_level(AV_LOG_INFO); av_log_set_callback(ffmpeg_log_back); @@ -296,14 +307,14 @@ enum AFCodecID AVCodec2CicadaCodec(enum AVCodecID codec) return AF_CODEC_ID_AV1; /* subtitle */ -// case AV_CODEC_ID_TEXT: -// return AF_CODEC_ID_TEXT; -// -// case AV_CODEC_ID_SSA: -// return AF_CODEC_ID_SSA; -// -// case AV_CODEC_ID_SRT: -// return AF_CODEC_ID_SRT; + case AV_CODEC_ID_WEBVTT: + return AF_CODEC_ID_WEBVTT; + // + // case AV_CODEC_ID_SSA: + // return AF_CODEC_ID_SSA; + // + // case AV_CODEC_ID_SRT: + // return AF_CODEC_ID_SRT; default: // if (codec->codec_id == AV_CODEC_ID_NONE && (!av_strcasecmp((char *) &codec->codec_tag, "dtse"))) @@ -363,13 +374,15 @@ typedef struct pix_fmt_pair_t { } pix_fmt_pair; static pix_fmt_pair pix_fmt_pair_table[] = { - {AF_PIX_FMT_NONE, AV_PIX_FMT_NONE}, - {AF_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P}, - {AF_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P}, - {AF_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ420P}, - {AF_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ422P}, - {AF_PIX_FMT_D3D11, AV_PIX_FMT_D3D11}, - {AF_PIX_FMT_DXVA2_VLD, AV_PIX_FMT_DXVA2_VLD}, + {AF_PIX_FMT_NONE, AV_PIX_FMT_NONE}, + {AF_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P}, + {AF_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P}, + {AF_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ420P}, + {AF_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ422P}, + {AF_PIX_FMT_YUV420P10BE, AV_PIX_FMT_YUV420P10BE}, + {AF_PIX_FMT_YUV420P10LE, AV_PIX_FMT_YUV420P10LE}, + {AF_PIX_FMT_D3D11, AV_PIX_FMT_D3D11}, + {AF_PIX_FMT_DXVA2_VLD, AV_PIX_FMT_DXVA2_VLD}, }; int AVPixFmt2Cicada(enum AVPixelFormat fmt) @@ -505,15 +518,26 @@ int get_stream_meta(const struct AVStream *pStream, Stream_meta *meta) // meta->cicada_codec_context = pStream->codec; meta->cicada_codec_context_size = sizeof(AVCodecContext); // meta->index = stream_index; - + meta->ptsTimeBase = (float) pStream->time_base.num * 1000000 / (float) pStream->time_base.den; if (codec_type == AVMEDIA_TYPE_VIDEO) { - if (pStream->codecpar->sample_aspect_ratio.num) { + if (pStream->sample_aspect_ratio.num && av_cmp_q(pStream->sample_aspect_ratio, pStream->codecpar->sample_aspect_ratio)) { + AVRational display_aspect_ratio; + av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, + pStream->codecpar->width * (int64_t) pStream->sample_aspect_ratio.num, + pStream->codecpar->height * (int64_t) pStream->sample_aspect_ratio.den, 1024 * 1024); + meta->displayWidth = pStream->codecpar->width; + meta->displayWidth *= pStream->sample_aspect_ratio.num; + meta->displayWidth /= pStream->sample_aspect_ratio.den; + meta->displayHeight = pStream->codecpar->height; + AF_LOGI("DAR %d:%d", meta->displayWidth, meta->displayHeight); + } else if (pStream->codecpar->sample_aspect_ratio.num){ meta->displayWidth = pStream->codecpar->width; meta->displayWidth *= pStream->codecpar->sample_aspect_ratio.num; meta->displayWidth /= pStream->codecpar->sample_aspect_ratio.den; meta->displayHeight = pStream->codecpar->height; -// av_log(NULL, AV_LOG_ERROR, "DAR %d:%d", meta->displayWidth, meta->displayHeight); - } else { + AF_LOGI("DAR %d:%d", meta->displayWidth, meta->displayHeight); + } + else { meta->displayWidth = meta->displayHeight = 0; } @@ -522,6 +546,11 @@ int get_stream_meta(const struct AVStream *pStream, Stream_meta *meta) meta->height = pStream->codecpar->height; meta->profile = pStream->codecpar->profile; meta->pixel_fmt = pStream->codecpar->format; + meta->color_info.color_range = (enum AFColorRange) pStream->codecpar->color_range; + meta->color_info.color_primaries = (enum AFColorPrimaries) pStream->codecpar->color_primaries; + meta->color_info.color_space = (enum AFColorSpace) pStream->codecpar->color_space; + meta->color_info.chroma_location = (enum AFChromaLocation) pStream->codecpar->chroma_location; + meta->color_info.color_trc = (enum AFColorTransferCharacteristic) pStream->codecpar->color_trc; if (meta->codec == AF_CODEC_ID_H264) { meta->interlaced = InterlacedType_UNKNOWN; @@ -656,7 +685,7 @@ void copyPCMData(const AVFrame *frame, uint8_t *buffer) } } } else { - memcpy(buffer, frame->extended_data[0], (size_t) (sampleSize * frame->nb_samples * frame->channels)); + memcpy(buffer, frame->extended_data[0], ((size_t) sampleSize * frame->nb_samples * frame->channels)); } } @@ -693,7 +722,7 @@ size_t copyPCMDataWithOffset(const AVFrame *frame, int frameOffset, uint8_t *out *frameClear = true; return totalWriteSize; } else { - if (outSize > (sampleSize * frame->nb_samples * frame->channels - frameOffset)) { + if (outSize >= (sampleSize * frame->nb_samples * frame->channels - frameOffset)) { totalWriteSize = (sampleSize * frame->nb_samples * frame->channels - frameOffset); *frameClear = true; } else { @@ -720,7 +749,7 @@ void copyPCMData2(const AVFrame *frame, fillBufferCallback fillCallback, void *a } } else { if (fillCallback != NULL) { - fillCallback(args, frame->extended_data[0], (size_t) (sampleSize * frame->nb_samples * frame->channels)); + fillCallback(args, frame->extended_data[0], ((size_t) sampleSize * frame->nb_samples * frame->channels)); } } } @@ -775,3 +804,597 @@ const char *getErrorString(int err) av_strerror(err, errorBuff, sizeof(errorBuff)); return errorBuff; } + +int h2645_ps_to_nalu(const uint8_t *src, int src_size, uint8_t **out, int *out_size) +{ + int i; + int ret = 0; + uint8_t *p = NULL; + static const uint8_t nalu_header[] = { 0x00, 0x00, 0x00, 0x01 }; + + if (!out || !out_size) { + return AVERROR(EINVAL); + } + + p = av_malloc(sizeof(nalu_header) + src_size); + if (!p) { + return AVERROR(ENOMEM); + } + + *out = p; + *out_size = sizeof(nalu_header) + src_size; + + memcpy(p, nalu_header, sizeof(nalu_header)); + memcpy(p + sizeof(nalu_header), src, src_size); + + /* Escape 0x00, 0x00, 0x0{0-3} pattern */ + for (i = 4; i < *out_size; i++) { + if (i < *out_size - 3 && + p[i + 0] == 0 && + p[i + 1] == 0 && + p[i + 2] <= 3) { + uint8_t *new_data; + + *out_size += 1; + new_data = av_realloc(*out, *out_size); + if (!new_data) { + ret = AVERROR(ENOMEM); + goto done; + } + *out = p = new_data; + + i = i + 2; + memmove(p + i + 1, p + i, *out_size - (i + 1)); + p[i] = 0x03; + } + } + done: + if (ret < 0) { + av_freep(out); + *out_size = 0; + } + + return ret; +} + +int parse_h264_extraData(enum AVCodecID codecId, const uint8_t* extraData,int extraData_size, + uint8_t** sps_data ,int*sps_data_size, + uint8_t** pps_data, int* pps_data_size, + int* nal_length_size + ) +{ + AVCodec *codec = avcodec_find_decoder(codecId); + if (codec == NULL) { + return -1; + } + + AVCodecContext *avctx = avcodec_alloc_context3((const AVCodec *) codec); + if (avctx == NULL) { + return -1; + } + + int ret; + + H264ParamSets ps; + const PPS *pps = NULL; + const SPS *sps = NULL; + int is_avc = 0; + + memset(&ps, 0, sizeof(ps)); + + ret = ff_h264_decode_extradata((const uint8_t *) extraData, extraData_size, &ps, &is_avc, nal_length_size, 0, avctx); + if (ret < 0) { + goto done; + } + + int i; + for (i = 0; i < MAX_PPS_COUNT; i++) { + if (ps.pps_list[i]) { + pps = (const PPS *) ps.pps_list[i]->data; + break; + } + } + + if (pps) { + if (ps.sps_list[pps->sps_id]) { + sps = (const SPS *) ps.sps_list[pps->sps_id]->data; + } + } + + if (pps && sps) { + + if ((ret = h2645_ps_to_nalu(sps->data, sps->data_size, sps_data, sps_data_size)) < 0) { + goto done; + } + + if ((ret = h2645_ps_to_nalu(pps->data, pps->data_size, pps_data, pps_data_size)) < 0) { + goto done; + } + } else { + av_log(avctx, AV_LOG_ERROR, "Could not extract PPS/SPS from extradata"); + ret = AVERROR_INVALIDDATA; + } + + done: + ff_h264_ps_uninit(&ps); + avcodec_free_context(&avctx); + return ret; +} + +int parse_h265_extraData(enum AVCodecID codecId, const uint8_t* extradata,int extradata_size, + uint8_t** vps_data ,int*vps_data_size, + uint8_t** sps_data ,int*sps_data_size, + uint8_t** pps_data, int* pps_data_size, + int* nal_length_size) +{ +#ifdef ENABLE_CODEC_HEVC + AVCodec *codec = avcodec_find_decoder(codecId); + if (codec == NULL) { + return -1; + } + + AVCodecContext *avctx = avcodec_alloc_context3((const AVCodec *) codec); + if (avctx == NULL) { + return -1; + } + + int i; + int ret; + + HEVCParamSets ps; + HEVCSEI sei; + + const HEVCVPS *vps = NULL; + const HEVCPPS *pps = NULL; + const HEVCSPS *sps = NULL; + int is_nalff = 0; + + memset(&ps, 0, sizeof(ps)); + memset(&sei, 0, sizeof(sei)); + + ret = ff_hevc_decode_extradata(extradata, extradata_size, &ps, &sei, &is_nalff, nal_length_size, 0, 1, avctx); + if (ret < 0) { + goto done; + } + + for (i = 0; i < HEVC_MAX_VPS_COUNT; i++) { + if (ps.vps_list[i]) { + vps = (const HEVCVPS *) ps.vps_list[i]->data; + break; + } + } + + for (i = 0; i < HEVC_MAX_PPS_COUNT; i++) { + if (ps.pps_list[i]) { + pps = (const HEVCPPS *) ps.pps_list[i]->data; + break; + } + } + + if (pps) { + if (ps.sps_list[pps->sps_id]) { + sps = (const HEVCSPS *) ps.sps_list[pps->sps_id]->data; + } + } + + if (vps && pps && sps) { + if ((ret = h2645_ps_to_nalu(vps->data, vps->data_size, vps_data, vps_data_size)) < 0 || + (ret = h2645_ps_to_nalu(sps->data, sps->data_size, sps_data, sps_data_size)) < 0 || + (ret = h2645_ps_to_nalu(pps->data, pps->data_size, pps_data, pps_data_size)) < 0) { + goto done; + } + } else { + av_log(avctx, AV_LOG_ERROR, "Could not extract VPS/PPS/SPS from extradata"); + ret = AVERROR_INVALIDDATA; + } + + done: + ff_hevc_ps_uninit(&ps); + avcodec_free_context(&avctx); + return ret; +#else + return -ENOSYS; +#endif +} +#define RELATIVE_TS_BASE (INT64_MAX - (1LL<<48)) +static int is_relative(int64_t ts) { + return ts > (RELATIVE_TS_BASE - (1LL<<48)); +} +static int has_decode_delay_been_guessed(AVStream *st) +{ + if (st->codecpar->codec_id != AV_CODEC_ID_H264) return 1; + if (!st->info) // if we have left find_stream_info then nb_decoded_frames won't increase anymore for stream copy + return 1; +#if CONFIG_H264_DECODER + if (st->internal->avctx->has_b_frames && + avpriv_h264_has_num_reorder_frames(st->internal->avctx) == st->internal->avctx->has_b_frames) + return 1; +#endif + if (st->internal->avctx->has_b_frames<3) + return st->nb_decoded_frames >= 7; + else if (st->internal->avctx->has_b_frames<4) + return st->nb_decoded_frames >= 18; + else + return st->nb_decoded_frames >= 20; +} +static AVPacketList *get_next_pkt(AVFormatContext *s, AVStream *st, AVPacketList *pktl) +{ + if (pktl->next) + return pktl->next; + if (pktl == s->internal->packet_buffer_end) + return s->internal->parse_queue; + return NULL; +} + +static int64_t select_from_pts_buffer(AVStream *st, int64_t *pts_buffer, int64_t dts) { + int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 && + st->codecpar->codec_id != AV_CODEC_ID_HEVC; + + if(!onein_oneout) { + int delay = st->internal->avctx->has_b_frames; + int i; + + if (dts == AV_NOPTS_VALUE) { + int64_t best_score = INT64_MAX; + for (i = 0; ipts_reorder_error_count[i]) { + int64_t score = st->pts_reorder_error[i] / st->pts_reorder_error_count[i]; + if (score < best_score) { + best_score = score; + dts = pts_buffer[i]; + } + } + } + } else { + for (i = 0; ipts_reorder_error[i]; + diff = FFMAX(diff, st->pts_reorder_error[i]); + st->pts_reorder_error[i] = diff; + st->pts_reorder_error_count[i]++; + if (st->pts_reorder_error_count[i] > 250) { + st->pts_reorder_error[i] >>= 1; + st->pts_reorder_error_count[i] >>= 1; + } + } + } + } + } + + if (dts == AV_NOPTS_VALUE) + dts = pts_buffer[0]; + + return dts; +} +static void update_dts_from_pts(AVFormatContext *s, int stream_index, + AVPacketList *pkt_buffer) +{ + AVStream *st = s->streams[stream_index]; + int delay = st->internal->avctx->has_b_frames; + int i; + + int64_t pts_buffer[MAX_REORDER_DELAY+1]; + + for (i = 0; ipkt.stream_index != stream_index) + continue; + + if (pkt_buffer->pkt.pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) { + pts_buffer[0] = pkt_buffer->pkt.pts; + for (i = 0; i pts_buffer[i + 1]; i++) + FFSWAP(int64_t, pts_buffer[i], pts_buffer[i + 1]); + + pkt_buffer->pkt.dts = select_from_pts_buffer(st, pts_buffer, pkt_buffer->pkt.dts); + } + } +} +static void update_initial_timestamps(AVFormatContext *s, int stream_index, + int64_t dts, int64_t pts, AVPacket *pkt) +{ + AVStream *st = s->streams[stream_index]; + AVPacketList *pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue; + AVPacketList *pktl_it; + + uint64_t shift; + + if (st->first_dts != AV_NOPTS_VALUE || + dts == AV_NOPTS_VALUE || + st->cur_dts == AV_NOPTS_VALUE || + st->cur_dts < INT_MIN + RELATIVE_TS_BASE || + is_relative(dts)) + return; + + st->first_dts = dts - (st->cur_dts - RELATIVE_TS_BASE); + st->cur_dts = dts; + shift = (uint64_t)st->first_dts - RELATIVE_TS_BASE; + + if (is_relative(pts)) + pts += shift; + + for (pktl_it = pktl; pktl_it; pktl_it = get_next_pkt(s, st, pktl_it)) { + if (pktl_it->pkt.stream_index != stream_index) + continue; + if (is_relative(pktl_it->pkt.pts)) + pktl_it->pkt.pts += shift; + + if (is_relative(pktl_it->pkt.dts)) + pktl_it->pkt.dts += shift; + + if (st->start_time == AV_NOPTS_VALUE && pktl_it->pkt.pts != AV_NOPTS_VALUE) { + st->start_time = pktl_it->pkt.pts; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate) + st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + } + } + + if (has_decode_delay_been_guessed(st)) { + update_dts_from_pts(s, stream_index, pktl); + } + + if (st->start_time == AV_NOPTS_VALUE) { + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || !(pkt->flags & AV_PKT_FLAG_DISCARD)) { + st->start_time = pts; + } + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->sample_rate) + st->start_time += av_rescale_q(st->skip_samples, (AVRational){1, st->codecpar->sample_rate}, st->time_base); + } +} +static void update_initial_durations(AVFormatContext *s, AVStream *st, + int stream_index, int duration) +{ + AVPacketList *pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue; + int64_t cur_dts = RELATIVE_TS_BASE; + + if (st->first_dts != AV_NOPTS_VALUE) { + if (st->update_initial_durations_done) + return; + st->update_initial_durations_done = 1; + cur_dts = st->first_dts; + for (; pktl; pktl = get_next_pkt(s, st, pktl)) { + if (pktl->pkt.stream_index == stream_index) { + if (pktl->pkt.pts != pktl->pkt.dts || + pktl->pkt.dts != AV_NOPTS_VALUE || + pktl->pkt.duration) + break; + cur_dts -= duration; + } + } + if (pktl && pktl->pkt.dts != st->first_dts) { + av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s (pts %s, duration %"PRId64") in the queue\n", + av_ts2str(st->first_dts), av_ts2str(pktl->pkt.dts), av_ts2str(pktl->pkt.pts), pktl->pkt.duration); + return; + } + if (!pktl) { + av_log(s, AV_LOG_DEBUG, "first_dts %s but no packet with dts in the queue\n", av_ts2str(st->first_dts)); + return; + } + pktl = s->internal->packet_buffer ? s->internal->packet_buffer : s->internal->parse_queue; + st->first_dts = cur_dts; + } else if (st->cur_dts != RELATIVE_TS_BASE) + return; + + for (; pktl; pktl = get_next_pkt(s, st, pktl)) { + if (pktl->pkt.stream_index != stream_index) + continue; + if ((pktl->pkt.pts == pktl->pkt.dts || + pktl->pkt.pts == AV_NOPTS_VALUE) && + (pktl->pkt.dts == AV_NOPTS_VALUE || + pktl->pkt.dts == st->first_dts || + pktl->pkt.dts == RELATIVE_TS_BASE) && + !pktl->pkt.duration) { + pktl->pkt.dts = cur_dts; + if (!st->internal->avctx->has_b_frames) + pktl->pkt.pts = cur_dts; +// if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) + pktl->pkt.duration = duration; + } else + break; + cur_dts = pktl->pkt.dts + pktl->pkt.duration; + } + if (!pktl) + st->cur_dts = cur_dts; +} +static int is_intra_only(enum AVCodecID id) +{ + const AVCodecDescriptor *d = avcodec_descriptor_get(id); + if (!d) + return 0; + if (d->type == AVMEDIA_TYPE_VIDEO && !(d->props & AV_CODEC_PROP_INTRA_ONLY)) + return 0; + return 1; +} +// copy from ffmpeg n4.2.1 +void av_compute_pkt_fields(AVFormatContext *s, AVStream *st, + AVCodecParserContext *pc, AVPacket *pkt, + int64_t next_dts, int64_t next_pts) +{ + int num, den, presentation_delayed, delay, i; + int64_t offset; + AVRational duration; + int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 && + st->codecpar->codec_id != AV_CODEC_ID_HEVC; + + if (s->flags & AVFMT_FLAG_NOFILLIN) + return; + + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && pkt->dts != AV_NOPTS_VALUE) { + if (pkt->dts == pkt->pts && st->last_dts_for_order_check != AV_NOPTS_VALUE) { + if (st->last_dts_for_order_check <= pkt->dts) { + st->dts_ordered++; + } else { + av_log(s, st->dts_misordered ? AV_LOG_DEBUG : AV_LOG_WARNING, + "DTS %"PRIi64" < %"PRIi64" out of order\n", + pkt->dts, + st->last_dts_for_order_check); + st->dts_misordered++; + } + if (st->dts_ordered + st->dts_misordered > 250) { + st->dts_ordered >>= 1; + st->dts_misordered >>= 1; + } + } + + st->last_dts_for_order_check = pkt->dts; + if (st->dts_ordered < 8*st->dts_misordered && pkt->dts == pkt->pts) + pkt->dts = AV_NOPTS_VALUE; + } + + if ((s->flags & AVFMT_FLAG_IGNDTS) && pkt->pts != AV_NOPTS_VALUE) + pkt->dts = AV_NOPTS_VALUE; + + if (pc && pc->pict_type == AV_PICTURE_TYPE_B + && !st->internal->avctx->has_b_frames) + //FIXME Set low_delay = 0 when has_b_frames = 1 + st->internal->avctx->has_b_frames = 1; + + /* do we have a video B-frame ? */ + delay = st->internal->avctx->has_b_frames; + presentation_delayed = 0; + + /* XXX: need has_b_frame, but cannot get it if the codec is + * not initialized */ + if (delay && + pc && pc->pict_type != AV_PICTURE_TYPE_B) + presentation_delayed = 1; + + if (pkt->pts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE && + st->pts_wrap_bits < 63 && + pkt->dts - (1LL << (st->pts_wrap_bits - 1)) > pkt->pts) { + if (is_relative(st->cur_dts) || pkt->dts - (1LL<<(st->pts_wrap_bits - 1)) > st->cur_dts) { + pkt->dts -= 1LL << st->pts_wrap_bits; + } else + pkt->pts += 1LL << st->pts_wrap_bits; + } + + /* Some MPEG-2 in MPEG-PS lack dts (issue #171 / input_file.mpg). + * We take the conservative approach and discard both. + * Note: If this is misbehaving for an H.264 file, then possibly + * presentation_delayed is not set correctly. */ + if (delay == 1 && pkt->dts == pkt->pts && + pkt->dts != AV_NOPTS_VALUE && presentation_delayed) { + av_log(s, AV_LOG_DEBUG, "invalid dts/pts combination %"PRIi64"\n", pkt->dts); + if ( strcmp(s->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") + && strcmp(s->iformat->name, "flv")) // otherwise we discard correct timestamps for vc1-wmapro.ism + pkt->dts = AV_NOPTS_VALUE; + } + + duration = av_mul_q((AVRational) {pkt->duration, 1}, st->time_base); + if (pkt->duration == 0) { + ff_compute_frame_duration(s, &num, &den, st, pc, pkt); + if (den && num) { + duration = (AVRational) {num, den}; + pkt->duration = av_rescale_rnd(1, + num * (int64_t) st->time_base.den, + den * (int64_t) st->time_base.num, + AV_ROUND_DOWN); + } + } + + if (pkt->duration != 0 && (s->internal->packet_buffer || s->internal->parse_queue)) + update_initial_durations(s, st, pkt->stream_index, pkt->duration); + + /* Correct timestamps with byte offset if demuxers only have timestamps + * on packet boundaries */ + if (pc && st->need_parsing == AVSTREAM_PARSE_TIMESTAMPS && pkt->size) { + /* this will estimate bitrate based on this frame's duration and size */ + offset = av_rescale(pc->offset, pkt->duration, pkt->size); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += offset; + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += offset; + } + + /* This may be redundant, but it should not hurt. */ + if (pkt->dts != AV_NOPTS_VALUE && + pkt->pts != AV_NOPTS_VALUE && + pkt->pts > pkt->dts) + presentation_delayed = 1; + + if (s->debug & FF_FDEBUG_TS) + av_log(s, AV_LOG_DEBUG, + "IN delayed:%d pts:%s, dts:%s cur_dts:%s st:%d pc:%p duration:%"PRId64" delay:%d onein_oneout:%d\n", + presentation_delayed, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), + pkt->stream_index, pc, pkt->duration, delay, onein_oneout); + + /* Interpolate PTS and DTS if they are not present. We skip H264 + * currently because delay and has_b_frames are not reliably set. */ + if ((delay == 0 || (delay == 1 && pc)) && + onein_oneout) { + if (presentation_delayed) { + /* DTS = decompression timestamp */ + /* PTS = presentation timestamp */ + if (pkt->dts == AV_NOPTS_VALUE) + pkt->dts = st->last_IP_pts; + update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); + if (pkt->dts == AV_NOPTS_VALUE) + pkt->dts = st->cur_dts; + + /* This is tricky: the dts must be incremented by the duration + * of the frame we are displaying, i.e. the last I- or P-frame. */ + if (st->last_IP_duration == 0 && (uint64_t)pkt->duration <= INT32_MAX) + st->last_IP_duration = pkt->duration; + if (pkt->dts != AV_NOPTS_VALUE) + st->cur_dts = pkt->dts + st->last_IP_duration; + if (pkt->dts != AV_NOPTS_VALUE && + pkt->pts == AV_NOPTS_VALUE && + st->last_IP_duration > 0 && + ((uint64_t)st->cur_dts - (uint64_t)next_dts + 1) <= 2 && + next_dts != next_pts && + next_pts != AV_NOPTS_VALUE) + pkt->pts = next_dts; + + if ((uint64_t)pkt->duration <= INT32_MAX) + st->last_IP_duration = pkt->duration; + st->last_IP_pts = pkt->pts; + /* Cannot compute PTS if not present (we can compute it only + * by knowing the future. */ + } else if (pkt->pts != AV_NOPTS_VALUE || + pkt->dts != AV_NOPTS_VALUE || + pkt->duration ) { + + /* presentation is not delayed : PTS and DTS are the same */ + if (pkt->pts == AV_NOPTS_VALUE) + pkt->pts = pkt->dts; + update_initial_timestamps(s, pkt->stream_index, pkt->pts, + pkt->pts, pkt); + if (pkt->pts == AV_NOPTS_VALUE) + pkt->pts = st->cur_dts; + pkt->dts = pkt->pts; + if (pkt->pts != AV_NOPTS_VALUE) + st->cur_dts = av_add_stable(st->time_base, pkt->pts, duration, 1); + } + } + + if (pkt->pts != AV_NOPTS_VALUE && delay <= MAX_REORDER_DELAY) { + st->pts_buffer[0] = pkt->pts; + for (i = 0; ipts_buffer[i] > st->pts_buffer[i + 1]; i++) + FFSWAP(int64_t, st->pts_buffer[i], st->pts_buffer[i + 1]); + + if(has_decode_delay_been_guessed(st)) + pkt->dts = select_from_pts_buffer(st, st->pts_buffer, pkt->dts); + } + // We skipped it above so we try here. + if (!onein_oneout) + // This should happen on the first packet + update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); + if (pkt->dts > st->cur_dts) + st->cur_dts = pkt->dts; + + if (s->debug & FF_FDEBUG_TS) + av_log(s, AV_LOG_DEBUG, "OUTdelayed:%d/%d pts:%s, dts:%s cur_dts:%s st:%d (%d)\n", + presentation_delayed, delay, av_ts2str(pkt->pts), av_ts2str(pkt->dts), av_ts2str(st->cur_dts), st->index, st->id); + + /* update flags */ + if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA || is_intra_only(st->codecpar->codec_id)) + pkt->flags |= AV_PKT_FLAG_KEY; +#if FF_API_CONVERGENCE_DURATION + FF_DISABLE_DEPRECATION_WARNINGS + if (pc) + pkt->convergence_duration = pc->convergence_duration; + FF_ENABLE_DEPRECATION_WARNINGS +#endif +} diff --git a/framework/utils/ffmpeg_utils.h b/framework/utils/ffmpeg_utils.h index 292ca8aca..3f3067cdd 100644 --- a/framework/utils/ffmpeg_utils.h +++ b/framework/utils/ffmpeg_utils.h @@ -13,6 +13,7 @@ extern "C"{ #include #include +#include #include #ifdef __cplusplus @@ -68,9 +69,22 @@ void copyPCMData2(const AVFrame *frame, fillBufferCallback fillCallback, void *a bool updateH26xHeader2xxc(AVCodecParameters *par); - const char *getErrorString(int err); +int parse_h264_extraData(enum AVCodecID codecId, const uint8_t* extraData,int extraData_size, + uint8_t** sps_data ,int*sps_data_size, + uint8_t** pps_data, int* pps_data_size, + int* nal_length_size); + +int parse_h265_extraData(enum AVCodecID codecId, const uint8_t* extradata,int extradata_size, + uint8_t** vps_data ,int*vps_data_size, + uint8_t** sps_data ,int*sps_data_size, + uint8_t** pps_data, int* pps_data_size, + int* nal_length_size); + +void av_compute_pkt_fields(AVFormatContext *s, AVStream *st, + AVCodecParserContext *pc, AVPacket *pkt, + int64_t next_dts, int64_t next_pts); #ifdef __cplusplus }; diff --git a/framework/utils/frame_work_log.c b/framework/utils/frame_work_log.c index c00205799..71581e487 100644 --- a/framework/utils/frame_work_log.c +++ b/framework/utils/frame_work_log.c @@ -17,9 +17,9 @@ #ifdef __APPLE__ +#include "appleLog.h" #include #include - #endif #ifdef ANDROID @@ -308,6 +308,9 @@ int __log_print(int prio, const char *tag, const char *fmt, ...) if (!logCtrl.disable_console) { #ifdef ANDROID __android_log_print(lev, ANDROID_APP_TAG, "%s", finalLogMsg); +#elif defined(__APPLE__) && (TARGET_OS_IPHONE) + const char *ctr = NULL; + appleNSlogC(get_char_lev(prio, &ctr), finalLogMsg); #else linux_print(prio, out_buf); #endif diff --git a/framework/utils/frame_work_log.h b/framework/utils/frame_work_log.h index 24fd63d46..6864b2b51 100644 --- a/framework/utils/frame_work_log.h +++ b/framework/utils/frame_work_log.h @@ -7,8 +7,9 @@ #include -#include #include "CicadaType.h" +#include +#include #define AF_LOG_LEVEL_NONE 0 #define AF_LOG_LEVEL_FATAL 8 @@ -71,8 +72,20 @@ char *getTime(); #define AF_LOGE(...) __log_print(AF_LOG_LEVEL_ERROR,LOG_TAG,__VA_ARGS__) #define AF_LOGF(...) __log_print(LOG_LEVEL_FATAL,LOG_TAG,__VA_ARGS__) +#define API_LOG_IN_TAG "API_IN:" +#define API_LOG_OUT_TAG "API_OUT:" + #define AF_TRACE do { AF_LOGD("%s:%d(%s)\n",__FILE__,__LINE__,__func__);} while(0) +#define AF_API_TRACE_IN \ + do { \ + AF_LOGD(API_LOG_IN_TAG "%s\n", __func__); \ + } while (0) +#define AF_API_TRACE_OUT \ + do { \ + AF_LOGD(API_LOG_OUT_TAG "%s)\n", __func__); \ + } while (0) + #define AF_DUMP_INT(LINE) AF_LOGD("%s is %lld\n",#LINE,LINE) #ifdef __cplusplus diff --git a/framework/utils/mediaFrame.c b/framework/utils/mediaFrame.c index 32e7e2ba9..4b4066f6f 100644 --- a/framework/utils/mediaFrame.c +++ b/framework/utils/mediaFrame.c @@ -37,6 +37,16 @@ void releaseMeta(Stream_meta *pMeta) pMeta->description = NULL; } + if (pMeta->keyUrl) { + free(pMeta->keyUrl); + pMeta->keyUrl = NULL; + } + + if (pMeta->keyFormat) { + free(pMeta->keyFormat); + pMeta->keyFormat = NULL; + } + Source_meta *meta = pMeta->meta; releaseSourceMeta(meta); diff --git a/framework/utils/pixelBufferConvertor.cpp b/framework/utils/pixelBufferConvertor.cpp new file mode 100644 index 000000000..5cc39f6e6 --- /dev/null +++ b/framework/utils/pixelBufferConvertor.cpp @@ -0,0 +1,505 @@ +// +// Created by pingkai on 2021/1/26. +// + +#define LOG_TAG "pixelBufferConvertor" +#include "pixelBufferConvertor.h" +#include +#include + +using namespace Cicada; +static int copy_avframe_to_pixel_buffer(const AVFrame *frame, CVPixelBufferRef cv_img, const size_t *plane_strides, + const size_t *plane_rows) +{ + int i, j; + size_t plane_count; + int status; + int rows; + int src_stride; + int dst_stride; + uint8_t *src_addr; + uint8_t *dst_addr; + size_t copy_bytes; + + status = CVPixelBufferLockBaseAddress(cv_img, 0); + if (status) { + AF_LOGE("Error: Could not lock base address of CVPixelBuffer: %d.\n", status); + } + + if (CVPixelBufferIsPlanar(cv_img)) { + plane_count = CVPixelBufferGetPlaneCount(cv_img); + for (i = 0; frame->data[i]; i++) { + if (i == plane_count) { + CVPixelBufferUnlockBaseAddress(cv_img, 0); + AF_LOGE("Error: different number of planes in AVFrame and CVPixelBuffer.\n"); + + return AVERROR_EXTERNAL; + } + + dst_addr = (uint8_t *) CVPixelBufferGetBaseAddressOfPlane(cv_img, i); + src_addr = (uint8_t *) frame->data[i]; + dst_stride = CVPixelBufferGetBytesPerRowOfPlane(cv_img, i); + src_stride = plane_strides[i]; + rows = plane_rows[i]; + + if (dst_stride == src_stride) { + memcpy(dst_addr, src_addr, src_stride * rows); + } else { + copy_bytes = dst_stride < src_stride ? dst_stride : src_stride; + + for (j = 0; j < rows; j++) { + memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes); + } + } + } + } else { + if (frame->data[1]) { + CVPixelBufferUnlockBaseAddress(cv_img, 0); + AF_LOGE("Error: different number of planes in AVFrame and non-planar CVPixelBuffer.\n"); + + return AVERROR_EXTERNAL; + } + + dst_addr = (uint8_t *) CVPixelBufferGetBaseAddress(cv_img); + src_addr = (uint8_t *) frame->data[0]; + dst_stride = CVPixelBufferGetBytesPerRow(cv_img); + src_stride = plane_strides[0]; + rows = plane_rows[0]; + + if (dst_stride == src_stride) { + memcpy(dst_addr, src_addr, src_stride * rows); + } else { + copy_bytes = dst_stride < src_stride ? dst_stride : src_stride; + + for (j = 0; j < rows; j++) { + memcpy(dst_addr + j * dst_stride, src_addr + j * src_stride, copy_bytes); + } + } + } + + status = CVPixelBufferUnlockBaseAddress(cv_img, 0); + if (status) { + AF_LOGE("Error: Could not unlock CVPixelBuffer base address: %d.\n", status); + return AVERROR_EXTERNAL; + } + + return 0; +} + +static int get_cv_pixel_format(enum AVPixelFormat fmt, enum AVColorRange range, int *av_pixel_format, int *range_guessed) +{ + if (range_guessed) *range_guessed = range != AVCOL_RANGE_MPEG && range != AVCOL_RANGE_JPEG; + + //MPEG range is used when no range is set + if (fmt == AV_PIX_FMT_NV12) { + *av_pixel_format = range == AVCOL_RANGE_JPEG ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange + : kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; + } else if (fmt == AV_PIX_FMT_YUV420P) { + *av_pixel_format = range == AVCOL_RANGE_JPEG ? kCVPixelFormatType_420YpCbCr8PlanarFullRange : kCVPixelFormatType_420YpCbCr8Planar; + } else if (fmt == AV_PIX_FMT_P010LE) { + *av_pixel_format = range == AVCOL_RANGE_JPEG ? kCVPixelFormatType_420YpCbCr10BiPlanarFullRange + : kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange; + *av_pixel_format = kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange; + } else { + return -(EINVAL); + } + + return 0; +} + +static int get_cv_pixel_info(const AVFrame *frame, int *color, int *plane_count, size_t *widths, size_t *heights, size_t *strides, + size_t *contiguous_buf_size) +{ + int av_format = frame->format; + int av_color_range = frame->color_range; + int i; + int range_guessed; + int status; + + status = get_cv_pixel_format(static_cast(av_format), static_cast(av_color_range), color, &range_guessed); + if (status) { + AF_LOGE("Could not get pixel format for color format"); + return -(EINVAL); + } + + switch (av_format) { + case AV_PIX_FMT_NV12: + *plane_count = 2; + + widths[0] = frame->width; + heights[0] = frame->height; + strides[0] = frame ? frame->linesize[0] : frame->width; + + widths[1] = (frame->width + 1) / 2; + heights[1] = (frame->height + 1) / 2; + strides[1] = frame ? frame->linesize[1] : (frame->width + 1) & -2; + break; + + case AV_PIX_FMT_YUV420P: + *plane_count = 3; + + widths[0] = frame->width; + heights[0] = frame->height; + strides[0] = frame ? frame->linesize[0] : frame->width; + + widths[1] = (frame->width + 1) / 2; + heights[1] = (frame->height + 1) / 2; + strides[1] = frame ? frame->linesize[1] : (frame->width + 1) / 2; + + widths[2] = (frame->width + 1) / 2; + heights[2] = (frame->height + 1) / 2; + strides[2] = frame ? frame->linesize[2] : (frame->width + 1) / 2; + break; + + case AV_PIX_FMT_P010LE: + *plane_count = 2; + widths[0] = frame->width; + heights[0] = frame->height; + strides[0] = frame ? frame->linesize[0] : (frame->width * 2 + 63) & -64; + + widths[1] = (frame->width + 1) / 2; + heights[1] = (frame->height + 1) / 2; + strides[1] = frame ? frame->linesize[1] : ((frame->width + 1) / 2 + 63) & -64; + break; + + default: + AF_LOGE("Could not get frame format info for color %d range %d.\n", av_format, av_color_range); + return -(EINVAL); + } + + *contiguous_buf_size = 0; + for (i = 0; i < *plane_count; i++) { + if (i < *plane_count - 1 && frame->data[i] + strides[i] * heights[i] != frame->data[i + 1]) { + *contiguous_buf_size = 0; + break; + } + + *contiguous_buf_size += strides[i] * heights[i]; + } + + return 0; +} + +void cfdict_set_int32(CFMutableDictionaryRef dict, CFStringRef key, int value) +{ + CFNumberRef number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value); + CFDictionarySetValue(dict, key, number); + CFRelease(number); +} +CFMutableDictionaryRef cfdict_create(CFIndex capacity) +{ + return CFDictionaryCreateMutable(kCFAllocatorDefault, capacity, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); +} + +CVPixelBufferPoolRef cvpxpool_create(const IAFFrame::videoInfo &src, unsigned count) +{ + int cvpx_format; + switch (src.format) { + case AF_PIX_FMT_UYVY422: + cvpx_format = kCVPixelFormatType_422YpCbCr8; + break; + case AF_PIX_FMT_NV12: + if (src.colorRange == AVCOL_RANGE_JPEG) { + cvpx_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; + } else + cvpx_format = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange; + break; + case AF_PIX_FMT_YUV420P: + cvpx_format = kCVPixelFormatType_420YpCbCr8Planar; + break; + // case AV_PIX_FMT_BGR0: + // cvpx_format = kCVPixelFormatType_32BGRA; + // break; + // case AV_PIX_FMT_P010: + // cvpx_format = 'x420'; /* kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange */ + // break; + default: + return nullptr; + } + + /* destination pixel buffer attributes */ + CFMutableDictionaryRef cvpx_attrs_dict = cfdict_create(5); + if (cvpx_attrs_dict == nullptr) return nullptr; + CFMutableDictionaryRef pool_dict = cfdict_create(2); + if (pool_dict == nullptr) { + CFRelease(cvpx_attrs_dict); + return nullptr; + } + + CFMutableDictionaryRef io_dict = cfdict_create(0); + if (io_dict == nullptr) { + CFRelease(cvpx_attrs_dict); + CFRelease(pool_dict); + return nullptr; + } + CFDictionarySetValue(cvpx_attrs_dict, kCVPixelBufferIOSurfacePropertiesKey, io_dict); + CFRelease(io_dict); + + cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferPixelFormatTypeKey, cvpx_format); + cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferWidthKey, src.width); + cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferHeightKey, src.height); + /* Required by CIFilter to render IOSurface */ + cfdict_set_int32(cvpx_attrs_dict, kCVPixelBufferBytesPerRowAlignmentKey, 16); + + cfdict_set_int32(pool_dict, kCVPixelBufferPoolMinimumBufferCountKey, count); + cfdict_set_int32(pool_dict, kCVPixelBufferPoolMaximumBufferAgeKey, 0); + + CVPixelBufferPoolRef pool; + CVReturn err = CVPixelBufferPoolCreate(nullptr, pool_dict, cvpx_attrs_dict, &pool); + CFRelease(pool_dict); + CFRelease(cvpx_attrs_dict); + if (err != kCVReturnSuccess) { + return nullptr; + } + + CVPixelBufferRef cvpxs[count]; + for (unsigned i = 0; i < count; ++i) { + err = CVPixelBufferPoolCreatePixelBuffer(nullptr, pool, &cvpxs[i]); + if (err != kCVReturnSuccess) { + CVPixelBufferPoolRelease(pool); + pool = nullptr; + count = i; + break; + } + } + for (unsigned i = 0; i < count; ++i) { + CFRelease(cvpxs[i]); + } + + return pool; +} + +CVPixelBufferRef pixelBufferConvertor::avFrame2pixelBuffer(AVFrame *frame) +{ + int plane_count; + int color; + size_t widths[AV_NUM_DATA_POINTERS]; + size_t heights[AV_NUM_DATA_POINTERS]; + size_t strides[AV_NUM_DATA_POINTERS]; + int status; + size_t contiguous_buf_size; + memset(widths, 0, sizeof(widths)); + memset(heights, 0, sizeof(heights)); + memset(strides, 0, sizeof(strides)); + status = get_cv_pixel_info(frame, &color, &plane_count, widths, heights, strides, &contiguous_buf_size); + if (status) { + AF_LOGE("Error: Cannot convert format %d color_range %d: %d\n", frame->format, frame->color_range, status); + return nullptr; + } + + CVPixelBufferRef pixelBuffer; + status = CVPixelBufferPoolCreatePixelBuffer(nullptr, mPixBufPool, &pixelBuffer); + + if (status) { + AF_LOGE("Could not create pixel buffer from pool: %d.\n", status); + return nullptr; + } + + status = copy_avframe_to_pixel_buffer(frame, pixelBuffer, strides, heights); + if (status) { + CFRelease(pixelBuffer); + pixelBuffer = nullptr; + } + return pixelBuffer; +} +pixelBufferConvertor::pixelBufferConvertor() +{ + memset(&mVideoInfo, 0, sizeof(IAFFrame::videoInfo)); + mVideoInfo.format = -1; +} +pixelBufferConvertor::~pixelBufferConvertor() +{ + if (sws_ctx) { + sws_freeContext(sws_ctx); + } + av_frame_free(&mOutFrame); + if (mPixBufPool) { + CVPixelBufferPoolRelease(mPixBufPool); + } +} +static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) +{ + AVFrame *picture; + int ret; + + picture = av_frame_alloc(); + if (!picture) return nullptr; + + picture->format = pix_fmt; + picture->width = width; + picture->height = height; + + /* allocate the buffers for the frame data */ + ret = av_frame_get_buffer(picture, 32); + if (ret < 0) { + fprintf(stderr, "Could not allocate frame data.\n"); + exit(1); + } + + return picture; +} + +int pixelBufferConvertor::init(const IAFFrame::videoInfo &src) +{ + // TODO: get the dst format + enum AVPixelFormat dstFormat; + IAFFrame::videoInfo dst = src; + switch (src.format) { + case AF_PIX_FMT_UYVY422: + case AF_PIX_FMT_NV12: + case AF_PIX_FMT_YUV420P: + break; + default: + dst.format = AV_PIX_FMT_NV12; + } + + + if (mPixBufPool) { + CVPixelBufferPoolRelease(mPixBufPool); + } + + mPixBufPool = cvpxpool_create(dst, 3); + + if (mPixBufPool == nullptr) { + return -EINVAL; + } + + + if (sws_ctx) { + sws_freeContext(sws_ctx); + sws_ctx = nullptr; + } + av_frame_free(&mOutFrame); + + if (src != dst) { + + sws_ctx = sws_getContext(src.width, src.height, static_cast(src.format), src.width, src.height, dstFormat, + SWS_BILINEAR, nullptr, nullptr, nullptr); + mOutFrame = alloc_picture(dstFormat, src.width, src.height); + } + return 0; +} + +IAFFrame *pixelBufferConvertor::convert(IAFFrame *frame) +{ + auto *avAFFrame = dynamic_cast(frame); + if (avAFFrame == nullptr) { + return nullptr; + } + + if (mVideoInfo != frame->getInfo().video) { + int ret = init(frame->getInfo().video); + if (ret < 0) { + AF_LOGE("convert init error %d\n", ret); + return nullptr; + } + mVideoInfo = frame->getInfo().video; + } + auto *avFrame = (AVFrame *) (*avAFFrame); + + if (sws_ctx) { + sws_scale(sws_ctx, avFrame->data, avFrame->linesize, 0, avFrame->height, mOutFrame->data, mOutFrame->linesize); + avFrame = mOutFrame; + } + + CVPixelBufferRef pixelBuffer = avFrame2pixelBuffer(avFrame); + if (pixelBuffer == nullptr) { + return nullptr; + } + VideoColorInfo colorInfo; + colorInfo.chroma_location = static_cast(avFrame->chroma_location); + colorInfo.color_primaries = static_cast(avFrame->color_primaries); + colorInfo.color_range = static_cast(avFrame->color_range); + colorInfo.color_space = static_cast(avFrame->colorspace); + colorInfo.color_trc = static_cast(avFrame->color_trc); + + UpdateColorInfo(colorInfo, pixelBuffer); + auto *pBFrame = new PBAFFrame(pixelBuffer, frame->getInfo().pts, frame->getInfo().duration); + CFRelease(pixelBuffer); + return pBFrame; +} +void pixelBufferConvertor::UpdateColorInfo(const VideoColorInfo &info, CVPixelBufferRef pixelBuffer) +{ + CFStringRef value; + switch (info.color_primaries) { + case AFCOL_PRI_BT709: + value = kCVImageBufferColorPrimaries_ITU_R_709_2; + break; + case AFCOL_PRI_SMPTE170M: + value = kCVImageBufferColorPrimaries_SMPTE_C; + break; + case AFCOL_PRI_BT2020: + value = kCVImageBufferColorPrimaries_ITU_R_2020; + break; + default: + value = nullptr; + break; + } + if (value) { + CVBufferSetAttachment(pixelBuffer, kCVImageBufferColorPrimariesKey, value, kCVAttachmentMode_ShouldPropagate); + } + switch (info.color_trc) { + case AFCOL_TRC_BT709: + case AFCOL_TRC_SMPTE170M: + value = kCVImageBufferTransferFunction_ITU_R_709_2; + break; + case AFCOL_TRC_BT2020_10: + value = kCVImageBufferTransferFunction_ITU_R_2020; + break; + case AFCOL_TRC_SMPTE2084: +#if TARGET_OS_IPHONE + if (__builtin_available(iOS 11.0, *)) +#else + if (__builtin_available(macOS 10.13, *)) +#endif + { + value = kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ; + } + break; + case AFCOL_TRC_SMPTE428: +#if TARGET_OS_IPHONE + if (__builtin_available(iOS 10.0, *)) +#else + if (__builtin_available(macOS 10.12, *)) +#endif + { + value = kCVImageBufferTransferFunction_SMPTE_ST_428_1; + } + break; + default: + value = nullptr; + break; + } + if (value) { + CVBufferSetAttachment(pixelBuffer, kCVImageBufferTransferFunctionKey, value, kCVAttachmentMode_ShouldPropagate); + } + + switch (info.color_space) { + case AFCOL_SPC_BT709: + value = kCGColorSpaceITUR_709; + CVBufferSetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, kCVImageBufferYCbCrMatrix_ITU_R_709_2, + kCVAttachmentMode_ShouldPropagate); + break; + case AFCOL_SPC_BT2020_NCL: + value = kCGColorSpaceITUR_2020; + CVBufferSetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, kCVImageBufferYCbCrMatrix_ITU_R_2020, + kCVAttachmentMode_ShouldPropagate); + break; + case AFCOL_SPC_SMPTE170M: + value = kCGColorSpaceSRGB; + CVBufferSetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, kCVImageBufferYCbCrMatrix_ITU_R_601_4, + kCVAttachmentMode_ShouldPropagate); + break; + default: + value = nullptr; + break; + } + _Nullable CGColorSpaceRef m_ColorSpace{nullptr}; + if (value) { + m_ColorSpace = CGColorSpaceCreateWithName(value); + } + if (m_ColorSpace != nullptr) { + CVBufferSetAttachment(pixelBuffer, kCVImageBufferCGColorSpaceKey, m_ColorSpace, kCVAttachmentMode_ShouldPropagate); + } + CGColorSpaceRelease(m_ColorSpace); +} diff --git a/framework/utils/pixelBufferConvertor.h b/framework/utils/pixelBufferConvertor.h new file mode 100644 index 000000000..8d972f5a5 --- /dev/null +++ b/framework/utils/pixelBufferConvertor.h @@ -0,0 +1,38 @@ +// +// Created by pingkai on 2021/1/26. +// + +#ifndef CICADAMEDIA_PIXELBUFFERCONVERTOR_H +#define CICADAMEDIA_PIXELBUFFERCONVERTOR_H + + +#include +#include +extern "C" { +#include +#include +} +namespace Cicada { + class pixelBufferConvertor { + public: + pixelBufferConvertor(); + ~pixelBufferConvertor(); + + IAFFrame *convert(IAFFrame *frame); + + static void UpdateColorInfo(const VideoColorInfo &info, CVPixelBufferRef pixelBuffer); + + private: + int init(const IAFFrame::videoInfo &src); + CVPixelBufferRef avFrame2pixelBuffer(AVFrame *frame); + + private: + struct SwsContext *sws_ctx{nullptr}; + AVFrame *mOutFrame{nullptr}; + IAFFrame::videoInfo mVideoInfo{}; + CVPixelBufferPoolRef mPixBufPool{nullptr}; + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_PIXELBUFFERCONVERTOR_H diff --git a/framework/utils/uuid.h b/framework/utils/uuid.h new file mode 100644 index 000000000..7f61de9e7 --- /dev/null +++ b/framework/utils/uuid.h @@ -0,0 +1,467 @@ +/* + Single-file, STB-style, library to generate UUID:s. No dependencies + except for OS-provided functionality. + + version 0.1, August, 2016 + + Copyright (C) 2016- Fredrik Kihlander + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Fredrik Kihlander +*/ + +#ifndef UUID_H_INCLUDED +#define UUID_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +struct uuid { unsigned char bytes[16]; }; + +/** + * Set uuid to the null_uuid. + */ +static void uuid0_generate( uuid* res ); + +/** + * Generate an uuid of version 4 ( Random ) into res. + * @note res will be the null_uuid on failure. + */ +static void uuid4_generate( uuid* res ); + +/** + * Return the type of the provided uuid. + * + * @return 0 if it is the null-uuid + * 1 MAC address & date-time + * 2 DCE Security + * 3 MD5 hash & namespace + * 4 Random + * 5 SHA-1 hash & namespace + * + * -1 on an invalid uuid. + */ +static int uuid_type( uuid* id ); + +/** + * Converts an uuid to string. + * @param id uuid to convert. + * @param out pointer to char-buffer where to write uuid, uuid is NOT 0-terminated + * and is expected to be at least 36 bytes. + * @return out + */ +static char* uuid_to_string( uuid* id, char* out ); + +/** + * Convert a string to an uuid. + * @param str to convert. + * @param out uuid to parse to. + * @return true on success. + */ +static bool uuid_from_string( const char* str, uuid* out ); + +/** + * Copy uuid from src to dst. + */ +static void uuid_copy( const uuid* src, uuid* dst ); + +#ifdef __cplusplus +} + +struct _uuid_to_str +{ + char str[37]; + _uuid_to_str( uuid* id ) + { + uuid_to_string( id, str ); + str[36] = '\0'; + } +}; + +/** + * Helper macro to convert uuid to string. + */ +#define UUID_TO_STRING( id ) _uuid_to_str( id ).str + +#endif // __cplusplus + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#if defined(__LINUX__) || defined(__linux__) || defined(__ANDROID__) +# include +#endif + +#if defined(__ANDROID__) +#include +#endif + +#if defined( _MSC_VER ) +# include +# pragma comment(lib, "Ole32.lib") +#endif + +#if defined( __APPLE__ ) +#include +#endif + + +#if defined(__ANDROID__) + +#include +#include +#define MAX_TASK_NAME_LEN (16) + +extern JavaVM *g_vm; + +static jboolean attach_jvm(JNIEnv **jni_env) +{ + if ((g_vm)->GetEnv((void **)jni_env, + JNI_VERSION_1_4) < 0) { + JavaVMAttachArgs lJavaVMAttachArgs; + lJavaVMAttachArgs.version = JNI_VERSION_1_4; + int size = MAX_TASK_NAME_LEN + 1; + char *name = static_cast(malloc(size)); + memset(name, 0, size); + + if (prctl(PR_GET_NAME, (unsigned long) name, 0, 0, 0) != 0) { + strcpy(name, ""); + } else { + name[MAX_TASK_NAME_LEN] = 0; + } + + lJavaVMAttachArgs.name = name; + lJavaVMAttachArgs.group = nullptr; + jint result = (g_vm)->AttachCurrentThread(jni_env, &lJavaVMAttachArgs); + free(name); + + if (result < 0) { + jni_env = NULL; + return JNI_FALSE; + } + return JNI_TRUE; + } + + return JNI_FALSE; +} + +#define detach_jvm(attached) \ +if (attached) \ + (g_vm)->DetachCurrentThread(); + +static int uuid_get_uuid(char* msg_buf, int buf_len) +{ + jclass uuid_class = 0; + jmethodID get_uuid_method; + jmethodID to_string_method; + JNIEnv *jni_env = 0; + jobject javaUuid = 0; + jstring uuid_string = 0; + int error_code = 0; + const char *native_string; + + jboolean attached = attach_jvm(&jni_env); + if (!jni_env) { + error_code = 1; + goto on_error; + } + + uuid_class = static_cast((jni_env)->NewGlobalRef((jni_env)->FindClass("java/util/UUID"))); + + if (uuid_class == 0) { + error_code = 2; + goto on_error; + } + + get_uuid_method = (jni_env)->GetStaticMethodID( uuid_class, + "randomUUID", + "()Ljava/util/UUID;"); + + if (get_uuid_method == 0) { + error_code = 3; + goto on_error; + } + + javaUuid = (jni_env)->CallStaticObjectMethod( uuid_class, get_uuid_method); + if (javaUuid == 0) { + error_code = 4; + goto on_error; + } + + to_string_method = (jni_env)->GetMethodID( uuid_class, "toString", "()Ljava/lang/String;"); + + if (to_string_method == 0) { + error_code = 5; + goto on_error; + } + + uuid_string = static_cast((jni_env)->CallObjectMethod(javaUuid, to_string_method)); + if (uuid_string == 0) { + error_code = 6; + goto on_error; + } + + native_string = (jni_env)->GetStringUTFChars( uuid_string, JNI_FALSE); + if (native_string == 0) { + error_code = 7; + goto on_error; + } + + strcpy(msg_buf, native_string); + + (jni_env)->ReleaseStringUTFChars( uuid_string, native_string); + + if(javaUuid != 0){ + jni_env->DeleteLocalRef(javaUuid); + } + + if(uuid_class != 0){ + jni_env->DeleteGlobalRef(uuid_class); + } + + detach_jvm(attached); + + return strlen(msg_buf); + + on_error: + snprintf(msg_buf, buf_len, "-ERR%d-", error_code); + + if(uuid_string != 0){ + jni_env->DeleteLocalRef(uuid_string); + } + + if(javaUuid != 0){ + jni_env->DeleteLocalRef(javaUuid); + } + + if(uuid_class != 0){ + jni_env->DeleteGlobalRef(uuid_class); + } + + detach_jvm(attached); + return -1; +} + +#endif + + +static char* uuid_to_string( uuid* id, char* out ) +{ + static const char TOHEXCHAR[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + char* c = out; + int src_byte = 0; + for( int i = 0; i < 4; ++i ) + { + *c++ = TOHEXCHAR[ ( id->bytes[src_byte] >> 4 ) & 0xF ]; + *c++ = TOHEXCHAR[ id->bytes[src_byte] & 0xF ]; + ++src_byte; + } + *c++ = '-'; + + for( int i = 0; i < 2; ++i ) + { + *c++ = TOHEXCHAR[ ( id->bytes[src_byte] >> 4 ) & 0xF ]; + *c++ = TOHEXCHAR[ id->bytes[src_byte] & 0xF ]; + ++src_byte; + } + *c++ = '-'; + + for( int i = 0; i < 2; ++i ) + { + *c++ = TOHEXCHAR[ ( id->bytes[src_byte] >> 4 ) & 0xF ]; + *c++ = TOHEXCHAR[ id->bytes[src_byte] & 0xF ]; + ++src_byte; + } + *c++ = '-'; + + for( int i = 0; i < 2; ++i ) + { + *c++ = TOHEXCHAR[ ( id->bytes[src_byte] >> 4 ) & 0xF ]; + *c++ = TOHEXCHAR[ id->bytes[src_byte] & 0xF ]; + ++src_byte; + } + *c++ = '-'; + + for( int i = 0; i < 6; ++i ) + { + *c++ = TOHEXCHAR[ ( id->bytes[src_byte] >> 4 ) & 0xF ]; + *c++ = TOHEXCHAR[ id->bytes[src_byte] & 0xF ]; + ++src_byte; + } + + return out; +} + +static bool uuid_from_string( const char* str, uuid* out ) +{ + char uuid_str[32]; + char* outc = uuid_str; + for( int i = 0; i < 36; ++i ) + { + char c = str[i]; + if( i == 8 || i == 13 || i == 18 || i == 23 ) + { + if( c != '-' ) + return false; + } + else + { + if( !isxdigit( c ) ) + return false; + *outc = (char)tolower( c ); + ++outc; + } + } + +#define UUID_HEXCHRTO_DEC( c ) (unsigned char)( (c) <= '9' ? ( (c) - '0' ) : 10 + (c) - ( (c) <= 'F' ? 'A' : 'a' ) ) + + for( int byte = 0; byte < 16; ++byte ) + { + unsigned char v1 = UUID_HEXCHRTO_DEC( uuid_str[ byte * 2 ] ); + unsigned char v2 = UUID_HEXCHRTO_DEC( uuid_str[ byte * 2 + 1 ] ); + out->bytes[byte] = (unsigned char)(( v1 << 4 ) | v2); + } +#undef UUID_HEXCHRTO_DEC + + return true; +} + +void uuid0_generate( uuid* res ) +{ + memset( res, 0x0, sizeof(uuid) ); +} + +void uuid4_generate( uuid* res ) +{ + uuid0_generate( res ); + +#if defined(__ANDROID__) + char buf[37]; + int ret = uuid_get_uuid(buf ,37); + if(ret <0){ + }else { + uuid_from_string(buf, res); + } + +#elif defined(__LINUX__) || defined(__linux__) + FILE* f = fopen( "/proc/sys/kernel/random/uuid", "rb" ); + if( f == 0x0 ) + return; + + char uuid_str[36]; + size_t read = fread( uuid_str, 1, sizeof( uuid_str ), f ); + fclose(f); + if( read != 36 ) + return; + uuid_from_string( uuid_str, res ); +#elif defined(_MSC_VER) + GUID g; + HRESULT hres = CoCreateGuid( &g ); + if( hres != S_OK ) + return; + // ... endian swap to little endian to make uuid memcpy:able ... + g.Data1 = ( ( g.Data1 & 0x00FF ) << 24 ) | ( ( g.Data1 & 0xFF00 ) << 8) | ( ( g.Data1 >> 8 ) & 0xFF00 ) | ( ( g.Data1 >> 24 ) & 0x00FF ); + g.Data2 = (WORD)( ( ( g.Data2 & 0x00FF ) << 8 ) | ( ( g.Data2 & 0xFF00 ) >> 8 ) ); + g.Data3 = (WORD)( ( ( g.Data3 & 0x00FF ) << 8 ) | ( ( g.Data3 & 0xFF00 ) >> 8 ) ); + memcpy( res->bytes, &g, sizeof( res->bytes ) ); +#elif defined( __APPLE__ ) + CFUUIDRef new_uuid = CFUUIDCreate(0x0); + CFUUIDBytes bytes = CFUUIDGetUUIDBytes( new_uuid ); + + res->bytes[0] = bytes.byte0; + res->bytes[1] = bytes.byte1; + res->bytes[2] = bytes.byte2; + res->bytes[3] = bytes.byte3; + res->bytes[4] = bytes.byte4; + res->bytes[5] = bytes.byte5; + res->bytes[6] = bytes.byte6; + res->bytes[7] = bytes.byte7; + res->bytes[8] = bytes.byte8; + res->bytes[9] = bytes.byte9; + res->bytes[10] = bytes.byte10; + res->bytes[11] = bytes.byte11; + res->bytes[12] = bytes.byte12; + res->bytes[13] = bytes.byte13; + res->bytes[14] = bytes.byte14; + res->bytes[15] = bytes.byte15; + CFRelease ( new_uuid ); +#else +# error "unhandled platform" +#endif +} + +int uuid_type( uuid* id ) +{ + switch( ( id->bytes[6] & 0xF0 ) >> 4 ) + { + case 0: + for( int i = 0; i < 16; ++i ) + if( id->bytes[i] != 0 ) + return -1; + return 0; + case 1: + return 1; + case 2: + return 2; + case 3: + switch( ( id->bytes[8] & 0xF0 ) >> 4 ) + { + case 8: + case 9: + case 10: + case 11: + return 4; + default: + return -1; + } + break; + case 4: + switch( ( id->bytes[8] & 0xF0 ) >> 4 ) + { + case 8: + case 9: + case 10: + case 11: + return 4; + default: + return -1; + } + break; + case 5: + return 5; + } + return -1; +} + +void uuid_copy( const uuid* src, uuid* dst ) +{ + memcpy( dst, src, sizeof(uuid) ); +} + +#ifdef __cplusplus +} +#endif + +#endif // UUID_H_INCLUDED \ No newline at end of file diff --git a/mediaPlayer/CMakeLists.txt b/mediaPlayer/CMakeLists.txt index 44cf5f11a..b514cc389 100644 --- a/mediaPlayer/CMakeLists.txt +++ b/mediaPlayer/CMakeLists.txt @@ -35,13 +35,15 @@ include(${TOPDIR}/framework/${TARGET_PLATFORM}.cmake) set(CMAKE_CXX_STANDARD 11) -include(../framework/module_config.cmake) +message("COMMON_INC_DIR is ${COMMON_INC_DIR}") if (ENABLE_CACHE_MODULE) add_definitions(-DENABLE_CACHE_MODULE) endif () -message("COMMON_INC_DIR is ${COMMON_INC_DIR}") +if (ENABLE_MUXER) + add_definitions(-DENABLE_MUXER) +endif () set(SOURCE_FILES analytics/IAnalyticsCollector.h @@ -81,6 +83,8 @@ set(SOURCE_FILES native_cicada_player_def.h MediaPlayer.cpp MediaPlayerConfig.cpp + QueryListener.cpp + QueryListener.h abr/AbrManager.h abr/AbrManager.cpp abr/AbrRefererData.h @@ -107,7 +111,34 @@ set(SOURCE_FILES mediaPlayerSubTitleListener.h playerOptions.cpp playerOptions.h - SMP_DCAManager.cpp) + SMP_DCAManager.cpp + CicadaPlayerPrototype.cpp + SMPAVDeviceManager.cpp + SMPAVDeviceManager.h + SMPRecorderSet.cpp + SMPRecorderSet.h + SMPMessageControllerListener.cpp + SMPMessageControllerListener.h) + +if(APPLE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fobjc-arc") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fobjc-arc") + set(SOURCE_FILES ${SOURCE_FILES} + externalPlayer/AppleAVPlayer.mm + externalPlayer/AppleAVPlayer.h + externalPlayer/AppleAVPlayerLayerProcessor.mm + externalPlayer/AppleAVPlayerLayerProcessor.h + externalPlayer/AppleAVPlayerHandler.h + externalPlayer/AppleAVPlayerHandler.mm + externalPlayer/AppleAVPlayerUtil.h + externalPlayer/AppleAVPlayerUtil.m + ) +#elseif(ANDROID) +# set(SOURCE_FILES ${SOURCE_FILES} +# externalPlayer/JNIPlayer.cpp +# externalPlayer/JNIPlayer.h +# ) +endif() if (TARGET_PLATFORM STREQUAL "Android") set(SOURCE_FILES ${SOURCE_FILES} TrafficStats.c diff --git a/mediaPlayer/CicadaPlayerPrototype.cpp b/mediaPlayer/CicadaPlayerPrototype.cpp new file mode 100644 index 000000000..b14c575c5 --- /dev/null +++ b/mediaPlayer/CicadaPlayerPrototype.cpp @@ -0,0 +1,45 @@ +// +// Created by moqi on 2020/7/20. +// + +#include "CicadaPlayerPrototype.h" +#include "SuperMediaPlayer.h" +#ifdef __APPLE__ +#include "externalPlayer/AppleAVPlayer.h" +#endif +using namespace Cicada; +CicadaPlayerPrototype *CicadaPlayerPrototype::playerQueue[]; +int CicadaPlayerPrototype::_nextSlot; +void CicadaPlayerPrototype::addPrototype(Cicada::CicadaPlayerPrototype *se) +{ + playerQueue[_nextSlot++] = se; +} +ICicadaPlayer *CicadaPlayerPrototype::create(const options *opts = nullptr) +{ + int score_res = 0; + CicadaPlayerPrototype *playerType = nullptr; + + for (int i = 0; i < _nextSlot; ++i) { + int score = playerQueue[i]->probeScore(opts); + + if (score > score_res) { + score_res = score; + playerType = playerQueue[i]; + + if (score >= SUPPORT_MAX) { + break; + } + } + } + + if (playerType && score_res > SUPPORT_NOT) { + ICicadaPlayer *player = playerType->clone(); + return player; + } +#ifdef __APPLE__ + if (AppleAVPlayer::is_supported(opts)) { + return new AppleAVPlayer(); + } +#endif + return new SuperMediaPlayer(); +} diff --git a/mediaPlayer/CicadaPlayerPrototype.h b/mediaPlayer/CicadaPlayerPrototype.h new file mode 100644 index 000000000..d0ae079bc --- /dev/null +++ b/mediaPlayer/CicadaPlayerPrototype.h @@ -0,0 +1,33 @@ +// +// Created by moqi on 2020/7/20. +// + +#ifndef CICADAMEDIA_CICADAPLAYERPROTOTYPE_H +#define CICADAMEDIA_CICADAPLAYERPROTOTYPE_H +#include "ICicadaPlayer.h" +#include +#include +#include +namespace Cicada { + class CICADA_CPLUS_EXTERN CicadaPlayerPrototype { + static CicadaPlayerPrototype *playerQueue[10]; + static int _nextSlot; + + public: + virtual ~CicadaPlayerPrototype() = default; + + virtual ICicadaPlayer *clone() = 0; + + virtual int probeScore(const options *opts) + { + return SUPPORT_NOT; + } + + static void addPrototype(CicadaPlayerPrototype *se); + + static ICicadaPlayer *create(const options *opts); + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_CICADAPLAYERPROTOTYPE_H diff --git a/mediaPlayer/ErrorCodeMap.cpp b/mediaPlayer/ErrorCodeMap.cpp index 9c67c44a0..1ef82cd9f 100644 --- a/mediaPlayer/ErrorCodeMap.cpp +++ b/mediaPlayer/ErrorCodeMap.cpp @@ -72,6 +72,8 @@ void ErrorCodeMap::init() codeMap.insert(pair(MEDIA_PLAYER_ERROR_GENERAL_EPERM, 0x20080001)); codeMap.insert(pair(MEDIA_PLAYER_ERROR_GENERAL_ENOENT, 0x20080002)); codeMap.insert(pair(MEDIA_PLAYER_ERROR_GENERAL_EIO, 0x20080005)); + //8.render + codeMap.insert(pair(MEDIA_PLAYER_ERROR_RENDER_AUDIO_OPEN_DEVICE_FAILED, 0x20090001)); // codeMap.insert(pair(MEDIA_PLAYER_ERROR_UNKNOWN, 0x30000000 - 1)); } diff --git a/mediaPlayer/EventCodeMap.cpp b/mediaPlayer/EventCodeMap.cpp index 2b79e01ed..c365855da 100644 --- a/mediaPlayer/EventCodeMap.cpp +++ b/mediaPlayer/EventCodeMap.cpp @@ -58,4 +58,5 @@ void EventCodeMap::init() codeMap.insert(pair(MEDIA_PLAYER_EVENT_SUBTITLE_SELECT_ERROR, 114)); codeMap.insert(pair(MEDIA_PLAYER_EVENT_DECODER_RECOVER_SIZE, 115)); codeMap.insert(pair(MEDIA_PLAYER_EVENT_DIRECT_COMPONENT_MSG, 116)); + codeMap.insert(pair(MEDIA_PLAYER_EVENT_OPEN_AUDIO_DEVICE_FAILED, 117)); } diff --git a/mediaPlayer/ICicadaPlayer.h b/mediaPlayer/ICicadaPlayer.h index 000e129e3..7b4bc4ff6 100644 --- a/mediaPlayer/ICicadaPlayer.h +++ b/mediaPlayer/ICicadaPlayer.h @@ -8,6 +8,7 @@ #include #include "native_cicada_player_def.h" #include +#include namespace Cicada{ class IDemuxerFactory; @@ -22,6 +23,8 @@ namespace Cicada { virtual ~ICicadaPlayer() = default; + virtual string getName() = 0; + /* * 初始化;(同步) */ @@ -30,6 +33,10 @@ namespace Cicada { virtual void SetOnRenderCallBack(onRenderFrame cb, void *userData) = 0; + virtual void SetAudioRenderingCallBack(onRenderFrame cb, void *userData) = 0; + + virtual void SetUpdateViewCB(UpdateViewCB cb, void *userData) = 0; + /* *设置显示窗口 */ @@ -215,6 +222,8 @@ namespace Cicada { virtual int selectExtSubtitle(int index, bool bSelect) = 0; + virtual int setStreamDelay(int index, int64_t time) = 0; + virtual void setMediaFrameCb(playerMediaFrameCb func, void *arg) { if (mMediaFrameCb == func) { @@ -224,12 +233,7 @@ namespace Cicada { mMediaFrameCb = func; } - virtual void setBitStreamCb(readCB read, seekCB seek, void *arg) - { - mBSReadCb = read; - mBSSeekCb = seek; - mBSCbArg = arg; - } + virtual void setBitStreamCb(readCB read, seekCB seek, void *arg){}; virtual void setClockRefer(clockRefer cb, void *arg) { @@ -237,6 +241,8 @@ namespace Cicada { mCRArg = arg; } + virtual void setDrmRequestCallback(const std::function &drmCallback) = 0; + virtual int getCurrentStreamMeta(Stream_meta *meta, StreamType type) = 0; virtual void setErrorConverter(ErrorConverter *converter) @@ -256,13 +262,12 @@ namespace Cicada { virtual int invokeComponent(std::string content) = 0; + virtual float getCurrentDownloadSpeed() = 0; + protected: playerMediaFrameCb mMediaFrameCb = nullptr; void *mMediaFrameCbArg = nullptr; - readCB mBSReadCb = nullptr; - seekCB mBSSeekCb = nullptr; - void *mBSCbArg = nullptr; clockRefer mClockRef = nullptr; void* mCRArg = nullptr; diff --git a/mediaPlayer/MediaPlayer.cpp b/mediaPlayer/MediaPlayer.cpp index ed211055e..f875432ec 100644 --- a/mediaPlayer/MediaPlayer.cpp +++ b/mediaPlayer/MediaPlayer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "MediaPlayer.h" #include "media_player_api.h" #include "abr/AbrManager.h" @@ -18,6 +19,7 @@ #include "analytics/AnalyticsQueryListener.h" #include "media_player_error_def.h" #include "PlayerCacheDataSource.h" +#include "QueryListener.h" using namespace Cicada; @@ -25,66 +27,13 @@ namespace Cicada { #define GET_PLAYER_HANDLE playerHandle* handle = (playerHandle*)mPlayerHandle; #define GET_MEDIA_PLAYER MediaPlayer* player = (MediaPlayer*)userData; - - class QueryListener : public AnalyticsQueryListener { - public: - explicit QueryListener(MediaPlayer *player) - { - mPlayer = player; - } - - ~QueryListener() override = default;; - - // analytics query interface - int64_t OnAnalyticsGetCurrentPosition() override - { - if (mPlayer) { - return mPlayer->GetCurrentPosition(); - } - - return -1; - } - - int64_t OnAnalyticsGetBufferedPosition() override - { - if (mPlayer) { - return mPlayer->GetBufferedPosition(); - } - - return -1; - } - - int64_t OnAnalyticsGetDuration() override - { - if (mPlayer) { - return mPlayer->GetDuration(); - } - - return -1; - } - - std::string OnAnalyticsGetPropertyString(PropertyKey key) override - { - if (mPlayer) { - return mPlayer->GetPropertyString(key); - } - - return ""; - } - - private: - MediaPlayer *mPlayer = nullptr; - }; - - MediaPlayer::MediaPlayer() - : MediaPlayer(*(AnalyticsCollectorFactory::Instance())) + MediaPlayer::MediaPlayer(const char *opt) : MediaPlayer(*(AnalyticsCollectorFactory::Instance()), opt) { } - MediaPlayer::MediaPlayer(IAnalyticsCollectorFactory &factory) - : mCollectorFactory(factory) + MediaPlayer::MediaPlayer(IAnalyticsCollectorFactory &factory, const char *opt) : mCollectorFactory(factory) { - playerHandle *handle = CicadaCreatePlayer(); + playerHandle *handle = CicadaCreatePlayer(opt); mPlayerHandle = (void *) handle; playerListener listener{nullptr}; listener.userData = this; @@ -101,6 +50,7 @@ namespace Cicada { listener.LoadingStart = loadingStartCallback; listener.LoadingEnd = loadingEndCallback; listener.LoadingProgress = loadingProgressCallback; + listener.CurrentDownLoadSpeed = currentDownLoadSpeed; listener.Seeking = PlayerSeeking; listener.SeekEnd = PlayerSeekEnd; listener.SubtitleShow = subtitleShowCallback; @@ -117,6 +67,7 @@ namespace Cicada { configPlayer(mConfig); mQueryListener = new QueryListener(this); mCollector = mCollectorFactory.createAnalyticsCollector(mQueryListener); + bExternalCollector = false; mAbrManager = new AbrManager(); std::function fun = [this](int stream) -> void { return this->abrChanged(stream); @@ -125,6 +76,40 @@ namespace Cicada { AbrBufferRefererData *pRefererData = new AbrBufferRefererData(handle); mAbrAlgo->SetRefererData(pRefererData); mAbrManager->SetAbrAlgoStrategy(mAbrAlgo); + + refreshPlayerSessionId(); + } + + void MediaPlayer::refreshPlayerSessionId() { + char signatureStr[100] = {0}; + uuid id; + uuid4_generate( &id ); + uuid_to_string( &id, signatureStr); + mPlayerSessionId = signatureStr; + if(mCollector != nullptr) { + mCollector->ReportUpdatePlaySession(mPlayerSessionId); + } + + GET_PLAYER_HANDLE; + CicadaSetOption(handle, "sessionId" , mPlayerSessionId.c_str()); + } + + string MediaPlayer::GetPlayerSessionId() { + return mPlayerSessionId; + } + + void MediaPlayer::SetAnalyticsCollector(IAnalyticsCollector * collector) { + if (mCollector && !bExternalCollector) { + mCollectorFactory.destroyAnalyticsCollector(mCollector); + // avoid be used in derivative class + mCollector = nullptr; + } + + bExternalCollector = true; + mCollector = collector; + if(mCollector != nullptr) { + mCollector->ReportUpdatePlaySession(mPlayerSessionId); + } } void MediaPlayer::dummyFunction(bool dummy) @@ -145,7 +130,7 @@ namespace Cicada { delete mConfig; CicadaReleasePlayer(&handle); - if (mCollector) { + if (mCollector && !bExternalCollector) { mCollectorFactory.destroyAnalyticsCollector(mCollector); // avoid be used in derivative class mCollector = nullptr; @@ -277,6 +262,12 @@ namespace Cicada { void MediaPlayer::Prepare() { + if(mFirstPrepared) { + refreshPlayerSessionId(); + } else { + mFirstPrepared = true; + } + if (mCollector) { mCollector->ReportBlackInfo(); mCollector->ReportPrepare(); @@ -487,6 +478,10 @@ namespace Cicada { CicadaSetOption(handle, "http_proxy", playerConfig.httpProxy.c_str()); CicadaSetOption(handle, "ClearShowWhenStop", playerConfig.bClearShowWhenStop ? "1" : "0"); CicadaSetOption(handle, "enableVideoTunnelRender", playerConfig.bEnableTunnelRender ? "1" : "0"); + CicadaSetOption(handle, "disableAudio", playerConfig.mDisableAudio ? "1" : "0"); + CicadaSetOption(handle, "disableVideo", playerConfig.mDisableVideo ? "1" : "0"); + CicadaSetOption(handle, "timerInterval", to_string(playerConfig.mPositionTimerIntervalMs).c_str()); + CicadaSetOption(handle, "networkRetryCount", to_string(playerConfig.networkRetryCount).c_str()); if (playerConfig.pixelBufferOutputFormat != 0) { CicadaSetOption(handle, "pixelBufferOutputFormat", to_string(playerConfig.pixelBufferOutputFormat).c_str()); } @@ -869,6 +864,15 @@ namespace Cicada { } } + void MediaPlayer::currentDownLoadSpeed(int64_t speed, void *userData) + { + GET_MEDIA_PLAYER + + if (player->mListener.CurrentDownLoadSpeed) { + player->mListener.CurrentDownLoadSpeed(speed, player->mListener.userData); + } + } + void MediaPlayer::subtitleShowCallback(int64_t index, int64_t size, const void *content, void *userData) { @@ -1081,13 +1085,23 @@ namespace Cicada { GET_PLAYER_HANDLE; CicadaSelectExtSubtitle(handle, index, select); } + int MediaPlayer::SetStreamDelayTime(int index, int64_t time) + { + GET_PLAYER_HANDLE; + return CicadaSetStreamDelayTime(handle, index, time); + } void MediaPlayer::SetDataSourceChangedCallback(function urlChangedCallbak) { mPlayUrlChangedCallback = urlChangedCallbak; } - void MediaPlayer::onMediaFrameCallback(void *arg, const unique_ptr &frame, StreamType type) + void MediaPlayer::setDrmRequestCallback(const std::function & drmCallback){ + GET_PLAYER_HANDLE; + CicadaSetDrmRequestCallback(handle, drmCallback); + } + + void MediaPlayer::onMediaFrameCallback(void *arg, const IAFPacket *frame, StreamType type) { MediaPlayer *player = (MediaPlayer *) arg; @@ -1098,7 +1112,7 @@ namespace Cicada { player->mediaFrameCallback(frame, type); } - void MediaPlayer::mediaFrameCallback(const unique_ptr &frame, StreamType type) + void MediaPlayer::mediaFrameCallback(const IAFPacket *frame, StreamType type) { #ifdef ENABLE_CACHE_MODULE if (mCacheManager) { @@ -1122,6 +1136,19 @@ namespace Cicada { GET_PLAYER_HANDLE; CicadaSetOnRenderCallBack(handle, cb, userData); } + + void MediaPlayer::SetAudioRenderingCallback(onRenderFrame cb, void *userData) + { + GET_PLAYER_HANDLE; + CicadaSetAudioRenderingCallBack(handle, cb, userData); + } + + void MediaPlayer::SetUpdateViewCallback(UpdateViewCB cb, void *userData) + { + GET_PLAYER_HANDLE; + CicadaSetUpdateViewCallback(handle, cb, userData); + } + void MediaPlayer::SetStreamTypeFlags(uint64_t flags) { GET_PLAYER_HANDLE; @@ -1139,9 +1166,15 @@ namespace Cicada { int value = mode; CicadaSetOption(handle, "fastStart", to_string(value).c_str()); } - int MediaPlayer::InvokeComponent(const std::string &content) + int MediaPlayer::InvokeComponent(const char *content) + { + GET_PLAYER_HANDLE; + return CicadaInvokeComponent(handle, content); + } + + std::string MediaPlayer::getName() { GET_PLAYER_HANDLE; - return CicadaInvokeComponent(handle, content.c_str()); + return CicadaGetPlayerName(handle); } } diff --git a/mediaPlayer/MediaPlayer.h b/mediaPlayer/MediaPlayer.h index 066537860..a11056fb9 100644 --- a/mediaPlayer/MediaPlayer.h +++ b/mediaPlayer/MediaPlayer.h @@ -22,6 +22,7 @@ class AbrAlgoStrategy; #include #include #include +#include #ifdef ANDROID @@ -48,9 +49,9 @@ namespace Cicada { class MediaPlayer { public: - MediaPlayer(); + explicit MediaPlayer(const char *opt = nullptr); - MediaPlayer(IAnalyticsCollectorFactory &factory); + explicit MediaPlayer(IAnalyticsCollectorFactory &factory, const char *opt = nullptr); ~MediaPlayer(); @@ -59,13 +60,21 @@ namespace Cicada { return "paas 0.9";//TODO version } + std::string getName(); + public: + + void SetAnalyticsCollector(IAnalyticsCollector * collector); + void EnableVideoRenderedCallback(bool enable); void SetOnRenderFrameCallback(onRenderFrame cb, void *userData); - void SetStreamTypeFlags(uint64_t flags); + void SetAudioRenderingCallback(onRenderFrame cb, void *userData); + void SetUpdateViewCallback(UpdateViewCB cb, void *userData); + + void SetStreamTypeFlags(uint64_t flags); void SetIPResolveType(IpResolveType type); @@ -96,6 +105,8 @@ namespace Cicada { */ void SetDataSourceChangedCallback(function urlChangedCallbak); + void setDrmRequestCallback(const std::function & drmCallback); + /* *select specific track info */ @@ -302,6 +313,8 @@ namespace Cicada { void SelectExtSubtitle(int index, bool select); + int SetStreamDelayTime(int index, int64_t time); + void setErrorConverter(ErrorConverter *converter); std::string GetPropertyString(PropertyKey key); @@ -334,7 +347,9 @@ namespace Cicada { void SetDefaultBandWidth(int bandWidth); - int InvokeComponent(const std::string &content); + int InvokeComponent(const char *content); + + string GetPlayerSessionId(); protected: static void preparedCallback(void *userData); @@ -363,6 +378,8 @@ namespace Cicada { static void loadingProgressCallback(int64_t prg, void *userData); + static void currentDownLoadSpeed(int64_t speed, void *userData); + static void subtitleShowCallback(int64_t index, int64_t size, const void *content, void *userData); static void subtitleHideCallback(int64_t index, int64_t size, const void *content, void *userData); @@ -385,12 +402,14 @@ namespace Cicada { void abrChanged(int stream); - static void onMediaFrameCallback(void *arg, const unique_ptr &frame, StreamType type); - void mediaFrameCallback(const unique_ptr &frame, StreamType type); + static void onMediaFrameCallback(void *arg, const IAFPacket *frame, StreamType type); + void mediaFrameCallback(const IAFPacket *frame, StreamType type); private: void configPlayer(const MediaPlayerConfig *config) const; + void refreshPlayerSessionId(); + void dummyFunction(bool dummy); protected: @@ -399,6 +418,7 @@ namespace Cicada { MediaPlayerConfig *mConfig; AnalyticsQueryListener *mQueryListener; IAnalyticsCollector *mCollector{nullptr}; + bool bExternalCollector{false}; IAnalyticsCollectorFactory &mCollectorFactory; AbrManager *mAbrManager; AbrAlgoStrategy *mAbrAlgo; @@ -418,6 +438,10 @@ namespace Cicada { void *mMediaFrameArg = nullptr; function mPlayUrlChangedCallback = nullptr; + + std::string mPlayerSessionId{}; + bool mFirstPrepared = false; + }; }// namespace Cicada diff --git a/mediaPlayer/MediaPlayerConfig.cpp b/mediaPlayer/MediaPlayerConfig.cpp index f34540f51..f8f4750ba 100644 --- a/mediaPlayer/MediaPlayerConfig.cpp +++ b/mediaPlayer/MediaPlayerConfig.cpp @@ -20,6 +20,9 @@ namespace Cicada { bEnableTunnelRender = false; pixelBufferOutputFormat = 0; liveStartIndex = -3; + mDisableAudio = false; + mDisableVideo = false; + mPositionTimerIntervalMs = 500; } std::string MediaPlayerConfig::toString() const @@ -36,6 +39,9 @@ namespace Cicada { item.addValue("startBufferDuration", startBufferDuration); item.addValue("bClearShowWhenStop", bClearShowWhenStop); item.addValue("bEnableTunnelRender", bEnableTunnelRender); + item.addValue("mDisableAudio", mDisableAudio); + item.addValue("mDisableVideo", mDisableVideo); + item.addValue("mPositionTimerIntervalMs", mPositionTimerIntervalMs); return item.printJSON(); } } diff --git a/mediaPlayer/MediaPlayerConfig.h b/mediaPlayer/MediaPlayerConfig.h index f4050d3b9..d887fefbd 100644 --- a/mediaPlayer/MediaPlayerConfig.h +++ b/mediaPlayer/MediaPlayerConfig.h @@ -43,6 +43,11 @@ namespace Cicada { int liveStartIndex; + bool mDisableAudio; + bool mDisableVideo; + + int mPositionTimerIntervalMs; + public: std::string toString() const; }; diff --git a/mediaPlayer/MediaPlayerUtil.cpp b/mediaPlayer/MediaPlayerUtil.cpp index 33f143cce..e347e7dfd 100644 --- a/mediaPlayer/MediaPlayerUtil.cpp +++ b/mediaPlayer/MediaPlayerUtil.cpp @@ -10,6 +10,7 @@ #include "MediaPlayerUtil.h" #include "utils/timer.h" #include "utils/CicadaJSON.h" +#include using namespace std; @@ -69,6 +70,8 @@ namespace Cicada { mLastLoopTime = 0; mLoopIndex = 0; mVideoRenderFps = 0; + mReadGotSize = 0; + mCurrentDownloadSpeed = 0; } void MediaPlayerUtil::getPropertyJSONStr(const std::string &name, CicadaJSONArray &array, bool isArray, @@ -137,7 +140,7 @@ namespace Cicada { } } - void MediaPlayerUtil::notifyRead(enum readEvent event) + void MediaPlayerUtil::notifyRead(enum readEvent event, uint64_t size) { switch (event) { case readEvent_Again: @@ -146,6 +149,8 @@ namespace Cicada { case readEvent_Got: mReadGotIndex++; + assert(size > 0); + mReadGotSize += size; break; case readEvent_timeOut: @@ -165,13 +170,15 @@ namespace Cicada { float timeS = float(time - mLastReadTime) / 1000000; if (timeS > 1.0) { + mCurrentDownloadSpeed = (float) mReadGotSize * 8 / timeS; AF_LOGD("mReadLoopIndex is \t %f\n", (float) mReadLoopIndex / timeS); AF_LOGD("mReadAgainIndex is\t %f\n", (float) mReadAgainIndex / timeS); AF_LOGD("mReadGotIndex is\t %f\n", (float) mReadGotIndex / timeS); AF_LOGD("mReadTimeOutIndex\t is %f\n", (float) mReadTimeOutIndex / timeS); + AF_LOGD("mCurrentDownloadSpeed\t is %f kbps\n", mCurrentDownloadSpeed / 1024); AF_LOGD("\n"); mLastReadTime = time; - mReadLoopIndex = mReadAgainIndex = mReadGotIndex = mReadTimeOutIndex = 0; + mReadLoopIndex = mReadAgainIndex = mReadGotIndex = mReadTimeOutIndex = mReadGotSize = 0; } } } diff --git a/mediaPlayer/MediaPlayerUtil.h b/mediaPlayer/MediaPlayerUtil.h index 825d18ff9..75e0fc37d 100644 --- a/mediaPlayer/MediaPlayerUtil.h +++ b/mediaPlayer/MediaPlayerUtil.h @@ -39,7 +39,7 @@ namespace Cicada { void notifyPlayerLoop(int64_t time); - void notifyRead(enum readEvent event); + void notifyRead(enum readEvent event, uint64_t size); void render(int64_t pts); @@ -48,6 +48,11 @@ namespace Cicada { float getVideoRenderFps() { return mVideoRenderFps; } + float getCurrentDownloadSpeed() const + { + return mCurrentDownloadSpeed; + } + static void getPropertyJSONStr(const std::string &name, CicadaJSONArray &array, bool isArray, std::deque &streamInfoQueue, demuxer_service *service); @@ -69,6 +74,9 @@ namespace Cicada { int64_t mReadGotIndex = 0; int64_t mReadTimeOutIndex = 0; int64_t mLastReadTime = 0; + uint64_t mReadGotSize{0}; + + float mCurrentDownloadSpeed{0}; float mVideoRenderFps = 0; }; diff --git a/mediaPlayer/QueryListener.cpp b/mediaPlayer/QueryListener.cpp new file mode 100644 index 000000000..727333e3d --- /dev/null +++ b/mediaPlayer/QueryListener.cpp @@ -0,0 +1,51 @@ +// +// Created by lifujun on 2020/8/10. +// + +#include "QueryListener.h" + +using namespace Cicada; + +QueryListener::QueryListener(MediaPlayer *player) { + mPlayer = player; +} + +void QueryListener::setMediaPlayer(MediaPlayer *player) { + mPlayer = player; +} + +// analytics query interface +int64_t QueryListener::OnAnalyticsGetCurrentPosition() { + if (mPlayer) { + return mPlayer->GetCurrentPosition(); + + } + + return -1; +} + +int64_t QueryListener::OnAnalyticsGetBufferedPosition() { + if (mPlayer) { + return mPlayer->GetBufferedPosition(); + + } + + return -1; +} + +int64_t QueryListener::OnAnalyticsGetDuration() { + if (mPlayer) { + return mPlayer->GetDuration(); + + } + + return -1; +} + +std::string QueryListener::OnAnalyticsGetPropertyString(PropertyKey key) { + if (mPlayer) { + return mPlayer->GetPropertyString(key); + } + + return ""; +} diff --git a/mediaPlayer/QueryListener.h b/mediaPlayer/QueryListener.h new file mode 100644 index 000000000..6933ac0bc --- /dev/null +++ b/mediaPlayer/QueryListener.h @@ -0,0 +1,34 @@ +// +// Created by lifujun on 2020/8/10. +// + +#ifndef SOURCE_QUERYLISTENER_H +#define SOURCE_QUERYLISTENER_H + +#include "MediaPlayer.h" +#include "analytics/AnalyticsQueryListener.h" + +namespace Cicada { + class QueryListener : public AnalyticsQueryListener { + public: + explicit QueryListener(MediaPlayer *player); + + ~QueryListener() override = default; + + void setMediaPlayer(MediaPlayer *player); + + // analytics query interface + int64_t OnAnalyticsGetCurrentPosition() override; + + int64_t OnAnalyticsGetBufferedPosition() override; + + int64_t OnAnalyticsGetDuration() override; + + std::string OnAnalyticsGetPropertyString(PropertyKey key) override; + + private: + MediaPlayer *mPlayer = nullptr; + }; +} + +#endif //SOURCE_QUERYLISTENER_H diff --git a/mediaPlayer/SMPAVDeviceManager.cpp b/mediaPlayer/SMPAVDeviceManager.cpp new file mode 100644 index 000000000..8adc57d84 --- /dev/null +++ b/mediaPlayer/SMPAVDeviceManager.cpp @@ -0,0 +1,356 @@ +// +// Created by pingkai on 2020/11/2. +// + +#define LOG_TAG "SMPAVDeviceManager" +#include "SMPAVDeviceManager.h" +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#endif + +using namespace Cicada; +using namespace std; +SMPAVDeviceManager::SMPAVDeviceManager() +{} +SMPAVDeviceManager::~SMPAVDeviceManager() +{ + if (mAudioDecoder.decoder) { + mAudioDecoder.decoder->close(); + } + if (mVideoDecoder.decoder) { + flushVideoRender(); + mVideoRender = nullptr; + mVideoDecoder.decoder->flush(); + mVideoDecoder.decoder->close(); + } +} +int SMPAVDeviceManager::setUpDecoder(uint64_t decFlag, const Stream_meta *meta, void *device, deviceType type, uint32_t dstFormat) +{ + std::lock_guard uMutex(mMutex); + DecoderHandle *decoderHandle = getDecoderHandle(type); + if (decoderHandle == nullptr) { + return -EINVAL; + } + if (decoderHandle->valid) { + return 0; + } + + DrmInfo drmInfo{}; + if (meta->keyFormat != nullptr) { + drmInfo.format = meta->keyFormat; + drmInfo.uri = meta->keyUrl == nullptr ? "" : meta->keyUrl; + } + + if (decoderHandle->decoder) { + if (decoderHandle->match(meta, decFlag, device, dstFormat, drmInfo) && decoderHandle->decoder->supportReuse()) {// reuse decoder + + AF_LOGI("reuse decoder %s\n", type == DEVICE_TYPE_VIDEO ? "video" : "audio "); + decoderHandle->valid = true; + decoderHandle->meta = *meta; + decoderHandle->mDstFormat = dstFormat; + decoderHandle->mDrmInfo = drmInfo; + flushVideoRender(); + decoderHandle->decoder->flush(); + decoderHandle->decoder->pause(false); + return 0; + } + /* + * must flush decoder before close on android mediacodec decoder + */ + if (meta->type == STREAM_TYPE_VIDEO && decoderHandle->decoder->getName() == "VD.mediaCodec") { + flushVideoRender(); + } + decoderHandle->decoder->flush(); + decoderHandle->decoder->close(); + } + + decoderHandle->meta = *meta; + decoderHandle->decFlag = decFlag; + decoderHandle->device = device; + decoderHandle->decoder = + decoderFactory::create(*meta, decFlag, std::max(meta->height, meta->width), drmInfo.empty() ? nullptr : &drmInfo); + decoderHandle->mDrmInfo = drmInfo; + + if (decoderHandle->decoder == nullptr) { + return gen_framework_errno(error_class_codec, codec_error_video_not_support); + } + decoderHandle->decoder->setRequireDrmHandlerCallback(mRequireDrmHandlerCallback); + int ret; + if (dstFormat) { +#ifdef __APPLE__ + auto *vtbDecoder = dynamic_cast(decoderHandle->decoder.get()); + if (vtbDecoder) { + ret = vtbDecoder->setPixelBufferFormat(dstFormat); + if (ret < 0) { + AF_LOGW("setPixelBufferFormat error\n"); + } + } +#endif + } + ret = decoderHandle->decoder->open(meta, device, decFlag, drmInfo.empty() ? nullptr : &drmInfo); + if (ret < 0) { + AF_LOGE("config decoder error ret= %d \n", ret); + decoderHandle->decoder = nullptr; + return gen_framework_errno(error_class_codec, codec_error_video_device_error); + } + decoderHandle->valid = true; + return 0; +} +SMPAVDeviceManager::DecoderHandle *SMPAVDeviceManager::getDecoderHandle(const SMPAVDeviceManager::deviceType &type) +{ + DecoderHandle *decoderHandle = nullptr; + if (type == DEVICE_TYPE_VIDEO) { + decoderHandle = &mVideoDecoder; + } else if (type == DEVICE_TYPE_AUDIO) { + decoderHandle = &mAudioDecoder; + } + return decoderHandle; +} + +void SMPAVDeviceManager::invalidDevices(uint64_t deviceTypes) +{ + std::lock_guard uMutex(mMutex); + if (deviceTypes & DEVICE_TYPE_AUDIO) { + if (mAudioDecoder.decoder) { + mAudioDecoder.decoder->prePause(); + } + if (mAudioRender) { + mAudioRender->prePause(); + mAudioRender->mute(true); + } + mAudioDecoder.valid = false; + mAudioRenderValid = false; + } + if (deviceTypes & DEVICE_TYPE_VIDEO) { + if (mVideoDecoder.decoder) { + mVideoDecoder.decoder->prePause(); + } + // if (mVideoRender) { + // mVideoRender->invalid(true); + // } + mVideoDecoder.valid = false; + mVideoRenderValid = false; + } +} +void SMPAVDeviceManager::flushDevice(uint64_t deviceTypes) +{ + /* + * flush devices only on valid, otherwise the devices will be flushed on reusing + */ + if (deviceTypes & DEVICE_TYPE_AUDIO) { + if (mAudioDecoder.valid) { + assert(mAudioDecoder.decoder != nullptr); + mAudioDecoder.decoder->flush(); + } + if (mAudioRenderValid + /* + flush the Audio render on APPLE platform, otherwise it will output nise on reuse, but I don't know the reason. + */ +#if __APPLE__ + || mAudioRender +#endif + ) { + mAudioRender->flush(); + } + } + if (deviceTypes & DEVICE_TYPE_VIDEO) { + if (mVideoDecoder.valid) { + mVideoDecoder.decoder->flush(); + } + + flushVideoRender(); + } +} +int SMPAVDeviceManager::getFrame(std::unique_ptr &frame, deviceType type, uint64_t timeOut) +{ + DecoderHandle *decoderHandle = getDecoderHandle(type); + if (decoderHandle == nullptr || !decoderHandle->valid) { + return -EINVAL; + } + assert(decoderHandle->decoder); + return decoderHandle->decoder->getFrame(frame, timeOut); +} +int SMPAVDeviceManager::sendPacket(std::unique_ptr &packet, deviceType type, uint64_t timeOut) +{ + DecoderHandle *decoderHandle = getDecoderHandle(type); + if (decoderHandle == nullptr || !decoderHandle->valid) { + return -EINVAL; + } + assert(decoderHandle->decoder); + return decoderHandle->decoder->send_packet(packet, timeOut); +} +int SMPAVDeviceManager::setVolume(float volume) +{ + if (mAudioRender) { + return mAudioRender->setVolume(volume); + } + // TODO: save the value + return 0; +} +uint64_t SMPAVDeviceManager::getAudioRenderQueDuration() +{ + if (mAudioRender) { + return mAudioRender->getQueDuration(); + } + return 0; +} +int SMPAVDeviceManager::renderAudioFrame(std::unique_ptr &frame, int timeOut) +{ + if (mAudioRender) { + int ret = mAudioRender->renderFrame(frame, timeOut); + if (ret == IAudioRender::FORMAT_NOT_SUPPORT) { + if (mAudioRender->getQueDuration() == 0) { + mAudioRender = nullptr; + mAudioRenderValid = false; + return ret; + } else { + return -EAGAIN; + } + } + return ret; + } + return -EINVAL; +} +void SMPAVDeviceManager::pauseAudioRender(bool pause) +{ + if (mAudioRender) { + mAudioRender->pause(pause); + } + // TODO: save the status +} +int SMPAVDeviceManager::setUpAudioRender(const IAFFrame::audioInfo &info) +{ + std::lock_guard uMutex(mMutex); + if (mAudioRenderValid) { + assert(mAudioRender != nullptr); + return 0; + } + if (mAudioRender) { + mAudioRender->flush(); + mAudioRender->mute(mMute); + mAudioRender->pause(false); + mAudioRenderValid = true; + return 0; + } + if (mAudioRender == nullptr) { + mAudioRender = AudioRenderFactory::create(); + } + + assert(mAudioRender); + int audioInitRet = mAudioRender->init(&info); + + if (audioInitRet < 0) { + AF_LOGE("AudioOutHandle Init Error is %d", audioInitRet); + return -1; + } else { + mAudioRenderInfo = info; + mAudioRenderValid = true; + return 0; + } +} +int SMPAVDeviceManager::setSpeed(float speed) +{ + if (mAudioRender) { + mAudioRender->setSpeed(speed); + } + if (mVideoRender) { + mVideoRender->setSpeed(speed); + } + // TODO: save + return 0; +} +int64_t SMPAVDeviceManager::getAudioRenderPosition() +{ + if (mAudioRender) { + return mAudioRender->getPosition(); + } + return INT64_MIN; +} +void SMPAVDeviceManager::setAudioRenderListener(IAudioRenderListener *listener) +{ + if (mAudioRender) { + mAudioRender->setListener(listener); + } +} +void SMPAVDeviceManager::setVideoRenderListener(IVideoRender::IVideoRenderListener *listener) +{ + if (mVideoRender) { + mVideoRender->setListener(listener); + } +} +void SMPAVDeviceManager::setMute(bool mute) +{ + if (mAudioRender) { + mAudioRender->mute(mute); + } + mMute = mute; +} +void SMPAVDeviceManager::setAudioRenderingCb(renderingFrameCB cb, void *userData) +{ + if (mAudioRender) { + mAudioRender->setRenderingCb(cb, userData); + } +} +uint64_t SMPAVDeviceManager::getVideoDecoderFlags() +{ + if (mVideoDecoder.decoder) { + return static_cast(mVideoDecoder.decoder->getFlags()); + } + return 0; +} +int SMPAVDeviceManager::createVideoRender(uint64_t flags) +{ + if (mVideoRenderValid) { + return 0; + } + if (mVideoRender && mVideoRenderFlags == flags) { + flushVideoRender(); + mVideoRender->invalid(true); + mVideoRenderValid = true; + return 0; + } + + if (mVideoRender) { + mVideoRender->clearScreen(); + } + + mVideoRender = videoRenderFactory::create(flags); +// assert(mVideoRender != nullptr); + if (mVideoRender){ + mVideoRenderValid = true; + mVideoRenderFlags = flags; + } + return 0; +} +void SMPAVDeviceManager::flushVideoRender() +{ + if (mVideoRender) { + unique_ptr frame{nullptr}; + mVideoRender->renderFrame(frame); + } +} +int SMPAVDeviceManager::renderVideoFrame(unique_ptr &frame) +{ + if (mVideoRender) { + int ret = mVideoRender->renderFrame(frame); + mVideoRender->invalid(false); + return ret; + } + return -EINVAL; +} +void SMPAVDeviceManager::destroyVideoRender() +{ + mVideoRender = nullptr; + mVideoRenderValid = false; +} + +void SMPAVDeviceManager::setRequireDrmHandlerCallback( + const std::function& callback) { + mRequireDrmHandlerCallback = callback; +} diff --git a/mediaPlayer/SMPAVDeviceManager.h b/mediaPlayer/SMPAVDeviceManager.h new file mode 100644 index 000000000..70d01c8a2 --- /dev/null +++ b/mediaPlayer/SMPAVDeviceManager.h @@ -0,0 +1,149 @@ +// +// Created by pingkai on 2020/11/2. +// + +#ifndef CICADAMEDIA_SMPAVDEVICEMANAGER_H +#define CICADAMEDIA_SMPAVDEVICEMANAGER_H + +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + + +// TODO: add create lock +namespace Cicada { + class SMPAVDeviceManager { + public: + enum deviceType { DEVICE_TYPE_VIDEO = 1 << 0, DEVICE_TYPE_AUDIO = 1 << 1 }; + + struct DecoderHandle { + std::unique_ptr decoder{nullptr}; + Stream_meta meta{}; + bool valid{false}; + uint64_t decFlag; + void *device; + uint32_t mDstFormat{0}; + DrmInfo mDrmInfo{}; + + bool match(const Stream_meta *pMeta, uint64_t flag, void *pDevice, uint32_t dstFormat, const DrmInfo &info) + { +#ifdef __APPLE__ + auto *vtbDecoder = dynamic_cast(decoder.get()); + if (vtbDecoder) { + if (pMeta->interlaced) { + return false; + } + } +#endif + return (pDevice == device) && (flag == decFlag) && (pMeta->codec == meta.codec) && (dstFormat == mDstFormat) && + (mDrmInfo == info); + } + }; + + public: + SMPAVDeviceManager(); + ~SMPAVDeviceManager(); + + IDecoder *getDecoder(deviceType type) const + { + if (type == DEVICE_TYPE_VIDEO) { + return mVideoDecoder.decoder.get(); + } else if (type == DEVICE_TYPE_AUDIO) { + return mAudioDecoder.decoder.get(); + } + return nullptr; + } + + bool isDecoderValid(deviceType type) const + { + if (type == DEVICE_TYPE_VIDEO) { + return mVideoDecoder.valid; + } else if (type == DEVICE_TYPE_AUDIO) { + return mAudioDecoder.valid; + } + return false; + } + bool isAudioRenderValid() const + { + return mAudioRenderValid; + } + + void invalidDevices(uint64_t deviceTypes); + + void flushDevice(uint64_t deviceTypes); + + int getFrame(std::unique_ptr &frame, deviceType type, uint64_t timeOut); + + int sendPacket(std::unique_ptr &packet, deviceType type, uint64_t timeOut); + + int setVolume(float volume); + + void setMute(bool mute); + + uint64_t getAudioRenderQueDuration(); + + int renderAudioFrame(std::unique_ptr &frame, int timeOut); + + void pauseAudioRender(bool pause); + + int setUpAudioRender(const IAFFrame::audioInfo &info); + + int setSpeed(float speed); + + int64_t getAudioRenderPosition(); + + void setAudioRenderListener(IAudioRenderListener *listener); + + void setVideoRenderListener(IVideoRender::IVideoRenderListener *listener); + + void setAudioRenderingCb(renderingFrameCB cb, void *userData); + + int setUpDecoder(uint64_t decFlag, const Stream_meta *meta, void *device, deviceType type, uint32_t dstFormat); + + uint64_t getVideoDecoderFlags(); + + int createVideoRender(uint64_t flags); + + void destroyVideoRender(); + + bool isVideoRenderValid() + { + return mVideoRenderValid; + } + IVideoRender *getVideoRender() + { + if (mVideoRender) { + return mVideoRender.get(); + } + return nullptr; + } + void flushVideoRender(); + + int renderVideoFrame(std::unique_ptr &frame); + + void setRequireDrmHandlerCallback(const std::function& callback); + + private: + DecoderHandle *getDecoderHandle(const deviceType &type); + + private: + std::mutex mMutex{}; + DecoderHandle mAudioDecoder; + DecoderHandle mVideoDecoder; + std::unique_ptr mAudioRender{nullptr}; + IAFFrame::audioInfo mAudioRenderInfo{}; + bool mAudioRenderValid{false}; + bool mMute{false}; + std::unique_ptr mVideoRender{nullptr}; + bool mVideoRenderValid{false}; + uint64_t mVideoRenderFlags{0}; + std::function mRequireDrmHandlerCallback{nullptr}; + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_SMPAVDEVICEMANAGER_H diff --git a/mediaPlayer/SMPMessageControllerListener.cpp b/mediaPlayer/SMPMessageControllerListener.cpp new file mode 100644 index 000000000..880fa78c3 --- /dev/null +++ b/mediaPlayer/SMPMessageControllerListener.cpp @@ -0,0 +1,795 @@ +// +// Created by pingkai on 2020/12/28. +// + +#include "SMPMessageControllerListener.h" + +#include "SuperMediaPlayer.h" +#include "media_player_error_def.h" +#include +#include +#include +#include +#include + +#define HAVE_VIDEO (mPlayer.mCurrentVideoIndex >= 0) +#define HAVE_AUDIO (mPlayer.mCurrentAudioIndex >= 0) +#define HAVE_SUBTITLE (mPlayer.mCurrentSubtitleIndex >= 0) +#define PTS_DISCONTINUE_DELTA (20 * 1000 * 1000) +using namespace Cicada; +SMPMessageControllerListener::SMPMessageControllerListener(SuperMediaPlayer &player) : mPlayer(player) +{} +SMPMessageControllerListener::~SMPMessageControllerListener() = default; + +bool SMPMessageControllerListener::OnPlayerMsgIsPadding(PlayMsgType msg, MsgParam msgContent) +{ + bool padding = false; + + switch (msg) { + case MSG_CHANGE_VIDEO_STREAM: + padding = mPlayer.mVideoChangedFirstPts != INT64_MIN; + break; + + case MSG_CHANGE_AUDIO_STREAM: + padding = mPlayer.mAudioChangedFirstPts != INT64_MIN; + break; + + case MSG_CHANGE_SUBTITLE_STREAM: + padding = mPlayer.mSubtitleChangedFirstPts != INT64_MIN; + break; + + case MSG_SEEKTO: + if (mPlayer.mSeekFlag) { + padding = true; + } + + break; + + default: + padding = false; + } + + return padding; +} + +void SMPMessageControllerListener::ProcessPrepareMsg() +{ + AF_LOGD("ProcessPrepareMsg start"); + int ret; + + if (mPlayer.mSet->url.empty() && mPlayer.mBSReadCb == nullptr) { + AF_LOGD("ProcessPrepareMsg url is empty"); + mPlayer.ChangePlayerStatus(PLAYER_ERROR); + mPlayer.mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DATASOURCE_EMPTYURL, "Prepare url is empty"); + return; + } + + if (mPlayer.mPlayStatus != PLAYER_INITIALZED && mPlayer.mPlayStatus != PLAYER_STOPPED) { + AF_LOGD("ProcessPrepareMsg status is %d", mPlayer.mPlayStatus.load()); + return; + } + + mPlayer.mPlayStatus = PLAYER_PREPARINIT; + bool noFile = false; + + if (!(mPlayer.mBSReadCb != nullptr && mPlayer.mBSSeekCb != nullptr && mPlayer.mBSCbArg != nullptr)) { + if (!mPlayer.mSet->url.empty()) { + ret = openUrl(); + + if (ret < 0) { + AF_LOGD("%s mDataSource open failed,url is %s %s", __FUNCTION__, mPlayer.mSet->url.c_str(), framework_err2_string(ret)); + + if (ret == FRAMEWORK_ERR_EXIT) { + // stop by user. + //ChangePlayerStatus(PLAYER_STOPPED); + return; + } else if (ret == FRAMEWORK_ERR_PROTOCOL_NOT_SUPPORT) { + noFile = true; + } else { + mPlayer.NotifyError(ret); + return; + } + } + } + } + + if (mPlayer.mCanceled) { + return; + } + + { + std::lock_guard locker(mPlayer.mCreateMutex); + mPlayer.mDemuxerService = new demuxer_service(mPlayer.mDataSource); + mPlayer.mDemuxerService->setOptions(&mPlayer.mSet->mOptions); + } + + std::function demuxerCB = [this](const std::string &key, const std::string &value) -> void { + this->mPlayer.OnDemuxerCallback(key, value); + }; + mPlayer.mDemuxerService->setDemuxerCb(demuxerCB); + mPlayer.mDemuxerService->setNoFile(noFile); + + if (!noFile) { + mPlayer.mDemuxerService->SetDataCallBack(mPlayer.mBSReadCb, mPlayer.mBSCbArg, mPlayer.mBSSeekCb, mPlayer.mBSCbArg, nullptr); + } + + + //prepare之前seek + if (mPlayer.mSeekPos > 0) { + mPlayer.mDemuxerService->Seek(mPlayer.mSeekPos, 0, -1); + mPlayer.mSeekFlag = true; + } else { + mPlayer.ResetSeekStatus(); + } + + AF_LOGD("initOpen start"); + ret = mPlayer.mDemuxerService->createDemuxer((mPlayer.mBSReadCb || noFile) ? demuxer_type_bit_stream : demuxer_type_unknown); + + // TODO: video tool box HW decoder not merge the header + if (mPlayer.mDemuxerService->getDemuxerHandle()) { +#ifdef __APPLE__ + mPlayer.mDemuxerService->getDemuxerHandle()->setBitStreamFormat(header_type::header_type_extract, header_type::header_type_extract); +#else + mPlayer.mDemuxerService->getDemuxerHandle()->setBitStreamFormat(header_type::header_type_merge, header_type::header_type_merge); +#endif + if (noFile) { + IDataSource::SourceConfig config; + mPlayer.mDataSource->Get_config(config); + mPlayer.mDemuxerService->getDemuxerHandle()->setDataSourceConfig(config); + } + + mPlayer.mDemuxerService->getDemuxerHandle()->SetOption("sessionId", mPlayer.mSet->sessionId); + + mPlayer.mDcaManager->createObservers(); + mPlayer.sendDCAMessage(); + } + + //step2: Demuxer init and getstream index + ret = mPlayer.mDemuxerService->initOpen((mPlayer.mBSReadCb || noFile) ? demuxer_type_bit_stream : demuxer_type_unknown); + + if (ret < 0) { + if (ret != FRAMEWORK_ERR_EXIT && !mPlayer.mCanceled) { + mPlayer.NotifyError(ret); + } + + return; + } + + int nbStream = mPlayer.mDemuxerService->GetNbStreams(); + AF_LOGD("Demuxer service get nubmer streams is %d", nbStream); + unique_ptr pMeta; + int bandWidthNearStreamIndex = -1; + int minBandWidthDelta = INT_MAX; + int mDefaultBandWidth = mPlayer.mSet->mDefaultBandWidth; + + for (int i = 0; i < nbStream; ++i) { + mPlayer.mDemuxerService->GetStreamMeta(pMeta, i, false); + auto *meta = (Stream_meta *) (pMeta.get()); + + if (meta->type == STREAM_TYPE_MIXED) { + mPlayer.mMixMode = true; + } + + if (meta->type == STREAM_TYPE_MIXED || meta->type == STREAM_TYPE_VIDEO) { + int metaBandWidth = (int) meta->bandwidth; + + if (abs(mDefaultBandWidth - metaBandWidth) < minBandWidthDelta) { + bandWidthNearStreamIndex = i; + minBandWidthDelta = abs(mDefaultBandWidth - metaBandWidth); + } + } + } + + for (int i = 0; i < nbStream; ++i) { + int openStreamRet = 0; + mPlayer.mDemuxerService->GetStreamMeta(pMeta, i, false); + auto *meta = (Stream_meta *) (pMeta.get()); + + if (mPlayer.mDuration < 0) { + mPlayer.mDuration = meta->duration; + } + + auto *info = new StreamInfo(); + info->streamIndex = i; + info->subtitleLang = nullptr; + info->audioLang = nullptr; + info->description = nullptr; + AF_LOGD("get a stream %d\n", meta->type); + + if (!mPlayer.mSet->bDisableVideo && meta->type == STREAM_TYPE_VIDEO) { + info->type = ST_TYPE_VIDEO; + info->videoWidth = meta->width; + info->videoHeight = meta->height; + info->videoBandwidth = (int) meta->bandwidth; + info->HDRType = VideoHDRType_SDR; + if (meta->pixel_fmt == AF_PIX_FMT_YUV420P10BE || meta->pixel_fmt == AF_PIX_FMT_YUV420P10LE) { + info->HDRType = VideoHDRType_HDR10; + } + + if (meta->description) { + info->description = strdup((const char *) meta->description); + } + + mPlayer.mStreamInfoQueue.push_back(info); + mPlayer.mVideoInterlaced = meta->interlaced; + + if (mPlayer.mCurrentVideoIndex < 0 && !mPlayer.mMixMode && meta->attached_pic == 0) { + if (bandWidthNearStreamIndex == i) { + AF_LOGD("get a video stream\n"); + openStreamRet = mPlayer.mDemuxerService->OpenStream(i); + mPlayer.mCurrentVideoIndex = i; + mPlayer.updateVideoMeta(); + mPlayer.mDemuxerService->GetStreamMeta(mPlayer.mCurrentVideoMeta, i, false); + } + } + } else if (!mPlayer.mSet->bDisableAudio && meta->type == STREAM_TYPE_AUDIO) { + info->type = ST_TYPE_AUDIO; + + if (meta->lang) { + info->audioLang = strdup((const char *) meta->lang); + } + + if (meta->description) { + info->description = strdup((const char *) meta->description); + } + + info->nChannels = meta->channels; + info->sampleFormat = meta->sample_fmt; + info->sampleRate = meta->samplerate; + mPlayer.mStreamInfoQueue.push_back(info); + + if (mPlayer.mCurrentAudioIndex < 0 && !mPlayer.mMixMode) { + AF_LOGD("get a audio stream\n"); + openStreamRet = mPlayer.mDemuxerService->OpenStream(i); + mPlayer.mCurrentAudioIndex = i; + mPlayer.mCATimeBase = meta->ptsTimeBase; + } + } else if (meta->type == STREAM_TYPE_SUB) { + info->type = ST_TYPE_SUB; + + if (meta->lang) { + info->subtitleLang = strdup((const char *) meta->lang); + } + + if (meta->description) { + info->description = strdup((const char *) meta->description); + } + + mPlayer.mStreamInfoQueue.push_back(info); + + if (mPlayer.mCurrentSubtitleIndex < 0 && + /* + * The codec of the subtitle stream can't be detected in HLS master play list + */ + (meta->codec != AF_CODEC_ID_NONE || mPlayer.mDemuxerService->isPlayList())) { + AF_LOGD("get a subtitle stream\n"); + openStreamRet = mPlayer.mDemuxerService->OpenStream(i); + mPlayer.mCurrentSubtitleIndex = i; + } + } else if (meta->type == STREAM_TYPE_MIXED) { + info->type = ST_TYPE_VIDEO; + info->streamIndex = i; + info->videoBandwidth = (int) meta->bandwidth; + info->videoWidth = meta->width; + info->videoHeight = meta->height; + AF_LOGD("STREAM_TYPE_MIXED bandwidth is %llu", meta->bandwidth); + + if (mPlayer.mMainStreamId >= 0) { + AF_LOGD("already readed stream"); + } else if (bandWidthNearStreamIndex == i) { + mPlayer.mMixMode = true; + openStreamRet = mPlayer.mDemuxerService->OpenStream(i); + mPlayer.mMainStreamId = i; + } + + mPlayer.mStreamInfoQueue.push_back(info); + } else { + delete info; + } + + if (openStreamRet < 0) { + mPlayer.ChangePlayerStatus(PLAYER_ERROR); + mPlayer.mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DEMUXER_OPENSTREAM, "open stream failed"); + return; + } + } + + if (!HAVE_VIDEO) { + mPlayer.mSeekNeedCatch = false; + } + + AF_LOGD("initOpen end"); + mPlayer.mDemuxerService->start(); + mPlayer.ChangePlayerStatus(PLAYER_PREPARING); + mPlayer.mTimeoutStartTime = INT64_MIN; +} +void SMPMessageControllerListener::ProcessStartMsg() +{ + if (mPlayer.mPlayStatus == PLAYER_PAUSED || mPlayer.mPlayStatus == PLAYER_PREPARED || mPlayer.mPlayStatus == PLAYER_COMPLETION) { + mPlayer.mUtil->reset(); + + if (mPlayer.mPlayStatus != PLAYER_PAUSED) { + if (HAVE_AUDIO) { + mPlayer.mMasterClock.setTime(mPlayer.mFirstAudioPts); + } else { + mPlayer.mMasterClock.setTime(mPlayer.mFirstVideoPts); + } + } + + mPlayer.ChangePlayerStatus(PLAYER_PLAYING); + } +} + +void SMPMessageControllerListener::ProcessPauseMsg() +{ + if (mPlayer.mPlayStatus != PLAYER_PLAYING) { + return; + } + + mPlayer.ChangePlayerStatus(PLAYER_PAUSED); + mPlayer.startRendering(false); +} + +// TODO: set layout when init videoRender? +void SMPMessageControllerListener::ProcessSetDisplayMode() +{ + if (mPlayer.mAVDeviceManager->isVideoRenderValid()) { + mPlayer.mAVDeviceManager->getVideoRender()->setScale(SuperMediaPlayer::convertScaleMode(mPlayer.mSet->scaleMode)); + } +} + +void SMPMessageControllerListener::ProcessSetRotationMode() +{ + if (mPlayer.mAVDeviceManager->isVideoRenderValid()) { + mPlayer.mAVDeviceManager->getVideoRender()->setRotate(SuperMediaPlayer::convertRotateMode(mPlayer.mSet->rotateMode)); + } +} + +void SMPMessageControllerListener::ProcessSetMirrorMode() +{ + if (mPlayer.mAVDeviceManager->isVideoRenderValid()) { + mPlayer.mAVDeviceManager->getVideoRender()->setFlip(SuperMediaPlayer::convertMirrorMode(mPlayer.mSet->mirrorMode)); + } +} + +void SMPMessageControllerListener::ProcessSetVideoBackgroundColor() +{ + if (mPlayer.mAVDeviceManager->isVideoRenderValid()) { + mPlayer.mAVDeviceManager->getVideoRender()->setBackgroundColor(mPlayer.mSet->mVideoBackgroundColor); + } +} + +void SMPMessageControllerListener::ProcessSetViewMsg(void *view) +{ + if (view == mPlayer.mSet->mView) { + return; + } + + mPlayer.mSet->mView = view; + std::unique_lock uMutex(mPlayer.mCreateMutex); + + if (mPlayer.mAVDeviceManager->getVideoRender() != nullptr) { + mPlayer.mAVDeviceManager->getVideoRender()->setDisPlay(view); + } +} + +void SMPMessageControllerListener::ProcessSetDataSourceMsg(const std::string &url) +{ + if (mPlayer.mPlayStatus == PLAYER_IDLE || mPlayer.mPlayStatus == PLAYER_STOPPED) { + mPlayer.mSet->url = url; + mPlayer.ChangePlayerStatus(PLAYER_INITIALZED); + } +} + +void SMPMessageControllerListener::ProcessSetBitStreamMsg(readCB read, seekCB seek, void *arg) +{ + if (mPlayer.mPlayStatus == PLAYER_IDLE || mPlayer.mPlayStatus == PLAYER_STOPPED) { + mPlayer.mBSReadCb = read; + mPlayer.mBSSeekCb = seek; + mPlayer.mBSCbArg = arg; + mPlayer.ChangePlayerStatus(PLAYER_INITIALZED); + } +} + +void SMPMessageControllerListener::ProcessSeekToMsg(int64_t seekPos, bool bAccurate) +{ + mPlayer.mSeekNeedCatch = bAccurate; + mPlayer.mSeekPos = seekPos; + + // seek before prepare, should keep mSeekPos + if (mPlayer.mPlayStatus < PLAYER_PREPARING || + // if reuse player.. + mPlayer.mPlayStatus == PLAYER_STOPPED) { + return; + } + + //can seek when finished + if ((0 >= mPlayer.mDuration) || (mPlayer.mPlayStatus >= PLAYER_STOPPED && mPlayer.mPlayStatus != PLAYER_COMPLETION)) { + mPlayer.ResetSeekStatus(); + return; + } + //checkPosInPackQueue cache in seek + //TODO: seek sync + mPlayer.mSeekFlag = true; + mPlayer.mPlayedVideoPts = INT64_MIN; + mPlayer.mPlayedAudioPts = INT64_MIN; + mPlayer.mSoughtVideoPos = INT64_MIN; + mPlayer.mCurVideoPts = INT64_MIN; + //flush packet queue + mPlayer.mSeekInCache = mPlayer.SeekInCache(seekPos); + + mPlayer.mPNotifier->NotifySeeking(mPlayer.mSeekInCache); + + if (mPlayer.mSeekNeedCatch && !HAVE_VIDEO) { + mPlayer.mSeekNeedCatch = false; + } + + if (!mPlayer.mSeekInCache) { + mPlayer.mBufferController->ClearPacket(BUFFER_TYPE_ALL); + int64_t ret = mPlayer.mDemuxerService->Seek(seekPos, 0, -1); + + if (ret < 0) { + mPlayer.NotifyError(ret); + } + //in case of seekpos larger than duration. + mPlayer.mPNotifier->NotifyBufferPosition((seekPos <= mPlayer.mDuration ? seekPos : mPlayer.mDuration) / 1000); + mPlayer.mEof = false; + + if ((mPlayer.mVideoChangedFirstPts != INT64_MAX) && (INT64_MIN != mPlayer.mVideoChangedFirstPts)) { + mPlayer.mVideoChangedFirstPts = seekPos; + } + } else { + AF_LOGI("sought in cache"); + + if (mPlayer.mSeekNeedCatch) { + int64_t videoPos = mPlayer.mBufferController->GetKeyTimePositionBefore(BUFFER_TYPE_VIDEO, mPlayer.mSeekPos); + + if (videoPos < (mPlayer.mSeekPos - mPlayer.mSet->maxASeekDelta)) { + // first frame is far away from seek position, don't suppport accurate seek + mPlayer.mSeekNeedCatch = false; + } else { + mPlayer.mBufferController->ClearPacketBeforeTimePos(BUFFER_TYPE_AUDIO, mPlayer.mSeekPos); + } + } + + if ((mPlayer.mVideoChangedFirstPts != INT64_MAX) && (INT64_MIN != mPlayer.mVideoChangedFirstPts) && + (seekPos > mPlayer.mVideoChangedFirstPts)) { + mPlayer.mVideoChangedFirstPts = seekPos; + } + } + + mPlayer.FlushVideoPath(); + mPlayer.FlushAudioPath(); + mPlayer.FlushSubtitleInfo(); + + if (mPlayer.mSubPlayer) { + mPlayer.mSubPlayer->seek(seekPos); + } + + mPlayer.mFirstBufferFlag = true; + mPlayer.mMasterClock.setTime(seekPos); +} + +void SMPMessageControllerListener::ProcessMuteMsg() +{ + mPlayer.mAVDeviceManager->setMute(mPlayer.mSet->bMute); +} + +void SMPMessageControllerListener::ProcessSwitchStreamMsg(int index) +{ + if (mPlayer.mDemuxerService == nullptr) { + return; + } + + Stream_type type = STREAM_TYPE_UNKNOWN; + int i; + int number = mPlayer.mDemuxerService->GetNbStreams(); + + for (i = 0; i < number; i++) { + if (index == i) { + unique_ptr pMeta; + mPlayer.mDemuxerService->GetStreamMeta(pMeta, i, false); + auto *meta = (Stream_meta *) (pMeta.get()); + type = meta->type; + break; + } + } + + if (i >= number) { + AF_LOGW("no such stream\n"); + return; + } + + if (mPlayer.mDuration == 0) { + int id = GEN_STREAM_INDEX(index); + + if (mPlayer.mMainStreamId == -1 || mPlayer.mMainStreamId == id) { + AF_LOGD("current stream index is the same"); + return; + } + + mPlayer.mVideoChangedFirstPts = INT64_MAX; + mPlayer.mAudioChangedFirstPts = INT64_MAX; + mPlayer.mEof = false; + mPlayer.mDemuxerService->SwitchStreamAligned(mPlayer.mMainStreamId, id); + return; + } + + if (type == STREAM_TYPE_MIXED) { + int id = GEN_STREAM_INDEX(index); + + if (mPlayer.mMainStreamId == -1 || mPlayer.mMainStreamId == id) { + AF_LOGD("current stream index is the same"); + return; + } + + mPlayer.mVideoChangedFirstPts = INT64_MAX; + mPlayer.mAudioChangedFirstPts = INT64_MAX; + mPlayer.mEof = false; + switchVideoStream(id, type); + return; + } + + if (type == STREAM_TYPE_SUB && mPlayer.mCurrentSubtitleIndex >= 0 && mPlayer.mCurrentSubtitleIndex != index) { + return switchSubTitle(index); + } else if (type == STREAM_TYPE_AUDIO && mPlayer.mCurrentAudioIndex >= 0 && mPlayer.mCurrentAudioIndex != index) { + return switchAudio(index); + } else if (type == STREAM_TYPE_VIDEO && mPlayer.mCurrentVideoIndex >= 0 && mPlayer.mCurrentVideoIndex != index) { + return switchVideoStream(index, type); + } +} + +void SMPMessageControllerListener::ProcessVideoRenderedMsg(int64_t pts, int64_t timeMs, bool rendered, void *picUserData) +{ + mPlayer.mUtil->render(pts); + if (rendered) { + mPlayer.checkFirstRender(); + } + + if (!mPlayer.mSeekFlag) { + mPlayer.mCurVideoPts = pts; + } + + //AF_LOGD("video stream render pts is %lld , mVideoChangedFirstPts = %lld ", pts, mVideoChangedFirstPts); + + if ((INT64_MIN != mPlayer.mVideoChangedFirstPts) && (pts >= mPlayer.mVideoChangedFirstPts)) { + AF_LOGD("video stream changed"); + StreamInfo *info = mPlayer.GetCurrentStreamInfo(ST_TYPE_VIDEO); + mPlayer.mPNotifier->NotifyStreamChanged(info, ST_TYPE_VIDEO); + mPlayer.mVideoChangedFirstPts = INT64_MIN; + } + + mPlayer.mDemuxerService->SetOption("FRAME_RENDERED", pts); + + if (mPlayer.mSet->bEnableVRC) { + mPlayer.mPNotifier->NotifyVideoRendered(timeMs, pts); + } + + //TODO packetGotTime +} + +void SMPMessageControllerListener::ProcessVideoCleanFrameMsg() +{ + while (!mPlayer.mVideoFrameQue.empty()) { + int64_t pts = mPlayer.mVideoFrameQue.front()->getInfo().pts; + ProcessVideoRenderedMsg(pts, af_getsteady_ms(), false, nullptr); + mPlayer.mVideoFrameQue.front()->setDiscard(true); + mPlayer.mVideoFrameQue.pop(); + } + + mPlayer.mAVDeviceManager->flushVideoRender(); + + mPlayer.mPlayedVideoPts = INT64_MIN; + mPlayer.mCurVideoPts = INT64_MIN; + mPlayer.videoDecoderFull = false; + mPlayer.mVideoPtsRevert = false; + mPlayer.dropLateVideoFrames = true; +} + +void SMPMessageControllerListener::ProcessVideoHoldMsg(bool hold) +{ + if (mPlayer.mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)) { + mPlayer.mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)->holdOn(hold); + + if (!hold) { + int size = mPlayer.mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)->getRecoverQueueSize(); + + if (size > mPlayer.mSet->maxVideoRecoverSize) { + string des = "video decoder recover size too large:" + AfString::to_string(size); + mPlayer.mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_DECODER_RECOVER_SIZE, des.c_str()); + } + } + } +} + +void SMPMessageControllerListener::ProcessSetSpeed(float speed) +{ + if (!CicadaUtils::isEqual(mPlayer.mSet->rate, speed)) { + mPlayer.mAVDeviceManager->setSpeed(speed); + mPlayer.mSet->rate = speed; + mPlayer.mMasterClock.SetScale(speed); + } +} + +void SMPMessageControllerListener::ProcessAddExtSubtitleMsg(const std::string &url) +{ + lock_guard uMutex(mPlayer.mCreateMutex); + + if (mPlayer.mSubPlayer == nullptr) { + mPlayer.mSubListener = unique_ptr(new mediaPlayerSubTitleListener(*(mPlayer.mPNotifier))); + mPlayer.mSubPlayer = unique_ptr(new subTitlePlayer(*(mPlayer.mSubListener))); + } + + mPlayer.mSubPlayer->add(url); +} + +void SMPMessageControllerListener::ProcessSelectExtSubtitleMsg(int index, bool select) +{ + lock_guard uMutex(mPlayer.mCreateMutex); + + if (mPlayer.mSubPlayer == nullptr) { + AF_LOGE("select ext subtitle error\n"); + mPlayer.mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SUBTITLE_SELECT_ERROR, "No such subtitle stream"); + return; + } + + int ret = mPlayer.mSubPlayer->select(index, select); + + if (ret < 0) { + AF_LOGE("select ext subtitle error\n"); + mPlayer.mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SUBTITLE_SELECT_ERROR, "No such subtitle stream"); + } + + if (select) { + mPlayer.mSubPlayer->seek(mPlayer.getCurrentPosition()); + } +} + + +void SMPMessageControllerListener::switchVideoStream(int index, Stream_type type) +{ + int count = (int) mPlayer.mStreamInfoQueue.size(); + StreamInfo *currentInfo = nullptr; + StreamInfo *willChangeInfo = nullptr; + int i; + int currentId = mPlayer.mCurrentVideoIndex; + + if (type == STREAM_TYPE_MIXED) { + currentId = GEN_STREAM_INDEX(mPlayer.mCurrentVideoIndex); + } + + for (i = 0; i < count; i++) { + StreamInfo *info = mPlayer.mStreamInfoQueue[i]; + + if (info->streamIndex == index) { + willChangeInfo = info; + } + + if (currentId == info->streamIndex) { + currentInfo = info; + } + } + + if (!willChangeInfo || !currentInfo) { + return; + } + + AF_LOGD("video change video bitrate before is %d,after is %d", currentInfo->videoBandwidth, willChangeInfo->videoBandwidth); + //TODO: different strategy + mPlayer.mWillChangedVideoStreamIndex = index; + mPlayer.mVideoChangedFirstPts = INT64_MAX; + + if (willChangeInfo->videoBandwidth < currentInfo->videoBandwidth) { + mPlayer.mDemuxerService->SwitchStreamAligned(currentId, index); + } else { + mPlayer.mMixMode = (type == STREAM_TYPE_MIXED); + int videoCount = 0; + int64_t startTime = mPlayer.mBufferController->FindSeamlessPointTimePosition(BUFFER_TYPE_VIDEO, videoCount); + + if (startTime == 0 || videoCount < 40) { + mPlayer.mWillSwitchVideo = true; + return; + } + + if (mPlayer.mMixMode) { + int64_t startTimeA = mPlayer.mBufferController->FindSeamlessPointTimePosition(BUFFER_TYPE_AUDIO, videoCount); + + if (startTimeA == 0 || videoCount < 40) { + mPlayer.mWillSwitchVideo = true; + return; + } + + startTime = std::max(startTime, startTimeA); + } + mPlayer.SwitchVideo(startTime); + } +} + +void SMPMessageControllerListener::switchAudio(int index) +{ + // TODO: use position to seek demuxer ,and drop the late packet + int ret = mPlayer.mDemuxerService->OpenStream(index); + + if (ret < 0) { + AF_LOGD("subtitle", "switch audio open stream failed,stream index %d\n", index); + return; + } + std::unique_ptr meta; + mPlayer.mDemuxerService->GetStreamMeta(meta, index, true); + + mPlayer.mDemuxerService->CloseStream(mPlayer.mCurrentAudioIndex); + mPlayer.mAudioChangedFirstPts = INT64_MAX; + mPlayer.mCurrentAudioIndex = index; + mPlayer.mCATimeBase = ((Stream_meta *) (*meta))->ptsTimeBase; + int64_t playTime = mPlayer.mMasterClock.GetTime(); + int64_t pts = playTime - mPlayer.mFirstAudioPts; + mPlayer.mMasterClock.setReferenceClock(nullptr, nullptr); + mPlayer.mBufferController->ClearPacket(BUFFER_TYPE_AUDIO); + mPlayer.mEof = false; + mPlayer.FlushAudioPath(); + mPlayer.mDemuxerService->Seek(pts, 0, index); + mPlayer.mPlayedAudioPts = INT64_MIN; +} + +void SMPMessageControllerListener::switchSubTitle(int index) +{ + int ret = mPlayer.mDemuxerService->OpenStream(index); + + if (ret < 0) { + AF_LOGD("subtitle", "switch subtitle open stream failed,stream index %d\n", index); + return; + } + + mPlayer.mSubtitleChangedFirstPts = INT64_MAX; + mPlayer.mDemuxerService->CloseStream(mPlayer.mCurrentSubtitleIndex); + mPlayer.mCurrentSubtitleIndex = index; + mPlayer.mBufferController->ClearPacket(BUFFER_TYPE_SUBTITLE); + mPlayer.mEof = false; + mPlayer.mSubtitleEOS = false; + mPlayer.FlushSubtitleInfo(); + mPlayer.mDemuxerService->Seek(mPlayer.getCurrentPosition(), 0, index); +} + +int SMPMessageControllerListener::openUrl() +{ + IDataSource::SourceConfig config{}; + config.low_speed_time_ms = mPlayer.mSet->timeout_ms; + config.low_speed_limit = 1; + + switch (mPlayer.mSet->mIpType) { + case IpResolveWhatEver: + config.resolveType = IDataSource::SourceConfig::IpResolveWhatEver; + break; + case IpResolveV4: + config.resolveType = IDataSource::SourceConfig::IpResolveV4; + break; + case IpResolveV6: + config.resolveType = IDataSource::SourceConfig::IpResolveV6; + break; + } + // config.max_time_ms = mSet->timeout; + config.connect_time_out_ms = mPlayer.mSet->timeout_ms; + config.http_proxy = mPlayer.mSet->http_proxy; + config.refer = mPlayer.mSet->refer; + config.userAgent = mPlayer.mSet->userAgent; + config.customHeaders = mPlayer.mSet->customHeaders; + config.listener = mPlayer.mSourceListener.get(); + mPlayer.mSourceListener->enableRetry(); + + if (mPlayer.mCanceled) { + return FRAMEWORK_ERR_EXIT; + } + + { + std::lock_guard locker(mPlayer.mCreateMutex); + mPlayer.mDataSource = dataSourcePrototype::create(mPlayer.mSet->url, &(mPlayer.mSet->mOptions)); + } + + if (mPlayer.mDataSource) { + mPlayer.mDataSource->Set_config(config); + int ret = mPlayer.mDataSource->Open(0); + return ret; + } + + return -1; +} \ No newline at end of file diff --git a/mediaPlayer/SMPMessageControllerListener.h b/mediaPlayer/SMPMessageControllerListener.h new file mode 100644 index 000000000..15c56448f --- /dev/null +++ b/mediaPlayer/SMPMessageControllerListener.h @@ -0,0 +1,67 @@ +// +// Created by pingkai on 2020/12/28. +// + +#ifndef CICADAMEDIA_SMPMESSAGECONTROLLERLISTENER_H +#define CICADAMEDIA_SMPMESSAGECONTROLLERLISTENER_H + +#include "player_msg_control.h" +#include +namespace Cicada { + class SuperMediaPlayer; + class SMPMessageControllerListener : public PlayerMessageControllerListener { + public: + explicit SMPMessageControllerListener(SuperMediaPlayer &player); + ~SMPMessageControllerListener(); + + public: + void ProcessSetViewMsg(void *view) final; + void ProcessSetSpeed(float speed) final; + void ProcessVideoRenderedMsg(int64_t pts, int64_t timeMs, bool rendered, void *picUserData) final; + void ProcessSeekToMsg(int64_t seekPos, bool bAccurate) final; + void ProcessMuteMsg() final; + void ProcessVideoHoldMsg(bool hold) final; + + private: + bool OnPlayerMsgIsPadding(PlayMsgType msg, MsgParam msgContent) final; + + void ProcessPrepareMsg() final; + + void ProcessStartMsg() final; + + void ProcessSetDisplayMode() final; + + void ProcessSetRotationMode() final; + + void ProcessSetMirrorMode() final; + + void ProcessSetVideoBackgroundColor() final; + + void ProcessSetDataSourceMsg(const std::string &url) final; + + void ProcessSetBitStreamMsg(readCB read, seekCB seekCb, void *arg) final; + + void ProcessPauseMsg() final; + + void ProcessSwitchStreamMsg(int index) final; + + void ProcessVideoCleanFrameMsg() final; + + void ProcessAddExtSubtitleMsg(const std::string &url) final; + + void ProcessSelectExtSubtitleMsg(int index, bool select) final; + + + private: + void switchVideoStream(int index, Stream_type type); + void switchAudio(int index); + void switchSubTitle(int index); + int openUrl(); + + private: + SuperMediaPlayer &mPlayer; + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_SMPMESSAGECONTROLLERLISTENER_H diff --git a/mediaPlayer/SMPRecorderSet.cpp b/mediaPlayer/SMPRecorderSet.cpp new file mode 100644 index 000000000..7fd076d8b --- /dev/null +++ b/mediaPlayer/SMPRecorderSet.cpp @@ -0,0 +1,5 @@ +// +// Created by SuperMan on 12/4/20. +// + +#include "SMPRecorderSet.h" diff --git a/mediaPlayer/SMPRecorderSet.h b/mediaPlayer/SMPRecorderSet.h new file mode 100644 index 000000000..9320457ae --- /dev/null +++ b/mediaPlayer/SMPRecorderSet.h @@ -0,0 +1,69 @@ +// +// Created by SuperMan on 12/4/20. +// + +#ifndef SOURCE_SMPRECORDERSET_H +#define SOURCE_SMPRECORDERSET_H + + +#include +#include + +class DecodeFirstFrameInfo { +public: + DecodeFirstFrameInfo() = default; + + ~DecodeFirstFrameInfo() = default; + + int64_t sendFirstPacketTimeMs{INT64_MIN}; + int64_t getFirstFrameTimeMs{INT64_MIN}; + bool isFirstPacketSendToDecoder{false}; + bool waitFirstFrame{false}; + int64_t firstPacketSize{0}; + int64_t firstPacketPts{INT64_MIN}; + +public: + int64_t getDecodeFirstFrameCost() { + if (getFirstFrameTimeMs != INT64_MIN && sendFirstPacketTimeMs != INT64_MIN) { + return getFirstFrameTimeMs - sendFirstPacketTimeMs; + } else { + return INT64_MIN; + } + } + + void reset() { + sendFirstPacketTimeMs = INT64_MIN; + getFirstFrameTimeMs = INT64_MIN; + isFirstPacketSendToDecoder = false; + waitFirstFrame = false; + firstPacketSize = 0; + firstPacketPts = INT64_MIN; + } +}; + +class SMPRecorderSet { + +public: + SMPRecorderSet() = default; + + ~SMPRecorderSet() = default; + + void reset() { + createAudioDecoderCostMs = INT64_MIN; + createVideoDecoderCostMs = INT64_MIN; + + decodeFirstAudioFrameInfo.reset(); + decodeFirstVideoFrameInfo.reset(); + } + +public: + std::atomic createAudioDecoderCostMs{INT64_MIN}; + std::atomic createVideoDecoderCostMs{INT64_MIN}; + + DecodeFirstFrameInfo decodeFirstAudioFrameInfo{}; + DecodeFirstFrameInfo decodeFirstVideoFrameInfo{}; + +}; + + +#endif //SOURCE_SMPRECORDERSET_H diff --git a/mediaPlayer/SuperMediaPlayer.cpp b/mediaPlayer/SuperMediaPlayer.cpp index ebf9d9d6e..965e5a4e2 100644 --- a/mediaPlayer/SuperMediaPlayer.cpp +++ b/mediaPlayer/SuperMediaPlayer.cpp @@ -1,2076 +1,2174 @@ #define LOG_TAG "ApsaraPlayerService" -#include -#include -#include #include "SuperMediaPlayer.h" +#include "media_player_error_def.h" +#include "media_player_error_map.h" #include "utils/CicadaJSON.h" #include "utils/CicadaUtils.h" +#include #include -#include -#include -#include "media_player_error_def.h" -#include -#include -#include "media_player_error_map.h" -#include -#include #include -#include #include -#include #include #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #ifdef __APPLE__ #include -#include +#include #include #endif -#define PTS_DISCONTINUE_DELTA (20*1000*1000) +#define PTS_DISCONTINUE_DELTA (20 * 1000 * 1000) #define VIDEO_PICTURE_MAX_CACHE_SIZE 2 -static int MAX_DECODE_ERROR_FRAME = 100; - -//#define IS_REAL_TIME_STREAM (mSet.url.substr(0,7) == "rtmp://" || mSet.url.substr(0,7) == "artp://") -#define IS_REAL_TIME_STREAM ((mDuration == 0) && !(mDemuxerService->isPlayList())) - +static int MAX_DECODE_ERROR_FRAME = 1000; #define PTS_REVERTING (mVideoPtsRevert != mAudioPtsRevert) -namespace Cicada { - - static MsgParam dummyMsg{{nullptr}}; - - const int64_t SuperMediaPlayer::SEEK_ACCURATE_MAX = 11 * 1000 * 1000; - - SuperMediaPlayer::SuperMediaPlayer() - : mMessageControl(*this), mAudioRenderCB(*this), mApsaraThread([this]() -> int { return this->mainService(); }, LOG_TAG), - mSourceListener(*this), mDcaManager(*this) - { - AF_LOGD("SuperMediaPlayer()"); - mPNotifier = new PlayerNotifier(); - Reset(); - mTimerInterval = 500; +using namespace Cicada; +SuperMediaPlayer SuperMediaPlayer::se(1); + +static MsgParam dummyMsg{{nullptr}}; + +const int64_t SuperMediaPlayer::SEEK_ACCURATE_MAX = 11 * 1000 * 1000; + +#define HAVE_VIDEO (mCurrentVideoIndex >= 0) +#define HAVE_AUDIO (mCurrentAudioIndex >= 0) +#define HAVE_SUBTITLE (mCurrentSubtitleIndex >= 0) + +SuperMediaPlayer::SuperMediaPlayer() +{ + AF_LOGD("SuperMediaPlayer()"); + + mSet = static_cast>(new player_type_set()); + mBufferController = static_cast>(new BufferController()); + mUtil = static_cast>(new MediaPlayerUtil()); + mMsgCtrlListener = static_cast>(new SMPMessageControllerListener(*this)); + mMessageControl = static_cast>(new PlayerMessageControl(*mMsgCtrlListener)); + mAudioRenderCB = static_cast>(new ApsaraAudioRenderCallback(*this)); + mVideoRenderListener = static_cast>(new ApsaraVideoRenderListener(*this)); + mApsaraThread = static_cast>(new afThread([this]() -> int { return this->mainService(); }, LOG_TAG)); + mSourceListener = static_cast>(new SuperMediaPlayerDataSourceListener(*this)); + mDcaManager = static_cast>(new SMP_DCAManager(*this)); + mDrmManager = static_cast>(new DrmManager()); + mAVDeviceManager = static_cast>(new SMPAVDeviceManager()); + mAVDeviceManager->setRequireDrmHandlerCallback([this](const DrmInfo& info) -> DrmHandler* { + return mDrmManager->require(info); + }); + mRecorderSet = static_cast>(new SMPRecorderSet()); + + mPNotifier = new PlayerNotifier(); + Reset(); + mTimerInterval = 500; +} + +SuperMediaPlayer::~SuperMediaPlayer() +{ + if (mIsDummy) { + return; } - - SuperMediaPlayer::~SuperMediaPlayer() - { - Stop(); - AF_LOGD("SuperMediaPlayer"); - mCanceled = true; + Stop(); + AF_LOGD("SuperMediaPlayer"); + mCanceled = true; + mPlayerCondition.notify_one(); + mApsaraThread->stop(); + mSubPlayer = nullptr; + mSubListener = nullptr; + // delete mPNotifier after mPMainThread, to avoid be using + delete mPNotifier; + mPNotifier = nullptr; + mMessageControl = nullptr; +} + +void SuperMediaPlayer::putMsg(PlayMsgType type, const MsgParam ¶m, bool trigger) +{ + mMessageControl->putMsg(type, param); + + if (trigger) { mPlayerCondition.notify_one(); - mApsaraThread.stop(); - mVideoRender = nullptr; - mSubPlayer = nullptr; - mSubListener = nullptr; - // delete mPNotifier after mPMainThread, to avoid be using - delete mPNotifier; - mPNotifier = nullptr; - } - - void SuperMediaPlayer::putMsg(PlayMsgType type, const MsgParam ¶m, bool trigger) - { - mMessageControl.putMsg(type, param); - - if (trigger) { - mPlayerCondition.notify_one(); - } - } - - void SuperMediaPlayer::SetView(void *view) - { - ProcessSetViewMsg(view); } - - int64_t SuperMediaPlayer::GetMasterClockPts() - { - return mMasterClock.GetTime(); - } - - void SuperMediaPlayer::SetDataSource(const char *url) - { - MsgParam param; - MsgDataSourceParam dataSourceParam = {nullptr}; - dataSourceParam.url = new string(url ? url : ""); - param.dataSourceParam = dataSourceParam; - putMsg(MSG_SETDATASOURCE, param); +} + +void SuperMediaPlayer::SetView(void *view) +{ + mMsgCtrlListener->ProcessSetViewMsg(view); + + std::lock_guard lockGuard(mViewUpdateMutex); + if (mInited) { + mViewUpdateStatus = ViewUpdateStatus::No; + } +} + +int64_t SuperMediaPlayer::GetMasterClockPts() +{ + return mMasterClock.GetTime(); +} + +void SuperMediaPlayer::setBitStreamCb(readCB read, seekCB seek, void *arg) +{ + MsgParam param; + MsgBitStreamParam msgBitStreamParam = {nullptr}; + msgBitStreamParam.read = read; + msgBitStreamParam.seek = seek; + msgBitStreamParam.arg = arg; + param.msgBitStreamParam = msgBitStreamParam; + putMsg(MSG_SET_BITSTREAM, param); +} + +void SuperMediaPlayer::SetDataSource(const char *url) +{ + MsgParam param; + MsgDataSourceParam dataSourceParam = {nullptr}; + dataSourceParam.url = new string(url ? url : ""); + param.dataSourceParam = dataSourceParam; + putMsg(MSG_SETDATASOURCE, param); +} + +void SuperMediaPlayer::Prepare() +{ + if (mPlayStatus != PLAYER_INITIALZED && mPlayStatus != PLAYER_STOPPED) { + Stop(); } - void SuperMediaPlayer::Prepare() - { - if (mPlayStatus != PLAYER_INITIALZED && mPlayStatus != PLAYER_STOPPED) { - Stop(); - } - #if TARGET_OS_IPHONE - AFAudioSessionWrapper::activeAudio(); + AFAudioSessionWrapper::activeAudio(); #endif - mPrepareStartTime = af_gettime_relative(); - std::unique_lock uMutex(mPlayerMutex); - putMsg(MSG_PREPARE, dummyMsg); - mApsaraThread.start(); - } - - void SuperMediaPlayer::CaptureScreen() - { - std::lock_guard uMutex(mCreateMutex); - - if (mVideoRender && mCurrentVideoIndex >= 0) { - mVideoRender->captureScreen([this](uint8_t *data, int width, int height) { - if (this->mPNotifier) { - this->mPNotifier->NotifyCaptureScreen(data, width, height); - } - }); - } else { + mPrepareStartTime = af_gettime_relative(); + std::unique_lock uMutex(mPlayerMutex); + putMsg(MSG_PREPARE, dummyMsg); + mApsaraThread->start(); +} + +void SuperMediaPlayer::CaptureScreen() +{ + std::lock_guard uMutex(mCreateMutex); + + if (mAVDeviceManager->isVideoRenderValid()) { + mAVDeviceManager->getVideoRender()->captureScreen([this](uint8_t *data, int width, int height) { if (this->mPNotifier) { - this->mPNotifier->NotifyCaptureScreen(nullptr, 0, 0); + this->mPNotifier->NotifyCaptureScreen(data, width, height); } + }); + } else { + if (this->mPNotifier) { + this->mPNotifier->NotifyCaptureScreen(nullptr, 0, 0); } } +} - void SuperMediaPlayer::SetVolume(float volume) - { - //TODO:put message to - mSet.mVolume = volume; - - if (mSet.mVolume < 0) { - mSet.mVolume = 0; - } else if (mSet.mVolume > 1.0) { - AF_LOGW("volume >1.0"); - } +void SuperMediaPlayer::SetVolume(float volume) +{ + //TODO:put message to + mSet->mVolume = volume; - if (mAudioRender != nullptr) { - mAudioRender->setVolume(mSet.mVolume); - } + if (mSet->mVolume < 0) { + mSet->mVolume = 0; + } else if (mSet->mVolume > 1.0) { + AF_LOGW("volume >1.0"); } - void SuperMediaPlayer::Start() - { - if ((PLAYER_INITIALZED == mPlayStatus) || (PLAYER_PREPARING == mPlayStatus) || PLAYER_PREPARINIT == mPlayStatus) { - waitingForStart = true; - } + mAVDeviceManager->setVolume(mSet->mVolume); +} - this->putMsg(MSG_START, dummyMsg); +void SuperMediaPlayer::Start() +{ + if ((PLAYER_INITIALZED == mPlayStatus) || (PLAYER_PREPARING == mPlayStatus) || PLAYER_PREPARINIT == mPlayStatus) { + waitingForStart = true; } + this->putMsg(MSG_START, dummyMsg); + mPausedByAudioInterrupted = false; +} - void SuperMediaPlayer::Pause() - { - waitingForStart = false; - this->putMsg(MSG_PAUSE, dummyMsg); - } +void SuperMediaPlayer::Pause() +{ + waitingForStart = false; + this->putMsg(MSG_PAUSE, dummyMsg); + mPausedByAudioInterrupted = false; +} - void SuperMediaPlayer::SeekTo(int64_t pos, bool bAccurate) - { - MsgParam param; - MsgSeekParam seekParam; - seekParam.seekPos = (int64_t) pos * 1000; - seekParam.bAccurate = bAccurate; - param.seekParam = seekParam; - this->putMsg(MSG_SEEKTO, param); - mSeekPos = pos * 1000; - mSeekNeedCatch = bAccurate; - } - bool SuperMediaPlayer::OnPlayerMsgIsPadding(PlayMsgType msg, MsgParam msgContent) - { - bool padding = false; +void SuperMediaPlayer::SeekTo(int64_t pos, bool bAccurate) +{ + MsgParam param; + MsgSeekParam seekParam; + seekParam.seekPos = (int64_t) pos * 1000; + seekParam.bAccurate = bAccurate; + param.seekParam = seekParam; + this->putMsg(MSG_SEEKTO, param); + mSeekPos = pos * 1000; + mSeekNeedCatch = bAccurate; +} +void SuperMediaPlayer::Mute(bool bMute) +{ + if (bMute == mSet->bMute) { + return; + } - switch (msg) { - case MSG_CHANGE_VIDEO_STREAM: - padding = mVideoChangedFirstPts != INT64_MIN; - break; + mSet->bMute = bMute; + this->putMsg(MSG_MUTE, dummyMsg); +} - case MSG_CHANGE_AUDIO_STREAM: - padding = mAudioChangedFirstPts != INT64_MIN; - break; +void SuperMediaPlayer::EnterBackGround(bool back) +{ + // lock mAppStatusMutex before mCreateMutex + std::lock_guard lock(mAppStatusMutex); + MsgParam param; + MsgHoldOnVideoParam holdParam; - case MSG_CHANGE_SUBTITLE_STREAM: - padding = mSubtitleChangedFirstPts != INT64_MIN; - break; + if (back) { + AF_LOGI("EnterBackGround"); + mAppStatus = APP_BACKGROUND; + holdParam.hold = true; + param.msgHoldOnVideoParam = holdParam; + putMsg(MSG_INTERNAL_VIDEO_HOLD_ON, param); - case MSG_SEEKTO: - if (mSeekFlag) { - padding = true; - } + if (mPlayStatus == PLAYER_PLAYING) { + putMsg(MSG_INTERNAL_VIDEO_CLEAN_FRAME, dummyMsg); + } + } else { + AF_LOGI("EnterBackGround APP_FOREGROUND"); + mAppStatus = APP_FOREGROUND; + holdParam.hold = false; + param.msgHoldOnVideoParam = holdParam; + putMsg(MSG_INTERNAL_VIDEO_HOLD_ON, param); + } +} + +StreamType SuperMediaPlayer::SwitchStream(int streamIndex) +{ + MsgParam param; + MsgChangeStreamParam streamParam; + streamParam.index = streamIndex; + param.streamParam = streamParam; + StreamType streamType = ST_TYPE_UNKNOWN; + PlayMsgType type = MSG_INVALID; + + for (auto &it : mStreamInfoQueue) { + if (it->streamIndex == streamIndex) { + switch (it->type) { + case ST_TYPE_VIDEO: + streamType = ST_TYPE_VIDEO; + type = MSG_CHANGE_VIDEO_STREAM; + break; - break; + case ST_TYPE_AUDIO: + streamType = ST_TYPE_AUDIO; + type = MSG_CHANGE_AUDIO_STREAM; + break; - default: - padding = false; - } + case ST_TYPE_SUB: + streamType = ST_TYPE_SUB; + type = MSG_CHANGE_SUBTITLE_STREAM; + break; - return padding; - } + default: + AF_LOGE("unknown stream Type"); + return streamType; + } - void SuperMediaPlayer::Mute(bool bMute) - { - if (bMute == mSet.bMute) { - return; + break; } - - mSet.bMute = bMute; - this->putMsg(MSG_MUTE, dummyMsg); } - void SuperMediaPlayer::EnterBackGround(bool back) - { - // lock mAppStatusMutex before mCreateMutex - std::lock_guard lock(mAppStatusMutex); - MsgParam param; - MsgHoldOnVideoParam holdParam; - - if (back) { - AF_LOGI("EnterBackGround"); - mAppStatus = APP_BACKGROUND; - holdParam.hold = true; - param.msgHoldOnVideoParam = holdParam; - putMsg(MSG_INTERNAL_VIDEO_HOLD_ON, param); - - if (mPlayStatus == PLAYER_PLAYING) { - putMsg(MSG_INTERNAL_VIDEO_CLEAN_FRAME, dummyMsg); - } - } else { - AF_LOGI("EnterBackGround APP_FOREGROUND"); - mAppStatus = APP_FOREGROUND; - holdParam.hold = false; - param.msgHoldOnVideoParam = holdParam; - putMsg(MSG_INTERNAL_VIDEO_HOLD_ON, param); - } + if (type != MSG_INVALID) { + this->putMsg(type, param); } - StreamType SuperMediaPlayer::SwitchStream(int streamIndex) - { - MsgParam param; - MsgChangeStreamParam streamParam; - streamParam.index = streamIndex; - param.streamParam = streamParam; - StreamType streamType = ST_TYPE_UNKNOWN; - PlayMsgType type = MSG_INVALID; - - for (auto &it : mStreamInfoQueue) { - if (it->streamIndex == streamIndex) { - switch (it->type) { - case ST_TYPE_VIDEO: - streamType = ST_TYPE_VIDEO; - type = MSG_CHANGE_VIDEO_STREAM; - break; - - case ST_TYPE_AUDIO: - streamType = ST_TYPE_AUDIO; - type = MSG_CHANGE_AUDIO_STREAM; - break; - - case ST_TYPE_SUB: - streamType = ST_TYPE_SUB; - type = MSG_CHANGE_SUBTITLE_STREAM; - break; - - default: - AF_LOGE("unknown stream Type"); - return streamType; - } - - break; - } - } + return streamType; +} - if (type != MSG_INVALID) { - this->putMsg(type, param); - } +void SuperMediaPlayer::Interrupt(bool inter) +{ + AF_TRACE; + std::lock_guard locker(mCreateMutex); - return streamType; + if (mDataSource) { + mDataSource->Interrupt(inter); + } else { + AF_TRACE; } - void SuperMediaPlayer::Interrupt(bool inter) - { + if (mDemuxerService) { + mDemuxerService->interrupt(inter); + mDemuxerService->preStop(); + } else { AF_TRACE; - std::lock_guard locker(mCreateMutex); - - if (mDataSource) { - mDataSource->Interrupt(inter); - } else { - AF_TRACE; - } - - if (mDemuxerService) { - mDemuxerService->interrupt(inter); - mDemuxerService->preStop(); - } else { - AF_TRACE; - } } +} - int SuperMediaPlayer::Stop() - { - if ((afThread::THREAD_STATUS_RUNNING != mApsaraThread.getStatus()) - && ((mPlayStatus == PLAYER_IDLE) || (mPlayStatus == PLAYER_STOPPED))) { - return 0; - } +int SuperMediaPlayer::Stop() +{ + if ((afThread::THREAD_STATUS_RUNNING != mApsaraThread->getStatus()) && + ((mPlayStatus == PLAYER_IDLE) || (mPlayStatus == PLAYER_STOPPED))) { + return 0; + } - std::unique_lock uMutex(mPlayerMutex); - AF_LOGI("Player ReadPacket Stop"); - int64_t t1 = af_gettime_ms(); - AF_TRACE; - waitingForStart = false; - mCanceled = true; - mPNotifier->Clean(); - mPNotifier->Enable(false); - Interrupt(true); - mPlayerCondition.notify_one(); - mApsaraThread.pause(); - mPlayStatus = PLAYER_STOPPED; - // ChangePlayerStatus(PLAYER_STOPPED); - mBufferController.ClearPacket(BUFFER_TYPE_AV); - AF_TRACE; + std::unique_lock uMutex(mPlayerMutex); + AF_LOGI("Player ReadPacket Stop"); + int64_t t1 = af_gettime_ms(); + AF_TRACE; + waitingForStart = false; + mCanceled = true; + mPNotifier->Clean(); + mPNotifier->Enable(false); + Interrupt(true); + mPlayerCondition.notify_one(); + mApsaraThread->pause(); + mAVDeviceManager->invalidDevices(SMPAVDeviceManager::DEVICE_TYPE_AUDIO | SMPAVDeviceManager::DEVICE_TYPE_VIDEO); + mPlayStatus = PLAYER_STOPPED; + // ChangePlayerStatus(PLAYER_STOPPED); + mBufferController->ClearPacket(BUFFER_TYPE_AV); + + AF_TRACE; + FlushAudioPath(); + + AF_TRACE; + mBRendingStart = false; + AF_TRACE; + FlushVideoPath(); + // clear the message queue after flash video render + mMessageControl->clear(); + AF_TRACE; + + if (mDemuxerService) { + mDemuxerService->interrupt(1); - if (mVideoDecoder) { - mVideoDecoder->preClose(); + if (mDataSource) { + mDataSource->Interrupt(true); } - AF_TRACE; + std::lock_guard uMutex(mCreateMutex); + mDemuxerService->stop(); + mDemuxerService->close(); - if (mAudioDecoder != nullptr) { - //because FlushAudioPath call mAudioOutHandle flush, so stop should called first - if (mAudioRender) { - mAudioRender->pause(true); + if (mMixMode) { + if (mMainStreamId != -1) { + mDemuxerService->CloseStream(mMainStreamId); } - FlushAudioPath(); - mAudioDecoder->preClose(); - } - - AF_TRACE; - mAudioRender = nullptr; - mBRendingStart = false; - AF_TRACE; - { - std::lock_guard uMutex(mCreateMutex); - - if (mVideoDecoder) { - FlushVideoPath(); - mVideoDecoder->close(); - mVideoDecoder = nullptr; + if (mCurrentSubtitleIndex >= 0) { + mDemuxerService->CloseStream(mCurrentSubtitleIndex); } - - if (mAudioDecoder) { - FlushAudioPath(); - mAudioDecoder->close(); - mAudioDecoder = nullptr; + } else { + if (mCurrentAudioIndex >= 0) { + mDemuxerService->CloseStream(mCurrentAudioIndex); } - } - // clear the message queue after flash video render - mMessageControl.clear(); - AF_TRACE; - if (mDemuxerService) { - mDemuxerService->interrupt(1); - - if (mDataSource) { - mDataSource->Interrupt(true); + if (mCurrentVideoIndex >= 0) { + mDemuxerService->CloseStream(mCurrentVideoIndex); } - std::lock_guard uMutex(mCreateMutex); - mDemuxerService->stop(); - mDemuxerService->close(); - - if (mMixMode) { - if (mMainStreamId != -1) { - mDemuxerService->CloseStream(mMainStreamId); - } - - if (mCurrentSubtitleIndex >= 0) { - mDemuxerService->CloseStream(mCurrentSubtitleIndex); - } - } else { - if (mCurrentAudioIndex >= 0) { - mDemuxerService->CloseStream(mCurrentAudioIndex); - } - - if (mCurrentVideoIndex >= 0) { - mDemuxerService->CloseStream(mCurrentVideoIndex); - } - - if (mCurrentSubtitleIndex >= 0) { - mDemuxerService->CloseStream(mCurrentSubtitleIndex); - } + if (mCurrentSubtitleIndex >= 0) { + mDemuxerService->CloseStream(mCurrentSubtitleIndex); } - - delete mDemuxerService; - mDemuxerService = nullptr; - } - - if (mDataSource) { - mDataSource->Close(); - std::lock_guard uMutex(mCreateMutex); - delete mDataSource; - mDataSource = nullptr; } - if (mVideoRender) { - // lock mAppStatusMutex before mCreateMutex - std::lock_guard lock(mAppStatusMutex); + delete mDemuxerService; + mDemuxerService = nullptr; + } - // for iOS, don't delete render in background, and we should reuse it later. - if (APP_BACKGROUND != mAppStatus) { - std::lock_guard uMutex(mCreateMutex); + if (mDataSource) { + mDataSource->Close(); + std::lock_guard uMutex(mCreateMutex); + delete mDataSource; + mDataSource = nullptr; + } - if (mSet.clearShowWhenStop) { - mVideoRender->clearScreen(); - } - } - } + if (mAVDeviceManager->getVideoRender()) { + // lock mAppStatusMutex before mCreateMutex + std::lock_guard lock(mAppStatusMutex); - delete[] mStreamInfos; - mStreamInfos = nullptr; - delete mVideoParser; - mVideoParser = nullptr; - mSubPlayer = nullptr; - { + // for iOS, don't delete render in background, and we should reuse it later. + if (APP_BACKGROUND != mAppStatus) { std::lock_guard uMutex(mCreateMutex); - for (StreamInfo *info : mStreamInfoQueue) { - releaseStreamInfo(info); + if (mSet->clearShowWhenStop) { + mAVDeviceManager->getVideoRender()->clearScreen(); } - - mStreamInfoQueue.clear(); } - mBufferController.ClearPacket(BUFFER_TYPE_SUBTITLE); - Reset(); - AF_LOGD("stop spend time is %lld", af_gettime_ms() - t1); - return 0; } - void SuperMediaPlayer::releaseStreamInfo(const StreamInfo *info) const + delete[] mStreamInfos; + mStreamInfos = nullptr; + delete mVideoParser; + mVideoParser = nullptr; { - if (info->subtitleLang) { - free(info->subtitleLang); - } - - if (info->audioLang) { - free(info->audioLang); - } + std::lock_guard uMutex(mCreateMutex); - if (info->description) { - free(info->description); + for (StreamInfo *info : mStreamInfoQueue) { + releaseStreamInfo(info); } - delete info; + mStreamInfoQueue.clear(); } + mBufferController->ClearPacket(BUFFER_TYPE_SUBTITLE); + Reset(); - void SuperMediaPlayer::SetRefer(const char *referer) - { - if (referer) { - mSet.refer = referer; - } - } + mRecorderSet->reset(); + mDrmManager->clearErrorItems(); - void SuperMediaPlayer::SetUserAgent(const char *userAgent) - { - if (userAgent) { - mSet.userAgent = userAgent; - } - } + AF_LOGD("stop spend time is %lld", af_gettime_ms() - t1); + return 0; +} - void SuperMediaPlayer::SetTimeout(int timeout) - { - mSet.timeout_ms = timeout; +void SuperMediaPlayer::releaseStreamInfo(const StreamInfo *info) const +{ + if (info->subtitleLang) { + free(info->subtitleLang); } - void SuperMediaPlayer::SetDropBufferThreshold(int dropValue) - { - mSet.RTMaxDelayTime = dropValue * 1000; + if (info->audioLang) { + free(info->audioLang); } - void SuperMediaPlayer::SetLooping(bool looping) - { - mSet.bLooping = looping; + if (info->description) { + free(info->description); } - bool SuperMediaPlayer::isLooping() - { - return mSet.bLooping; + delete info; +} + +void SuperMediaPlayer::SetRefer(const char *referer) +{ + if (referer) { + mSet->refer = referer; } +} - int SuperMediaPlayer::SetOption(const char *key, const char *value) - { - if (key == nullptr) { - return -1; - } +void SuperMediaPlayer::SetUserAgent(const char *userAgent) +{ + if (userAgent) { + mSet->userAgent = userAgent; + } +} - int duration; - string theKey = key; +void SuperMediaPlayer::SetTimeout(int timeout) +{ + mSet->timeout_ms = timeout; +} - if (theKey == "startBufferDuration") { - duration = atoi(value); +void SuperMediaPlayer::SetDropBufferThreshold(int dropValue) +{ + mSet->RTMaxDelayTime = dropValue * 1000; +} - if (duration > 0) { - mSet.startBufferDuration = duration * 1000; - } - } else if (theKey == "RTMaxDelayTime") { - duration = atoi(value); +void SuperMediaPlayer::SetLooping(bool looping) +{ + mSet->bLooping = looping; +} - if (duration > 0) { - mSet.RTMaxDelayTime = duration * 1000; - } - } else if (theKey == "highLevelBufferDuration") { - duration = atoi(value); +bool SuperMediaPlayer::isLooping() +{ + return mSet->bLooping; +} - if (duration > 0) { - mSet.highLevelBufferDuration = duration * 1000; - } - } else if (theKey == "http_proxy") { - mSet.http_proxy = value; - } else if (theKey == "maxBufferDuration") { - duration = atoi(value); +int SuperMediaPlayer::SetOption(const char *key, const char *value) +{ + if (key == nullptr) { + return -1; + } - if (duration > 0) { - mSet.maxBufferDuration = int64_t(duration) * 1000; - } - } else if (theKey == "DisableBufferManager") { - mSet.bDisableBufferManager = (bool) atoi(value); - } else if (theKey == "LowLatency") { - mSet.bLowLatency = (bool) atoi(value); - } else if (theKey == "ClearShowWhenStop") { - int clearShowWhenStop = atoi(value); - mSet.clearShowWhenStop = (bool) clearShowWhenStop; - } else if (theKey == "enableVideoTunnelRender") { - mSet.bEnableTunnelRender = (atoi(value) != 0); - } else if (theKey == "Analytics.ReportID") { - if (nullptr == value) { - return -1; - } + int duration; + string theKey = key; - int64_t eventReportID = atoll(value); - mSet.AnalyticsID = eventReportID; - } else if (theKey == "bandWidth") { - mSet.mDefaultBandWidth = atoi(value); - } else if (theKey == "description") { - mSet.mOptions.set(theKey, value, options::REPLACE); - return 0; - } else if (theKey == "enableVRC") { - mSet.bEnableVRC = (atoi(value) != 0); - } else if (theKey == "maxAccurateSeekDelta") { - mSet.maxASeekDelta = atoi(value) * 1000; - } else if (theKey == "maxVideoRecoverSize") { - mSet.maxVideoRecoverSize = atoi(value); - } else if ( theKey == "surfaceChanged") { - std::lock_guard uMutex(mCreateMutex); + if (theKey == "startBufferDuration") { + duration = atoi(value); - if (mVideoRender != nullptr) { - mVideoRender->surfaceChanged(); - } - } else if (theKey == "streamTypes") { - uint64_t flags = atoll(value); - mSet.bDisableAudio = mSet.bDisableVideo = true; - if (flags & VIDEO_FLAG) { - mSet.bDisableVideo = false; - } - if (flags & AUDIO_FLAG) { - mSet.bDisableAudio = false; - } - } else if (theKey == "IPResolveType") { - uint64_t type = atoll(value); - mSet.mIpType = static_cast(type); - } else if (theKey == "fastStart") { - mSet.mFastStart = atol(value) != 0; - } else if (theKey == "pixelBufferOutputFormat") { - mSet.pixelBufferOutputFormat = atol(value); - } else if (theKey == "liveStartIndex") { - mSet.mOptions.set(theKey, value, options::REPLACE); + if (duration > 0) { + mSet->startBufferDuration = duration * 1000; } + } else if (theKey == "RTMaxDelayTime") { + duration = atoi(value); - return 0; - } + if (duration > 0) { + mSet->RTMaxDelayTime = duration * 1000; + } + } else if (theKey == "highLevelBufferDuration") { + duration = atoi(value); - void SuperMediaPlayer::GetOption(const char *key, char *value) - { - if (key == nullptr) { - return; + if (duration > 0) { + mSet->highLevelBufferDuration = duration * 1000; } + } else if (theKey == "http_proxy") { + mSet->http_proxy = value; + } else if (theKey == "maxBufferDuration") { + duration = atoi(value); - string theKey = key; + if (duration > 0) { + mSet->maxBufferDuration = int64_t(duration) * 1000; + } + } else if (theKey == "LowLatency") { + mSet->bLowLatency = (bool) atoi(value); + } else if (theKey == "ClearShowWhenStop") { + int clearShowWhenStop = atoi(value); + mSet->clearShowWhenStop = (bool) clearShowWhenStop; + } else if (theKey == "enableVideoTunnelRender") { + mSet->bEnableTunnelRender = (atoi(value) != 0); + } else if (theKey == "disableAudio") { + mSet->bDisableAudio = (atoi(value) != 0); + } else if (theKey == "disableVideo") { + mSet->bDisableVideo = (atoi(value) != 0); + } else if (theKey == "timerInterval") { + mTimerInterval = atoi(value); + } else if (theKey == "Analytics.ReportID") { + if (nullptr == value) { + return -1; + } - if (theKey == "maxBufferDuration") { - snprintf(value, MAX_OPT_VALUE_LENGTH, "%" PRId64 "", mSet.maxBufferDuration); - } else if (theKey == "mediaStreamSize") { - int64_t size = -1; - std::unique_lock uMutex(mCreateMutex); + int64_t eventReportID = atoll(value); + mSet->AnalyticsID = eventReportID; + } else if (theKey == "bandWidth") { + mSet->mDefaultBandWidth = atoi(value); + } else if (theKey == "description") { + mSet->mOptions.set(theKey, value, options::REPLACE); + return 0; + } else if (theKey == "enableVRC") { + mSet->bEnableVRC = (atoi(value) != 0); + } else if (theKey == "maxAccurateSeekDelta") { + mSet->maxASeekDelta = atoi(value) * 1000; + } else if (theKey == "maxVideoRecoverSize") { + mSet->maxVideoRecoverSize = atoi(value); + } else if (theKey == "surfaceChanged") { + std::lock_guard uMutex(mCreateMutex); - if (mDataSource && mDemuxerService) { - if (!mDemuxerService->isPlayList()) { - size = mDataSource->Seek(0, SEEK_SIZE); - } - } + if (mAVDeviceManager->isVideoRenderValid()) { + mAVDeviceManager->getVideoRender()->surfaceChanged(); + } + } else if (theKey == "streamTypes") { + uint64_t flags = atoll(value); + mSet->bDisableAudio = mSet->bDisableVideo = true; + if (flags & VIDEO_FLAG) { + mSet->bDisableVideo = false; + } + if (flags & AUDIO_FLAG) { + mSet->bDisableAudio = false; + } + } else if (theKey == "IPResolveType") { + uint64_t type = atoll(value); + mSet->mIpType = static_cast(type); + } else if (theKey == "fastStart") { + mSet->mFastStart = atol(value) != 0; + } else if (theKey == "pixelBufferOutputFormat") { + mSet->pixelBufferOutputFormat = atol(value); + } else if (theKey == "liveStartIndex") { + mSet->mOptions.set(theKey, value, options::REPLACE); + } else if (theKey == "DRMMagicKey") { + mSet->drmMagicKey = value; + } else if (theKey == "sessionId") { + mSet->sessionId = value; - snprintf(value, MAX_OPT_VALUE_LENGTH, "%" PRId64 "", size); - } else if (theKey == "description") { - sprintf(value, "%s", mSet.mOptions.get("description").c_str()); - } else if (theKey == "descriptionLen") { - snprintf(value, MAX_OPT_VALUE_LENGTH, "%lu", - static_cast(mSet.mOptions.get("description").length())); - } else if (theKey == "renderFps") { - float renderFps = GetVideoRenderFps(); - snprintf(value, MAX_OPT_VALUE_LENGTH, "%f", renderFps); + std::lock_guard uMutex(mCreateMutex); + if( mDemuxerService != nullptr && mDemuxerService->getDemuxerHandle()) { + mDemuxerService->getDemuxerHandle()->SetOption("sessionId" , mSet->sessionId); } + } else if (theKey == "networkRetryCount") { + mSet->netWorkRetryCount = (int) atol(value); + } + return 0; +} + +void SuperMediaPlayer::GetOption(const char *key, char *value) +{ + if (key == nullptr) { return; } - int64_t SuperMediaPlayer::GetCurrentPosition() - { - return getCurrentPosition() / 1000; - } + string theKey = key; - void SuperMediaPlayer::NotifyPosition(int64_t position) - { - mPlayingPosition = position; - mPNotifier->NotifyPosition(position); - } + if (theKey == "maxBufferDuration") { + snprintf(value, MAX_OPT_VALUE_LENGTH, "%" PRId64 "", mSet->maxBufferDuration); + } else if (theKey == "mediaStreamSize") { + int64_t size = -1; + std::unique_lock uMutex(mCreateMutex); - int64_t SuperMediaPlayer::GetPlayingPosition() - { - if (isSeeking()) { - return mSeekPos / 1000; + if (mDataSource && mDemuxerService) { + if (!mDemuxerService->isPlayList()) { + size = mDataSource->Seek(0, SEEK_SIZE); + } + } else if (mBSSeekCb) { + size = mBSSeekCb(mBSCbArg, 0, SEEK_SIZE); } - return mPlayingPosition; + snprintf(value, MAX_OPT_VALUE_LENGTH, "%" PRId64 "", size); + } else if (theKey == "description") { + sprintf(value, "%s", mSet->mOptions.get("description").c_str()); + } else if (theKey == "descriptionLen") { + snprintf(value, MAX_OPT_VALUE_LENGTH, "%lu", static_cast(mSet->mOptions.get("description").length())); + } else if (theKey == "renderFps") { + float renderFps = GetVideoRenderFps(); + snprintf(value, MAX_OPT_VALUE_LENGTH, "%f", renderFps); } - int64_t SuperMediaPlayer::getCurrentPosition() - { - if (isSeeking()) { - return mSeekPos; - } - - mCurrentPos = mCurrentPos < 0 ? 0 : mCurrentPos; + return; +} - if (mDuration > 0) { - mCurrentPos = mCurrentPos <= mDuration ? mCurrentPos : mDuration; - } +void SuperMediaPlayer::NotifyPosition(int64_t position) +{ + mPNotifier->NotifyPosition(position / 1000); +} - return mCurrentPos; +int64_t SuperMediaPlayer::getCurrentPosition() +{ + if (isSeeking()) { + return mSeekPos; } - void SuperMediaPlayer::SetScaleMode(ScaleMode mode) - { - if (mode == mSet.scaleMode) { - return; - } + mCurrentPos = mCurrentPos.load() < 0 ? 0 : mCurrentPos.load(); - mSet.scaleMode = static_cast(mode); - this->putMsg(MSG_SET_DISPLAY_MODE, dummyMsg); + if (mDuration > 0) { + mCurrentPos = mCurrentPos.load() <= mDuration ? mCurrentPos.load() : mDuration; } - void SuperMediaPlayer::SetRotateMode(RotateMode mode) - { - if (mode == mSet.rotateMode) { - return; - } + return mCurrentPos; +} - mSet.rotateMode = static_cast(mode); - this->putMsg(MSG_SET_ROTATE_MODE, dummyMsg); +void SuperMediaPlayer::SetScaleMode(ScaleMode mode) +{ + if (mode == mSet->scaleMode) { + return; } - RotateMode SuperMediaPlayer::GetRotateMode() - { - return mSet.rotateMode; + mSet->scaleMode = static_cast(mode); + this->putMsg(MSG_SET_DISPLAY_MODE, dummyMsg); +} + +void SuperMediaPlayer::SetRotateMode(RotateMode mode) +{ + if (mode == mSet->rotateMode) { + return; } - void SuperMediaPlayer::SetMirrorMode(MirrorMode mode) - { - if (mode == mSet.mirrorMode) { - return; - } + mSet->rotateMode = static_cast(mode); + this->putMsg(MSG_SET_ROTATE_MODE, dummyMsg); +} + +RotateMode SuperMediaPlayer::GetRotateMode() +{ + return mSet->rotateMode; +} - mSet.mirrorMode = static_cast(mode); - this->putMsg(MSG_SET_MIRROR_MODE, dummyMsg); +void SuperMediaPlayer::SetMirrorMode(MirrorMode mode) +{ + if (mode == mSet->mirrorMode) { + return; } - void SuperMediaPlayer::SetVideoBackgroundColor(uint32_t color) - { - if (color == mSet.mVideoBackgroundColor ) { - return; - } + mSet->mirrorMode = static_cast(mode); + this->putMsg(MSG_SET_MIRROR_MODE, dummyMsg); +} - mSet.mVideoBackgroundColor = color; - this->putMsg(MSG_SET_VIDEO_BACKGROUND_COLOR, dummyMsg); +void SuperMediaPlayer::SetVideoBackgroundColor(uint32_t color) +{ + if (color == mSet->mVideoBackgroundColor) { + return; } - MirrorMode SuperMediaPlayer::GetMirrorMode() - { - return mSet.mirrorMode; - } + mSet->mVideoBackgroundColor = color; + this->putMsg(MSG_SET_VIDEO_BACKGROUND_COLOR, dummyMsg); +} - ScaleMode SuperMediaPlayer::GetScaleMode() - { - return mSet.scaleMode; - } +MirrorMode SuperMediaPlayer::GetMirrorMode() +{ + return mSet->mirrorMode; +} - int64_t SuperMediaPlayer::GetBufferPosition() - { - return mBufferPosition / 1000; - } +ScaleMode SuperMediaPlayer::GetScaleMode() +{ + return mSet->scaleMode; +} - int64_t SuperMediaPlayer::GetDuration() const - { +int64_t SuperMediaPlayer::GetBufferPosition() +{ + return mBufferPosition / 1000; +} + +int64_t SuperMediaPlayer::GetDuration() const +{ + if (mDuration != INT64_MIN) { return mDuration / 1000; } + return -1; +} - // TODO: change name to EnableHwDecode - void SuperMediaPlayer::SetDecoderType(DecoderType type) - { - mSet.bEnableHwVideoDecode = (type == DT_HARDWARE); - } +// TODO: change name to EnableHwDecode +void SuperMediaPlayer::SetDecoderType(DecoderType type) +{ + mSet->bEnableHwVideoDecode = (type == DT_HARDWARE); +} - void SuperMediaPlayer::AddCustomHttpHeader(const char *header) - { - for (auto &item : mSet.customHeaders) { - if (item == header) { - return; - } +void SuperMediaPlayer::AddCustomHttpHeader(const char *header) +{ + for (auto &item : mSet->customHeaders) { + if (item == header) { + return; } - - mSet.customHeaders.emplace_back(header); } - void SuperMediaPlayer::RemoveAllCustomHttpHeader() - { - mSet.customHeaders.clear(); - } + mSet->customHeaders.emplace_back(header); +} - // TODO: move to mainService thread - void SuperMediaPlayer::setSpeed(float speed) - { - MsgParam param; - MsgSpeedParam speedParam; - speedParam.speed = speed; - param.msgSpeedParam = speedParam; - putMsg(MSG_SET_SPEED, param); - } +void SuperMediaPlayer::RemoveAllCustomHttpHeader() +{ + mSet->customHeaders.clear(); +} - float SuperMediaPlayer::getSpeed() - { - return mSet.rate; - } +// TODO: move to mainService thread +void SuperMediaPlayer::setSpeed(float speed) +{ + MsgParam param; + MsgSpeedParam speedParam; + speedParam.speed = speed; + param.msgSpeedParam = speedParam; + putMsg(MSG_SET_SPEED, param); +} - DecoderType SuperMediaPlayer::GetDecoderType() - { - std::lock_guard uMutex(mCreateMutex); +float SuperMediaPlayer::getSpeed() +{ + return mSet->rate; +} - if (mVideoDecoder) { - if (mVideoDecoder->getFlags() & DECFLAG_HW) { - return DT_HARDWARE; - } - } +DecoderType SuperMediaPlayer::GetDecoderType() +{ + std::lock_guard uMutex(mCreateMutex); - return DT_SOFTWARE; + if (mAVDeviceManager->getVideoDecoderFlags() & DECFLAG_HW) { + return DT_HARDWARE; } - PlayerStatus SuperMediaPlayer::GetPlayerStatus() const - { - return mPlayStatus; - } + return DT_SOFTWARE; +} - float SuperMediaPlayer::GetVolume() const - { - return mSet.mVolume; - } +PlayerStatus SuperMediaPlayer::GetPlayerStatus() const +{ + return mPlayStatus; +} - int64_t SuperMediaPlayer::GetPropertyInt(PropertyKey key) - { - switch (key) { - case PROPERTY_KEY_VIDEO_BUFFER_LEN: { - int64_t duration = mBufferController.GetPacketDuration(BUFFER_TYPE_VIDEO); +float SuperMediaPlayer::GetVolume() const +{ + return mSet->mVolume; +} - if (duration < 0) { - duration = mBufferController.GetPacketLastPTS(BUFFER_TYPE_VIDEO) - - mBufferController.GetPacketPts(BUFFER_TYPE_VIDEO); - } +int64_t SuperMediaPlayer::GetPropertyInt(PropertyKey key) +{ + switch (key) { + case PROPERTY_KEY_VIDEO_BUFFER_LEN: { + int64_t duration = mBufferController->GetPacketDuration(BUFFER_TYPE_VIDEO); - return duration; + if (duration < 0) { + duration = mBufferController->GetPacketLastPTS(BUFFER_TYPE_VIDEO) - mBufferController->GetPacketPts(BUFFER_TYPE_VIDEO); } - case PROPERTY_KEY_REMAIN_LIVE_SEG: - return mRemainLiveSegment; + return duration; + } - case PROPERTY_KEY_NETWORK_IS_CONNECTED: - return mSourceListener.isConnected(); + case PROPERTY_KEY_REMAIN_LIVE_SEG: + return mRemainLiveSegment; - default: - break; - } + case PROPERTY_KEY_NETWORK_IS_CONNECTED: + return mSourceListener->isConnected(); - return 0; + default: + break; } - std::string SuperMediaPlayer::GetPropertyString(PropertyKey key) - { - switch (key) { - case PROPERTY_KEY_RESPONSE_INFO: { - CicadaJSONArray array; - std::lock_guard uMutex(mCreateMutex); - MediaPlayerUtil::addURLProperty("responseInfo", array, mDataSource); - //if (mDemuxerService->isPlayList()) - { - MediaPlayerUtil::getPropertyJSONStr("responseInfo", array, false, mStreamInfoQueue, - mDemuxerService); - } - return array.printJSON(); - } + return 0; +} - case PROPERTY_KEY_CONNECT_INFO: { - std::lock_guard uMutex(mCreateMutex); +std::string SuperMediaPlayer::GetPropertyString(PropertyKey key) +{ + switch (key) { + case PROPERTY_KEY_RESPONSE_INFO: { + CicadaJSONArray array; + std::lock_guard uMutex(mCreateMutex); + MediaPlayerUtil::addURLProperty("responseInfo", array, mDataSource); + //if (mDemuxerService->isPlayList()) + { + MediaPlayerUtil::getPropertyJSONStr("responseInfo", array, false, mStreamInfoQueue, mDemuxerService); + } + return array.printJSON(); + } - if (mDataSource) { - return mDataSource->GetOption("connectInfo"); - } + case PROPERTY_KEY_CONNECT_INFO: { + std::lock_guard uMutex(mCreateMutex); - return ""; + if (mDataSource) { + return mDataSource->GetOption("connectInfo"); } - case PROPERTY_KEY_OPEN_TIME_STR: { - CicadaJSONArray array; - CicadaJSONItem item; - item.addValue("readpacketMS", (double) mFirstReadPacketSucMS); - array.addJSON(item); - std::lock_guard uMutex(mCreateMutex); - MediaPlayerUtil::addURLProperty("connectInfo", array, mDataSource); - //if (mDemuxerService->isPlayList()) - { - MediaPlayerUtil::getPropertyJSONStr("openJsonInfo", array, true, mStreamInfoQueue, - mDemuxerService); - } - return array.printJSON(); - } + return ""; + } - case PROPERTY_KEY_PROBE_STR: { - CicadaJSONArray array; - std::lock_guard uMutex(mCreateMutex); - MediaPlayerUtil::addURLProperty("probeInfo", array, mDataSource); + case PROPERTY_KEY_OPEN_TIME_STR: { + CicadaJSONArray array; + CicadaJSONItem item; + item.addValue("readpacketMS", (double) mFirstReadPacketSucMS); + array.addJSON(item); + std::lock_guard uMutex(mCreateMutex); + MediaPlayerUtil::addURLProperty("connectInfo", array, mDataSource); + //if (mDemuxerService->isPlayList()) + { + MediaPlayerUtil::getPropertyJSONStr("openJsonInfo", array, true, mStreamInfoQueue, mDemuxerService); + } + return array.printJSON(); + } - if (nullptr == mDemuxerService) { - return array.printJSON(); - } else if (mDemuxerService->isPlayList()) { - MediaPlayerUtil::getPropertyJSONStr("probeInfo", array, false, mStreamInfoQueue, mDemuxerService); - } else { - CicadaJSONItem item(mDemuxerService->GetProperty(0, "probeInfo")); - item.addValue("type", "video"); - array.addJSON(item); - } + case PROPERTY_KEY_PROBE_STR: { + CicadaJSONArray array; + std::lock_guard uMutex(mCreateMutex); + MediaPlayerUtil::addURLProperty("probeInfo", array, mDataSource); + if (nullptr == mDemuxerService) { return array.printJSON(); + } else if (mDemuxerService->isPlayList()) { + MediaPlayerUtil::getPropertyJSONStr("probeInfo", array, false, mStreamInfoQueue, mDemuxerService); + } else { + CicadaJSONItem item(mDemuxerService->GetProperty(0, "probeInfo")); + item.addValue("type", "video"); + array.addJSON(item); } - case PROPERTY_KEY_DELAY_INFO: { - if (nullptr != mDemuxerService) { - string ret = mDemuxerService->GetProperty(0, "delayInfo"); - return ret; - } + return array.printJSON(); + } - return ""; + case PROPERTY_KEY_DELAY_INFO: { + std::lock_guard uMutex(mCreateMutex); + if (nullptr != mDemuxerService) { + string ret = mDemuxerService->GetProperty(0, "delayInfo"); + return ret; } - default: - break; + return ""; + } + case PROPERTY_KEY_PLAY_CONFIG: { + CicadaJSONItem item{}; + item.addValue("http_proxy" , mSet->http_proxy); + item.addValue("refer" , mSet->refer); + item.addValue("timeout_ms" , (int)mSet->timeout_ms); + item.addValue("RTMaxDelayTime" , (int)mSet->RTMaxDelayTime); + item.addValue("startBufferDuration" , (int)mSet->startBufferDuration); + item.addValue("highLevelBufferDuration" , (int)mSet->highLevelBufferDuration); + item.addValue("maxBufferDuration" , (int)mSet->maxBufferDuration); + return item.printJSON(); + } + case PROPERTY_KEY_DECODE_INFO: { + CicadaJSONArray decodeInfos{}; + if (HAVE_AUDIO) { + CicadaJSONItem audioDecodeInfo{}; + audioDecodeInfo.addValue("type", "audio"); + audioDecodeInfo.addValue("createDecodeCost", + (int)mRecorderSet->createAudioDecoderCostMs); + audioDecodeInfo.addValue("decodeFirstCost", + (int)mRecorderSet->decodeFirstAudioFrameInfo.getDecodeFirstFrameCost()); + audioDecodeInfo.addValue("firstSize", + (int)mRecorderSet->decodeFirstAudioFrameInfo.firstPacketSize); + audioDecodeInfo.addValue("firstPts", + (double)mRecorderSet->decodeFirstAudioFrameInfo.firstPacketPts); + decodeInfos.addJSON(audioDecodeInfo); + } + if (HAVE_VIDEO) { + CicadaJSONItem videoDecodeInfo{}; + videoDecodeInfo.addValue("type", "video"); + videoDecodeInfo.addValue("createDecodeCost", + (int)mRecorderSet->createVideoDecoderCostMs); + videoDecodeInfo.addValue("decodeFirstCost", + (int)mRecorderSet->decodeFirstVideoFrameInfo.getDecodeFirstFrameCost()); + videoDecodeInfo.addValue("firstSize", + (int)mRecorderSet->decodeFirstVideoFrameInfo.firstPacketSize); + videoDecodeInfo.addValue("firstPts", + (double)mRecorderSet->decodeFirstVideoFrameInfo.firstPacketPts); + decodeInfos.addJSON(videoDecodeInfo); + } + + return decodeInfos.printJSON(); + } + case PROPERTY_KEY_HLS_KEY_URL: { + std::lock_guard uMutex(mCreateMutex); + if (nullptr != mDemuxerService) { + return mDemuxerService->GetProperty(0, "keyUrl"); + } + + return ""; } - return ""; + default: + break; } - int SuperMediaPlayer::getCurrentStreamMeta(Stream_meta *meta, StreamType type) - { - int streamIndex = -1; - - switch (type) { - case ST_TYPE_VIDEO: - streamIndex = mCurrentVideoIndex; - break; + return ""; +} - case ST_TYPE_AUDIO: - streamIndex = mCurrentAudioIndex; - break; +int SuperMediaPlayer::getCurrentStreamMeta(Stream_meta *meta, StreamType type) +{ + int streamIndex = -1; - case ST_TYPE_SUB: - streamIndex = mCurrentSubtitleIndex; - break; + switch (type) { + case ST_TYPE_VIDEO: + streamIndex = mCurrentVideoIndex; + break; - default: - return -EINVAL; - } + case ST_TYPE_AUDIO: + streamIndex = mCurrentAudioIndex; + break; - std::unique_lock uMutex(mCreateMutex); + case ST_TYPE_SUB: + streamIndex = mCurrentSubtitleIndex; + break; - if (streamIndex < 0 || mDemuxerService == nullptr) { + default: return -EINVAL; - } - - return mDemuxerService->GetStreamMeta(meta, streamIndex, false); } - void SuperMediaPlayer::reLoad() - { - mSourceListener.enableRetry(); - std::lock_guard uMutex(mCreateMutex); - if (mDemuxerService) { - mDemuxerService->getDemuxerHandle()->Reload(); - } - } + std::unique_lock uMutex(mCreateMutex); - IVideoRender::Scale SuperMediaPlayer::convertScaleMode(ScaleMode mode) - { - if (mode == ScaleMode::SM_CROP) { - return IVideoRender::Scale::Scale_AspectFill; - } else if (mode == ScaleMode::SM_FIT) { - return IVideoRender::Scale::Scale_AspectFit; - } else { - return IVideoRender::Scale::Scale_Fill; - } + if (streamIndex < 0 || mDemuxerService == nullptr) { + return -EINVAL; } - IVideoRender::Rotate SuperMediaPlayer::convertRotateMode(RotateMode mode) - { - if (mode == RotateMode::ROTATE_MODE_0) { - return IVideoRender::Rotate::Rotate_None; - } else if (mode == RotateMode::ROTATE_MODE_90) { - return IVideoRender::Rotate::Rotate_90; - } else if (mode == RotateMode::ROTATE_MODE_180) { - return IVideoRender::Rotate::Rotate_180; - } else if (mode == RotateMode::ROTATE_MODE_270) { - return IVideoRender::Rotate::Rotate_270; - } else { - return IVideoRender::Rotate::Rotate_None; - } + return mDemuxerService->GetStreamMeta(meta, streamIndex, false); +} + +void SuperMediaPlayer::reLoad() +{ + mSourceListener->enableRetry(); + std::lock_guard uMutex(mCreateMutex); + if (mDemuxerService && mDemuxerService->getDemuxerHandle()) { + mDemuxerService->getDemuxerHandle()->Reload(); } +} +IVideoRender::Scale SuperMediaPlayer::convertScaleMode(ScaleMode mode) +{ + if (mode == ScaleMode::SM_CROP) { + return IVideoRender::Scale::Scale_AspectFill; + } else if (mode == ScaleMode::SM_FIT) { + return IVideoRender::Scale::Scale_AspectFit; + } else { + return IVideoRender::Scale::Scale_Fill; + } +} - IVideoRender::Flip SuperMediaPlayer::convertMirrorMode(MirrorMode mode) - { - if (mode == MirrorMode::MIRROR_MODE_HORIZONTAL) { - return IVideoRender::Flip::Flip_Horizontal; - } else if (mode == MirrorMode::MIRROR_MODE_VERTICAL) { - return IVideoRender::Flip::Flip_Vertical; - } else if (mode == MirrorMode::MIRROR_MODE_NONE) { - return IVideoRender::Flip::Flip_None; - } else { - return IVideoRender::Flip::Flip_None; - } +IVideoRender::Rotate SuperMediaPlayer::convertRotateMode(RotateMode mode) +{ + if (mode == RotateMode::ROTATE_MODE_0) { + return IVideoRender::Rotate::Rotate_None; + } else if (mode == RotateMode::ROTATE_MODE_90) { + return IVideoRender::Rotate::Rotate_90; + } else if (mode == RotateMode::ROTATE_MODE_180) { + return IVideoRender::Rotate::Rotate_180; + } else if (mode == RotateMode::ROTATE_MODE_270) { + return IVideoRender::Rotate::Rotate_270; + } else { + return IVideoRender::Rotate::Rotate_None; } +} - float SuperMediaPlayer::GetVideoRenderFps() - { - if (mVideoRender) { - return mVideoRender->getRenderFPS(); - } - return mUtil.getVideoRenderFps(); +IVideoRender::Flip SuperMediaPlayer::convertMirrorMode(MirrorMode mode) +{ + if (mode == MirrorMode::MIRROR_MODE_HORIZONTAL) { + return IVideoRender::Flip::Flip_Horizontal; + } else if (mode == MirrorMode::MIRROR_MODE_VERTICAL) { + return IVideoRender::Flip::Flip_Vertical; + } else if (mode == MirrorMode::MIRROR_MODE_NONE) { + return IVideoRender::Flip::Flip_None; + } else { + return IVideoRender::Flip::Flip_None; } +} - - float SuperMediaPlayer::GetVideoDecodeFps() - { - return 0.0f; +float SuperMediaPlayer::GetVideoRenderFps() +{ + if (mAVDeviceManager->isVideoRenderValid()) { + return mAVDeviceManager->getVideoRender()->getRenderFPS(); } - void SuperMediaPlayer::NotifyError(int code) - { - ChangePlayerStatus(PLAYER_ERROR); + return mUtil->getVideoRenderFps(); +} - if (mErrorConverter) { - int newErrorCode; - std::string outStr; - int processed = mErrorConverter->ConvertErrorCode(code, newErrorCode, outStr); - if (processed) { - AF_LOGE("Player ConvertErrorCode 0x%08x :%s\n", newErrorCode, outStr.c_str()); - mPNotifier->NotifyError(newErrorCode, outStr.c_str()); - return; - } - } +float SuperMediaPlayer::GetVideoDecodeFps() +{ + return 0.0f; +} - int newErrorCode = framework_error2_code(code); - char errbuf[128] = {0}; - int isFfmpegError = -1; +void SuperMediaPlayer::NotifyError(int code) +{ + ChangePlayerStatus(PLAYER_ERROR); - if (newErrorCode == MEDIA_PLAYER_ERROR_UNKNOWN) { - //maybe ffmpeg error code,,, try get ffmpeg error msg. - isFfmpegError = get_ffmpeg_error_message(code, errbuf, 128); - } + if (mErrorConverter) { + int newErrorCode; + std::string outStr; + int processed = mErrorConverter->ConvertErrorCode(code, newErrorCode, outStr); - if (isFfmpegError == 0) { - AF_LOGE("Player ReadPacket ffmpeg error ?? 0x%04x :%s\n", -code, errbuf); - mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DEMUXER_OPENSTREAM, errbuf); - } else { - char *desc = const_cast(framework_err2_string(code)); - mPNotifier->NotifyError(newErrorCode, desc); + if (processed) { + AF_LOGE("Player ConvertErrorCode 0x%08x :%s\n", newErrorCode, outStr.c_str()); + mPNotifier->NotifyError(newErrorCode, outStr.c_str()); + return; } } - void SuperMediaPlayer::updateLoopGap() - { - switch (mPlayStatus.load()) { - case PLAYER_PREPARINIT: - case PLAYER_PREPARING: - case PLAYER_PREPARED: - mMaxRunningLoopGap = 3; - break; - - case PLAYER_PLAYING: - if (!mFirstRendered) { - mMaxRunningLoopGap = 3; - } else { - if (mSet.bDisableBufferManager) { - if (CicadaUtils::isEqual(mSet.rate, 1.0)) { - mMaxRunningLoopGap = 8; - } else { - mMaxRunningLoopGap = 6; - } - } else { - if (dropLateVideoFrames || mSeekNeedCatch) { - mMaxRunningLoopGap = 2; - return; - } - - float fps = GetVideoRenderFps(); + int newErrorCode = framework_error2_code(code); + char errbuf[128] = {0}; + int isFfmpegError = -1; - if (fps > 20 && (mSet.rate < 1.3)) { - mMaxRunningLoopGap = static_cast(1000 / fps - 5); - } else { - mMaxRunningLoopGap = 15; - } + if (newErrorCode == MEDIA_PLAYER_ERROR_UNKNOWN) { + //maybe ffmpeg error code,,, try get ffmpeg error msg. + isFfmpegError = get_ffmpeg_error_message(code, errbuf, 128); + } - //FIXME: for now this logic is not good, close it temp - mMaxRunningLoopGap = 10; + if (isFfmpegError == 0) { + AF_LOGE("Player ReadPacket ffmpeg error ?? 0x%04x :%s\n", -code, errbuf); + mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DEMUXER_OPENSTREAM, errbuf); + } else { + char *desc = const_cast(framework_err2_string(code)); + mPNotifier->NotifyError(newErrorCode, desc); + } +} - if (mLastAudioFrameDuration > 0) { - float duration = mLastAudioFrameDuration; +int SuperMediaPlayer::updateLoopGap() +{ + switch (mPlayStatus.load()) { + case PLAYER_PREPARINIT: + case PLAYER_PREPARING: + case PLAYER_PREPARED: + return 3; - if (mSet.rate >= 0.5) { - // loop once will decoder and render two audio frame, set 1.5 for more safe - duration = static_cast( - ((float) mLastAudioFrameDuration * 1.5) / - (1000 * mSet.rate)); - } + case PLAYER_PLAYING: + if (!mFirstRendered) { + return 3; + } else if (HAVE_VIDEO) { + if (mCurrentVideoMeta) { + // the loop gap can't too low + int fps = std::max(25, (int) (mCurrentVideoMeta->operator Stream_meta *()->avg_fps)); + return 1000 / int(fps * mSet->rate * 1.5); + } + } + return 1000 / int(50 * mSet->rate); - if (mMaxRunningLoopGap > duration) { - mMaxRunningLoopGap = static_cast(duration); - } - } + default: + return 40; + } +} - if (mMaxRunningLoopGap > 25) { - mMaxRunningLoopGap = 25; - } else if (mMaxRunningLoopGap < 10) { - mMaxRunningLoopGap = 10; - } - } - } +int SuperMediaPlayer::mainService() +{ + int64_t curTime = af_gettime_relative(); + mUtil->notifyPlayerLoop(curTime); + sendDCAMessage(); - break; + if (mMessageControl->empty() || (0 == mMessageControl->processMsg())) { + ProcessVideoLoop(); + int loopGap = updateLoopGap(); + int64_t use = (af_gettime_relative() - curTime) / 1000; + int64_t needWait = loopGap - use; + // AF_LOGD("use :%lld, needWait:%lld", use, needWait); - default: - mMaxRunningLoopGap = 40; - break; + if (needWait <= 0) { + if (loopGap < 5) { + needWait = 2; + } else { + return 0; + } } - } - int SuperMediaPlayer::mainService() - { - int64_t curTime = af_gettime_relative(); - mUtil.notifyPlayerLoop(curTime); - string event = mDcaManager.getEvent(); - while (!event.empty()) { - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_DIRECT_COMPONENT_MSG, event.c_str()); - event = mDcaManager.getEvent(); - } - - if (mMessageControl.empty() || (0 == mMessageControl.processMsg())) { - ProcessVideoLoop(); - int64_t use = (af_gettime_relative() - curTime) / 1000; - int64_t needWait = mMaxRunningLoopGap - use; -// AF_LOGD("use :%lld, needWait:%lld", use, needWait); - - if (needWait <= 0) { - if (mMaxRunningLoopGap < 5) { - needWait = 2; - } else { - return 0; - } - } + std::unique_lock uMutex(mSleepMutex); + mPlayerCondition.wait_for(uMutex, std::chrono::milliseconds(needWait), [this]() { return this->mCanceled.load(); }); + } - std::unique_lock uMutex(mSleepMutex); - mPlayerCondition.wait_for(uMutex, std::chrono::milliseconds(needWait), - [this]() { return this->mCanceled.load(); }); - } + return 0; +} - return 0; +void SuperMediaPlayer::sendDCAMessage() +{ + string event = mDcaManager->getEvent(); + while (!event.empty()) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_DIRECT_COMPONENT_MSG, event.c_str()); + event = mDcaManager->getEvent(); } +} - void SuperMediaPlayer::ProcessVideoLoop() +void SuperMediaPlayer::ProcessVideoLoop() +{ + int64_t curTime = af_gettime_relative() / 1000; { - int64_t curTime = af_gettime_relative() / 1000; - { #ifndef NDEBUG - int streamIds[] = { - mCurrentVideoIndex, - mCurrentAudioIndex, - mCurrentSubtitleIndex, - mWillChangedVideoStreamIndex, - mWillChangedAudioStreamIndex, - mWillChangedSubtitleStreamIndex - }; - int size = sizeof(streamIds) / sizeof(streamIds[0]); - - for (int i = 0; i < size; ++i) { - for (int j = 0; j < size; ++j) { - if (i != j && streamIds[i] >= 0) { - assert(streamIds[i] != streamIds[j]); - } + int streamIds[] = {mCurrentVideoIndex, mCurrentAudioIndex, mCurrentSubtitleIndex, + mWillChangedVideoStreamIndex, mWillChangedAudioStreamIndex, mWillChangedSubtitleStreamIndex}; + int size = sizeof(streamIds) / sizeof(streamIds[0]); + + for (int i = 0; i < size; ++i) { + for (int j = 0; j < size; ++j) { + if (i != j && streamIds[i] >= 0) { + assert(streamIds[i] != streamIds[j]); } } - -#endif } - if (mSubPlayer) { - mSubPlayer->onNoop(); - } +#endif + } - if ((PLAYER_COMPLETION != mPlayStatus && (mPlayStatus < PLAYER_PREPARING || mPlayStatus > PLAYER_PAUSED)) - || nullptr == mDemuxerService) { // not working - if (curTime - mTimerLatestTime > mTimerInterval) { - OnTimer(curTime); - mTimerLatestTime = curTime; - } + if (mSubPlayer) { + mSubPlayer->onNoop(); + } - return; + if ((PLAYER_COMPLETION != mPlayStatus && (mPlayStatus < PLAYER_PREPARING || mPlayStatus > PLAYER_PAUSED)) || + nullptr == mDemuxerService) {// not working + if (curTime - mTimerLatestTime > mTimerInterval) { + OnTimer(curTime); + mTimerLatestTime = curTime; } - setUpAVPath(); - doReadPacket(); + return; + } + doReadPacket(); - if (!DoCheckBufferPass()) { - return; - } + if (!DoCheckBufferPass()) { + return; + } - doDeCode(); + doDeCode(); - if (!mBRendingStart && mPlayStatus == PLAYER_PLAYING && !mBufferingFlag) { - if ((!HAVE_VIDEO || !mVideoFrameQue.empty() || (APP_BACKGROUND == mAppStatus)) - && (!HAVE_AUDIO || !mAudioFrameQue.empty())) { - startRendering(true); - } + // audio render will create after get a frame from decoder + setUpAVPath(); + + if (!mBRendingStart && mPlayStatus == PLAYER_PLAYING && !mBufferingFlag) { + if ((mEof && (!HAVE_AUDIO || mAVDeviceManager->isAudioRenderValid()) && + (!HAVE_VIDEO || mAVDeviceManager->isVideoRenderValid())) ||// render out the cache frame in renders + ((!HAVE_VIDEO || !mVideoFrameQue.empty() || (APP_BACKGROUND == mAppStatus)) && (!HAVE_AUDIO || !mAudioFrameQue.empty()))) { + startRendering(true); } + } - doRender(); - checkEOS(); - curTime = af_gettime_relative() / 1000; + doRender(); + checkEOS(); + curTime = af_gettime_relative() / 1000; - if (curTime - mTimerLatestTime > mTimerInterval) { - OnTimer(curTime); - mTimerLatestTime = curTime; - } + if (curTime - mTimerLatestTime > mTimerInterval) { + OnTimer(curTime); + mTimerLatestTime = curTime; } +} - void SuperMediaPlayer::doReadPacket() - { - //check packet queue full - int64_t cur_buffer_duration = getPlayerBufferDuration(false); - //100s - mUtil.notifyRead(MediaPlayerUtil::readEvent_Loop); - - if (!mEof) { - //demuxer read - int64_t read_start_time = af_gettime_relative(); - int timeout = 10000; - - if (mSet.bDisableBufferManager) { - timeout = 5000; - } +void SuperMediaPlayer::doReadPacket() +{ + //check packet queue full + int64_t cur_buffer_duration = getPlayerBufferDuration(false, false); + //100s + mUtil->notifyRead(MediaPlayerUtil::readEvent_Loop, 0); - mem_info info{}; - int checkStep = 0; + if (mEof) { + return; + } - while (true) { - // once buffer is full, we will try to read again if buffer consume more then BufferGap - if (mBufferIsFull) { - static const int BufferGap = 1000 * 1000; + //demuxer read + int64_t read_start_time = af_gettime_relative(); + int timeout = 10000; + mem_info info{}; + int checkStep = 0; - if ((mSet.maxBufferDuration > 2 * BufferGap) - && (cur_buffer_duration > mSet.maxBufferDuration - BufferGap)) { - break; - } - } + while (true) { + // once buffer is full, we will try to read again if buffer consume more then BufferGap + if (mBufferIsFull) { + static const int BufferGap = 1000 * 1000; - if (cur_buffer_duration > mSet.maxBufferDuration) { - mBufferIsFull = true; - break; - } + if ((mSet->maxBufferDuration > 2 * BufferGap) && (cur_buffer_duration > mSet->maxBufferDuration - BufferGap) && + getPlayerBufferDuration(false, true) > 0) { + break; + } + } - mBufferIsFull = false; + if (cur_buffer_duration > mSet->maxBufferDuration && + getPlayerBufferDuration(false, true) > 0// we need readout the buffer in demuxer when no buffer in player + ) { + mBufferIsFull = true; + break; + } - if ((0 >= checkStep--) - && (cur_buffer_duration > 1000 * 1000) - && (AFGetSystemMemInfo(&info) >= 0)) { - //AF_LOGD("system_availableram is %" PRIu64 "",info.system_availableram); - if (info.system_availableram > 2 * mSet.lowMemSize) { - checkStep = (int) (info.system_availableram / (5 * 1024 * 1024)); - } else if (info.system_availableram < mSet.lowMemSize) { - AF_LOGW("low memery..."); + mBufferIsFull = false; - if (!mLowMem) { - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SYSTEM_LOW_MEMORY, "App Low memory"); - } + if ((0 >= checkStep--) && (cur_buffer_duration > 1000 * 1000) && (AFGetSystemMemInfo(&info) >= 0)) { + //AF_LOGD("system_availableram is %" PRIu64 "",info.system_availableram); + if (info.system_availableram > 2 * mSet->lowMemSize) { + checkStep = (int) (info.system_availableram / (5 * 1024 * 1024)); + } else if (info.system_availableram < mSet->lowMemSize) { + AF_LOGW("low memery..."); - mLowMem = true; + if (!mLowMem) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SYSTEM_LOW_MEMORY, "App Low memory"); + } - if (mSet.highLevelBufferDuration > 800 * 1000) { - mSet.highLevelBufferDuration = 800 * 1000; - } + mLowMem = true; - if (mSet.startBufferDuration > 800 * 1000) { - mSet.startBufferDuration = 800 * 1000; - } + if (mSet->highLevelBufferDuration > 800 * 1000) { + mSet->highLevelBufferDuration = 800 * 1000; + } - break; - } else { - checkStep = 5; - mLowMem = false; - } + if (mSet->startBufferDuration > 800 * 1000) { + mSet->startBufferDuration = 800 * 1000; } - int ret = ReadPacket(); + break; + } else { + checkStep = 5; + mLowMem = false; + } + } - if (ret == -EAGAIN) { - if (0 == mDuration) { - mRemainLiveSegment = mDemuxerService->GetRemainSegmentCount(mCurrentVideoIndex); - } + int ret = ReadPacket(); - mUtil.notifyRead(MediaPlayerUtil::readEvent_Again); - break; - } else if (ret == 0) { - AF_LOGE("Player ReadPacket EOF"); + if (ret == -EAGAIN) { + if (0 == mDuration) { + mRemainLiveSegment = mDemuxerService->GetRemainSegmentCount(mCurrentVideoIndex); + } - if (!mEof) { - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_DEMUXER_EOF, "Demuxer End of File"); - } + mUtil->notifyRead(MediaPlayerUtil::readEvent_Again, 0); + break; + } else if (ret == 0) { + AF_LOGE("Player ReadPacket EOF"); - mEof = true; - break; - } else if (ret == FRAMEWORK_ERR_EXIT) { - AF_LOGE("Player ReadPacket error 0x%04x :%s\n", -ret, framework_err2_string(ret)); - break; - } else if (ret == FRAMEWORK_ERR_FORMAT_NOT_SUPPORT) { - AF_LOGW("read error %s\n", framework_err2_string(ret)); - break; - } else if (ret < 0) { - if (!mBufferingFlag) { - //AF_LOGI("Player ReadPacket ret < 0 with data"); - } else { - AF_LOGE("Player ReadPacket error 0x%04x :%s\n", -ret, framework_err2_string(ret)); + if (!mEof) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_DEMUXER_EOF, "Demuxer End of File"); + } - if (ret != FRAMEWORK_ERR_EXIT && !mCanceled) { - NotifyError(ret); - } - } + mEof = true; + break; + } else if (ret == FRAMEWORK_ERR_EXIT) { + AF_LOGE("Player ReadPacket error 0x%04x :%s\n", -ret, framework_err2_string(ret)); + break; + } else if (ret == FRAMEWORK_ERR_FORMAT_NOT_SUPPORT) { + AF_LOGE("read error %s\n", framework_err2_string(ret)); + NotifyError(ret); + break; + } else if (ret < 0) { + if (!mBufferingFlag && mPlayStatus >= PLAYER_PREPARED) { + //AF_LOGI("Player ReadPacket ret < 0 with data"); + } else { + AF_LOGE("Player ReadPacket error 0x%04x :%s\n", -ret, framework_err2_string(ret)); - break; + if (ret != FRAMEWORK_ERR_EXIT && !mCanceled) { + NotifyError(ret); } + } - //AF_LOGI("Player ReadPacket have data"); - if (0 >= mFirstReadPacketSucMS) { - mFirstReadPacketSucMS = af_getsteady_ms(); - } + break; + } - if (af_gettime_relative() - read_start_time > timeout) { - AF_LOGD("Player ReadPacket time out\n"); - mUtil.notifyRead(MediaPlayerUtil::readEvent_timeOut); -// mMsgProcessTime = 0; - break; - } + //AF_LOGI("Player ReadPacket have data"); + if (0 >= mFirstReadPacketSucMS) { + mFirstReadPacketSucMS = af_getsteady_ms(); + } - cur_buffer_duration = getPlayerBufferDuration(false); -// if(getPlayerBufferDuration(true) > mSet.maxBufferDuration * 2){ -// AF_LOGE("buffer stuffed\n"); -// mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_BUFFER_STUFFED,"buffer stuffed"); -// break; -// } - } + if (af_gettime_relative() - read_start_time > timeout) { + AF_LOGD("Player ReadPacket time out\n"); + mUtil->notifyRead(MediaPlayerUtil::readEvent_timeOut, 0); + // mMsgProcessTime = 0; + break; } - } - void SuperMediaPlayer::OnDemuxerCallback(const std::string &key, const std::string &value) - { + cur_buffer_duration = getPlayerBufferDuration(false, false); + // if(getPlayerBufferDuration(true) > mSet->maxBufferDuration * 2){ + // AF_LOGE("buffer stuffed\n"); + // mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_BUFFER_STUFFED,"buffer stuffed"); + // break; + // } } +} - bool SuperMediaPlayer::DoCheckBufferPass() - { - int64_t cur_buffer_duration = getPlayerBufferDuration(false); - int64_t HighBufferDur = mSet.highLevelBufferDuration; +void SuperMediaPlayer::OnDemuxerCallback(const std::string &key, const std::string &value) +{} - if (mSet.bDisableBufferManager) { - HighBufferDur = 10 * 1000; - } +bool SuperMediaPlayer::DoCheckBufferPass() +{ + int64_t cur_buffer_duration = getPlayerBufferDuration(false, false); + int64_t HighBufferDur = mSet->highLevelBufferDuration; - if (mFirstBufferFlag && !mEof) { - if (!mSet.bDisableBufferManager) { - HighBufferDur = mSet.startBufferDuration; - } + if (mFirstBufferFlag && !mEof) { + HighBufferDur = mSet->startBufferDuration; - //clean late audio data - if (cur_buffer_duration > HighBufferDur && HAVE_VIDEO && HAVE_AUDIO) { - if (mSoughtVideoPos > 0) { - AF_LOGW("clean late audio data"); - mBufferController.ClearPacketBeforeTimePos(BUFFER_TYPE_AUDIO, mSoughtVideoPos); - int64_t pts = mBufferController.GetPacketPts(BUFFER_TYPE_AUDIO); + //clean late audio data + if (cur_buffer_duration > HighBufferDur && HAVE_VIDEO && HAVE_AUDIO) { + if (mSoughtVideoPos > 0) { + AF_LOGW("clean late audio data"); + mBufferController->ClearPacketBeforeTimePos(BUFFER_TYPE_AUDIO, mSoughtVideoPos); + int64_t pts = mBufferController->GetPacketPts(BUFFER_TYPE_AUDIO); - if (mRemovedFirstAudioPts == INT64_MIN) { - mRemovedFirstAudioPts = pts; + if (mRemovedFirstAudioPts == INT64_MIN) { + mRemovedFirstAudioPts = pts; - if (mFirstAudioPts == INT64_MIN) { - mFirstAudioPts = pts - mFirstSeekStartTime; - } + if (mFirstAudioPts == INT64_MIN) { + mFirstAudioPts = pts - mFirstSeekStartTime; } + } - cur_buffer_duration = getPlayerBufferDuration(false); + cur_buffer_duration = getPlayerBufferDuration(false, false); - if (cur_buffer_duration < HighBufferDur) { - return false; - } + if (cur_buffer_duration < HighBufferDur) { + return false; } } } + } - if (mPlayStatus == PLAYER_PREPARING) { - if ((cur_buffer_duration >= HighBufferDur && - (!HAVE_VIDEO || videoDecoderFull || APP_BACKGROUND == mAppStatus || !mSet.mFastStart)) || - (mEof)) { - //open stream failed - if (mEof && getPlayerBufferDuration(true) <= 0) { + if (mPlayStatus == PLAYER_PREPARING) { + if ((cur_buffer_duration >= HighBufferDur && + (!HAVE_VIDEO || !mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_VIDEO) || videoDecoderFull || + APP_BACKGROUND == mAppStatus || !mSet->mFastStart)) || + (mEof)) { + if (mEof && getPlayerBufferDuration(true, false) <= 0) { + // If player don`t get any packets when read eof + if (mSeekPos > 0) { + //If caused by before prepare seeked, treat as play completed. + mPNotifier->NotifyLoading(loading_event_end, 0); + playCompleted(); + } else { ChangePlayerStatus(PLAYER_ERROR); mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DEMUXER_OPENSTREAM, "open stream failed"); - } else { - ChangePlayerStatus(PLAYER_PREPARED); - AF_LOGD("PLAYER_PREPARED"); - AF_LOGD("prepare use %lld ms\n", - (af_gettime_relative() - mPrepareStartTime) / 1000); - notifyPreparedCallback(); - - if (mFirstBufferFlag) { - mFirstBufferFlag = false; - } + } + } else { + ChangePlayerStatus(PLAYER_PREPARED); + AF_LOGD("PLAYER_PREPARED"); + AF_LOGD("prepare use %lld ms\n", (af_gettime_relative() - mPrepareStartTime) / 1000); + notifyPreparedCallback(); + + if (mFirstBufferFlag) { + mFirstBufferFlag = false; } } - } + } else { - //check buffering empty - if (!mEof && cur_buffer_duration <= 0 && !mBufferingFlag - && (mPlayStatus == PLAYER_PLAYING || mPlayStatus == PLAYER_PAUSED) - && !mSet.bDisableBufferManager) { - mBufferingFlag = true; - mPNotifier->NotifyLoading(loading_event_start, 0); - AF_LOGD("loading start"); - mLoadingProcess = 0; - mTimeoutStartTime = INT64_MIN; - mMasterClock.pause(); - return false; - } + int64_t duration_v = -1; + int64_t duration_a = -1; - //AF_LOGD("current duration is %lld,video duration is %lld,audio duration is %lld", cur_buffer_duration - // ,mBufferController.GetPacketDuration(BUFFER_TYPE_VIDEO), mBufferController.GetPacketDuration(BUFFER_TYPE_AUDIO)); - while (IS_REAL_TIME_STREAM && mSet.RTMaxDelayTime > 0) { - if (!HAVE_AUDIO) { - int64_t maxBufferDuration = getPlayerBufferDuration(true); - - if (maxBufferDuration > mSet.RTMaxDelayTime + 1000 * 1000 * 5) { - int64_t lastKeyPos = mBufferController.GetPacketLastKeyTimePos(BUFFER_TYPE_VIDEO); - mBufferController.ClearPacketBeforeTimePos(BUFFER_TYPE_VIDEO, lastKeyPos); - break; - } - - LiveCatchUp(maxBufferDuration); - break; + if (HAVE_VIDEO) { + duration_v = mBufferController->GetPacketDuration(BUFFER_TYPE_VIDEO); + } + if (HAVE_AUDIO) { + duration_a = mBufferController->GetPacketDuration(BUFFER_TYPE_AUDIO); } - int64_t maxBufferDuration = getPlayerBufferDuration(true); - - if (maxBufferDuration > mSet.RTMaxDelayTime + 1000 * 1000 * 5) { - //drop frame - int64_t lastVideoPos = mBufferController.GetPacketLastTimePos(BUFFER_TYPE_VIDEO); - int64_t lastAudioPos = mBufferController.GetPacketLastTimePos(BUFFER_TYPE_AUDIO); - int64_t lastAudioPts = mBufferController.GetPacketLastPTS(BUFFER_TYPE_AUDIO); - int64_t lastPos; - - if (lastVideoPos == INT64_MIN) { - lastPos = lastAudioPos; - } else if (lastAudioPos == INT64_MIN) { - lastPos = lastVideoPos; + /** + * if meta has audio and video stream infos , but after read 2 minutes duration , + * one of streams still has no buffer duration , close it to avoid read all packets. + */ + if (std::min(duration_v, duration_a) == 0 && std::max(duration_v, duration_a) > 2 * 60 * 1000000) { + if (duration_v > duration_a) { + mDemuxerService->CloseStream(mCurrentAudioIndex); + mCurrentAudioIndex = -1; + mMasterClock.setReferenceClock(nullptr, nullptr); + mAudioFrameQue.clear(); + mBufferController->ClearPacket(BUFFER_TYPE_AUDIO); + AF_LOGW("close audio stream"); } else { - lastPos = lastAudioPos < lastVideoPos ? lastAudioPos : lastVideoPos; - } - - lastPos -= min(mSet.RTMaxDelayTime, 500 * 1000); - int64_t lastVideoKeyTimePos = mBufferController.GetKeyTimePositionBefore(BUFFER_TYPE_VIDEO, lastPos); - if (lastVideoKeyTimePos != INT64_MIN) { - AF_LOGD("drop left lastPts %lld, lastVideoKeyPts %lld", lastPos, lastVideoKeyTimePos); - ProcessSetSpeed(1.0); - int64_t dropVideoCount = mBufferController.ClearPacketBeforeTimePos(BUFFER_TYPE_VIDEO, lastVideoKeyTimePos); - int64_t dropAudioCount = mBufferController.ClearPacketBeforeTimePos(BUFFER_TYPE_AUDIO, lastVideoKeyTimePos); - - if (dropVideoCount > 0) { - FlushVideoPath(); - AF_LOGD("drop left video duration is %lld,left video size is %d", - mBufferController.GetPacketDuration(BUFFER_TYPE_VIDEO), - mBufferController.GetPacketSize(BUFFER_TYPE_VIDEO)); - } - - if (dropAudioCount > 0) { - FlushAudioPath(); - AF_LOGD("drop left aduio duration is %lld,left aduio size is %d", - mBufferController.GetPacketDuration(BUFFER_TYPE_AUDIO), - mBufferController.GetPacketSize(BUFFER_TYPE_AUDIO)); - mMasterClock.setTime(lastAudioPts); - } + closeVideo(); } } + } + } - int64_t lastAudio = mBufferController.GetPacketLastPTS(BUFFER_TYPE_AUDIO); + //check buffering empty + if (!mEof && cur_buffer_duration <= 0 && !mBufferingFlag && (mPlayStatus == PLAYER_PLAYING || mPlayStatus == PLAYER_PAUSED)) { + mBufferingFlag = true; + mPNotifier->NotifyLoading(loading_event_start, 0); + AF_LOGD("loading start"); + mLoadingProcess = 0; + mTimeoutStartTime = INT64_MIN; + mMasterClock.pause(); + return false; + } - if ((lastAudio != INT64_MIN) && (mPlayedAudioPts != INT64_MIN)) { - int64_t delayTime = lastAudio - mPlayedAudioPts; - static int64_t lastT = af_gettime_ms(); + //AF_LOGD("current duration is %lld,video duration is %lld,audio duration is %lld", cur_buffer_duration + // ,mBufferController->GetPacketDuration(BUFFER_TYPE_VIDEO), mBufferController->GetPacketDuration(BUFFER_TYPE_AUDIO)); + bool isRealTime = false; + if (mDemuxerService != nullptr) { + isRealTime = mDemuxerService->isRealTimeStream(mCurrentVideoIndex); + } - if (af_gettime_ms() - lastT > 1000) { - lastT = af_gettime_ms(); - AF_LOGD("lastAudio:%lld mPlayedAudioPts:%lld, delayTime:%lld", lastAudio, mPlayedAudioPts, delayTime); - } + while (isRealTime && mSet->RTMaxDelayTime > 0) { + if (!HAVE_AUDIO) { + int64_t maxBufferDuration = getPlayerBufferDuration(true, false); - LiveCatchUp(delayTime); + if (maxBufferDuration > mSet->RTMaxDelayTime + 1000 * 1000 * 5) { + int64_t lastKeyPos = mBufferController->GetPacketLastKeyTimePos(BUFFER_TYPE_VIDEO); + mBufferController->ClearPacketBeforeTimePos(BUFFER_TYPE_VIDEO, lastKeyPos); + break; } + LiveCatchUp(maxBufferDuration); break; } - //check buffering status - if ((mBufferingFlag || mFirstBufferFlag) && !mSet.bDisableBufferManager) { - if ((cur_buffer_duration > HighBufferDur && (!HAVE_VIDEO || videoDecoderFull || APP_BACKGROUND == mAppStatus)) || mEof) { - // if still in seek, wait for seek status be changed. - if (!mSeekFlag) { - if (mBufferingFlag) { - mLoadingProcess = -1; - mPNotifier->NotifyLoading(loading_event_end, 0); - AF_LOGD("loading end"); + int64_t maxBufferDuration = getPlayerBufferDuration(true, false); - if (mPlayStatus == PLAYER_PLAYING) { - mMasterClock.start(); - } - } + if (maxBufferDuration > mSet->RTMaxDelayTime + 1000 * 1000 * 5) { + //drop frame + int64_t lastVideoPos = mBufferController->GetPacketLastTimePos(BUFFER_TYPE_VIDEO); + int64_t lastAudioPos = mBufferController->GetPacketLastTimePos(BUFFER_TYPE_AUDIO); + int64_t lastAudioPts = mBufferController->GetPacketLastPTS(BUFFER_TYPE_AUDIO); + int64_t lastPos; - mTimeoutStartTime = INT64_MIN; - mFirstBufferFlag = false; - mBufferingFlag = false; + if (lastVideoPos == INT64_MIN) { + lastPos = lastAudioPos; + } else if (lastAudioPos == INT64_MIN) { + lastPos = lastVideoPos; + } else { + lastPos = lastAudioPos < lastVideoPos ? lastAudioPos : lastVideoPos; + } + + lastPos -= min(mSet->RTMaxDelayTime, 500 * 1000); + int64_t lastVideoKeyTimePos = mBufferController->GetKeyTimePositionBefore(BUFFER_TYPE_VIDEO, lastPos); + if (lastVideoKeyTimePos != INT64_MIN) { + AF_LOGD("drop left lastPts %lld, lastVideoKeyPts %lld", lastPos, lastVideoKeyTimePos); + mMsgCtrlListener->ProcessSetSpeed(1.0); + int64_t dropVideoCount = mBufferController->ClearPacketBeforeTimePos(BUFFER_TYPE_VIDEO, lastVideoKeyTimePos); + int64_t dropAudioCount = mBufferController->ClearPacketBeforeTimePos(BUFFER_TYPE_AUDIO, lastVideoKeyTimePos); + + if (dropVideoCount > 0) { + FlushVideoPath(); + AF_LOGD("drop left video duration is %lld,left video size is %d", + mBufferController->GetPacketDuration(BUFFER_TYPE_VIDEO), mBufferController->GetPacketSize(BUFFER_TYPE_VIDEO)); } - } else if (cur_buffer_duration >= 0) { - // TODO: Notify when change - int prg = MIN(static_cast(cur_buffer_duration * 100 / HighBufferDur), 100); - - if (prg > mLoadingProcess && mBufferingFlag) { - mLoadingProcess = prg; - mPNotifier->NotifyLoading(loading_event_progress, prg); - mTimeoutStartTime = INT64_MIN; - } else if (mLoadingProcess == prg) {//15seconds loading progress not changed - int64_t curTime = af_gettime_relative() / 1000; - - if ((mTimeoutStartTime == INT64_MIN) - || (PLAYER_PLAYING != mPlayStatus)) { - mTimeoutStartTime = curTime; - } else if (curTime - mTimeoutStartTime >= mSet.timeout_ms) { - //TODO: demuxer still read crazy - if (mLowMem) { - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SYSTEM_LOW_MEMORY, "App Low memory"); - // if it's a network error or waiting for network to retry, don't notify error - } else if (mSourceListener.isConnected() && !mSourceListener.isPending()) { - // ChangePlayerStatus(PLAYER_ERROR); - // notifyErrorCallback(MEDIA_PLAYER_ERROR_LOADING_TIMEOUT, "Loading timeout"); - } - mTimeoutStartTime = curTime; - } + if (dropAudioCount > 0) { + FlushAudioPath(); + AF_LOGD("drop left aduio duration is %lld,left aduio size is %d", + mBufferController->GetPacketDuration(BUFFER_TYPE_AUDIO), mBufferController->GetPacketSize(BUFFER_TYPE_AUDIO)); + mMasterClock.setTime(lastAudioPts); } } } - return true; - } - - void SuperMediaPlayer::LiveCatchUp(int64_t delayTime) - { - int recoverGap = 50 * 1000; - - if (mSet.RTMaxDelayTime >= 1000 * 1000) { - recoverGap = 500 * 1000; - } else if (mSet.RTMaxDelayTime >= 200 * 1000) { - recoverGap = 100 * 1000; - } + int64_t lastAudio = mBufferController->GetPacketLastPTS(BUFFER_TYPE_AUDIO); - if ((delayTime > mSet.RTMaxDelayTime) && (150 * 1000 < delayTime)) { - ProcessSetSpeed(1.2); - } else if ((delayTime < mSet.RTMaxDelayTime - recoverGap) || (100 * 1000 > delayTime)) { - ProcessSetSpeed(1.0); - } - } + if ((lastAudio != INT64_MIN) && (mPlayedAudioPts != INT64_MIN)) { + int64_t delayTime = lastAudio - mPlayedAudioPts; + static int64_t lastT = af_gettime_ms(); - void SuperMediaPlayer::notifyPreparedCallback() - { - if (waitingForStart && mSet.bLooping) { - //when loop play don`t send prepare msg. - } else { - mPNotifier->NotifyPrepared(); - } + if (af_gettime_ms() - lastT > 1000) { + lastT = af_gettime_ms(); + AF_LOGD("lastAudio:%lld mPlayedAudioPts:%lld, delayTime:%lld", lastAudio, mPlayedAudioPts, delayTime); + } - if (waitingForStart || mAutoPlay) { - Start(); - waitingForStart = false; + LiveCatchUp(delayTime); } - if (mAutoPlay) { - mPNotifier->NotifyAutoPlayStart(); - } + break; } - void SuperMediaPlayer::doRender() - { - bool rendered = false; - - if (mFirstBufferFlag && mPlayStatus != PLAYER_PREPARING) { - if (HAVE_VIDEO && mAppStatus != APP_BACKGROUND) { - rendered = RenderVideo(true); - - if (rendered) { - AF_LOGD("TIMEPOS RenderVideo :%lld", mPlayedVideoPts / 1000); - mMasterClock.setTime(mPlayedVideoPts); - NotifyPosition(getCurrentPosition() / 1000); - - // seek preview can't render audio,but set the audio clock to here pts - if (HAVE_AUDIO && (mAudioTime.startTime <= 0)) { - if (!mAudioFrameQue.empty()) { - mAudioTime.startTime = mAudioFrameQue.front()->getInfo().pts; - } else { - mAudioTime.startTime = mPlayedVideoPts; - } + //check buffering status + if ((mBufferingFlag || mFirstBufferFlag)) { + if (((cur_buffer_duration > HighBufferDur || (HighBufferDur >= mSet->maxBufferDuration && mBufferIsFull)) && + (!HAVE_VIDEO || videoDecoderFull || APP_BACKGROUND == mAppStatus)) || + mEof) { + // if still in seek, wait for seek status be changed. + if (!mSeekFlag || mEof) { + if (mBufferingFlag) { + mLoadingProcess = -1; + mPNotifier->NotifyLoading(loading_event_end, 0); + AF_LOGD("loading end"); + + if (mPlayStatus == PLAYER_PLAYING) { + mMasterClock.start(); } } - } else { // audio only - if (!mAudioFrameQue.empty()) { - int64_t audioPTS = mAudioFrameQue.front()->getInfo().pts; - NotifyPosition(audioPTS / 1000); - rendered = true; - mAudioTime.startTime = audioPTS; - } - } - } - - if (mPlayStatus == PLAYER_PLAYING) { - if (!mBufferingFlag) { - rendered |= render(); - } - } - - if (rendered) { - mFirstBufferFlag = false; - //may audio already played over - if (mEof && mAudioFrameQue.empty() && mBufferController.GetPacketSize(BUFFER_TYPE_AUDIO) == 0) { - mMasterClock.setReferenceClock(nullptr, nullptr); - } - - if (mSeekFlag) { - mSeekFlag = false; - - if (!mMessageControl.findMsgByType(MSG_SEEKTO)) { - // update position when seek end. in case of when paused. - // update position before reset seek status, so getCurrentPosition return mSeekPos instead of mCurrentPos - // fix bug the mCurrentPos not accuracy - if (mPlayStatus == PLAYER_PAUSED) { - NotifyPosition(getCurrentPosition() / 1000); + mTimeoutStartTime = INT64_MIN; + mFirstBufferFlag = false; + mBufferingFlag = false; + } + } else if (cur_buffer_duration >= 0) { + // TODO: Notify when change + int prg = MIN(static_cast(cur_buffer_duration * 100 / HighBufferDur), 100); + + if (prg > mLoadingProcess && mBufferingFlag) { + mLoadingProcess = prg; + mPNotifier->NotifyLoading(loading_event_progress, prg); + mTimeoutStartTime = INT64_MIN; + } else if (mLoadingProcess == prg) {//15seconds loading progress not changed + int64_t curTime = af_gettime_relative() / 1000; + + if ((mTimeoutStartTime == INT64_MIN) || (PLAYER_PLAYING != mPlayStatus)) { + mTimeoutStartTime = curTime; + } else if (curTime - mTimeoutStartTime >= mSet->timeout_ms) { + //TODO: demuxer still read crazy + if (mLowMem) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SYSTEM_LOW_MEMORY, "App Low memory"); + // if it's a network error or waiting for network to retry, don't notify error + } else if (mSourceListener->isConnected() && !mSourceListener->isPending()) { + // ChangePlayerStatus(PLAYER_ERROR); + // notifyErrorCallback(MEDIA_PLAYER_ERROR_LOADING_TIMEOUT, "Loading timeout"); } - ResetSeekStatus(); - mPNotifier->NotifySeekEnd(mSeekInCache); - mSeekInCache = false; + + mTimeoutStartTime = curTime; } } } } - void SuperMediaPlayer::doDeCode() - { - //get video packet to decode - if (HAVE_VIDEO && !videoDecoderEOS && mVideoDecoder) { - int max_cache_size = VIDEO_PICTURE_MAX_CACHE_SIZE; - - if (mPictureCacheType == picture_cache_type_cannot) { - max_cache_size = 1; - } - - unsigned long videoFrameSize = mVideoFrameQue.size(); + return true; +} +void SuperMediaPlayer::closeVideo() +{ + AF_LOGW("close video stream"); + mDemuxerService->CloseStream(mCurrentVideoIndex); + mCurrentVideoIndex = -1; + // mVideoFrameQue.clear(); + mBufferController->ClearPacket(BUFFER_TYPE_VIDEO); + FlushVideoPath(); +} - if (videoFrameSize < max_cache_size) { - int64_t startDecodeTime = af_getsteady_ms(); - int64_t videoEarlyUs = 0; +void SuperMediaPlayer::LiveCatchUp(int64_t delayTime) +{ + int recoverGap = 50 * 1000; - do { - // if still in seeking, don't send data to decoder in background - // due to the playback position could be changed later. - if ((APP_BACKGROUND == mAppStatus) && isSeeking()) { - break; - } + if (mSet->RTMaxDelayTime >= 1000 * 1000) { + recoverGap = 500 * 1000; + } else if (mSet->RTMaxDelayTime >= 200 * 1000) { + recoverGap = 100 * 1000; + } - if (mVideoPacket == nullptr) { - mVideoPacket = mBufferController.getPacket(BUFFER_TYPE_VIDEO); - } + if ((delayTime > mSet->RTMaxDelayTime) && (150 * 1000 < delayTime)) { + mMsgCtrlListener->ProcessSetSpeed(1.2); + } else if ((delayTime < mSet->RTMaxDelayTime - recoverGap) || (100 * 1000 > delayTime)) { + mMsgCtrlListener->ProcessSetSpeed(1.0); + } +} - videoEarlyUs = mVideoPacket ? mVideoPacket->getInfo().dts - mMasterClock.GetTime() : 0; +void SuperMediaPlayer::notifyPreparedCallback() +{ + if (waitingForStart && mSet->bLooping) { + //when loop play don`t send prepare msg. + } else { + mPNotifier->NotifyPrepared(); + } - // don't send too much data when in background - if (mVideoPacket && APP_BACKGROUND == mAppStatus && videoEarlyUs > 0) { - break; - } + if (waitingForStart || mAutoPlay) { + Start(); + waitingForStart = false; + } - FillVideoFrame(); + if (mAutoPlay) { + mPNotifier->NotifyAutoPlayStart(); + } +} - if (nullptr == mVideoPacket && !mEof) { - break; - } +void SuperMediaPlayer::doRender() +{ + bool rendered = false; - if (mVideoPacket && !HAVE_AUDIO) { - if (mVideoPacket->getInfo().timePosition >= 0) { - mCurrentPos = mVideoPacket->getInfo().timePosition; - printTimePosition(mCurrentPos); - } - } + if (mFirstBufferFlag && mPlayStatus != PLAYER_PREPARING) { + if (HAVE_VIDEO && mAppStatus != APP_BACKGROUND) { + rendered = RenderVideo(true); - int ret = DecodeVideoPacket(mVideoPacket); + if (rendered) { + AF_LOGD("TIMEPOS RenderVideo :%lld", mPlayedVideoPts / 1000); - if (ret & STATUS_RETRY_IN) { - break; - } + /* + * set the position to video position tmp, audio will update the position when it rendered, + * otherwise the position will update to the old audio position when audio not reach on time after seek. + * + */ + if (mSoughtVideoPos != INT64_MIN) { + mCurrentPos = mSoughtVideoPos; + } else { + mCurrentPos = mPlayedVideoPts; + } + NotifyPosition(getCurrentPosition()); - if (af_getsteady_ms() - startDecodeTime > 50) { - break; + // seek preview can't render audio,but set the audio clock to here pts + if (HAVE_AUDIO && (mAudioTime.startTime <= 0)) { + if (!mAudioFrameQue.empty()) { + mAudioTime.startTime = mAudioFrameQue.front()->getInfo().pts; + } else { + mAudioTime.startTime = mPlayedVideoPts; } - } while ((mSeekNeedCatch || dropLateVideoFrames) && (videoEarlyUs < 200 * 1000)); + } + } + } else {// audio only + if (!mAudioFrameQue.empty()) { + int64_t audioPTS = mAudioFrameQue.front()->getInfo().pts; + NotifyPosition(audioPTS); + rendered = true; + mAudioTime.startTime = audioPTS; } } + } - //get audio packet to decode - if (HAVE_AUDIO && mAudioDecoder) { + if (mPlayStatus == PLAYER_PLAYING) { + if (!mBufferingFlag) { + rendered |= render(); + } + } - while (mAudioFrameQue.size() < 2 && !audioDecoderEOS) { + if (rendered) { + mFirstBufferFlag = false; - if (mAudioPacket == nullptr) { - mAudioPacket = mBufferController.getPacket(BUFFER_TYPE_AUDIO); - } + //may audio already played over + if (mEof && mAudioFrameQue.empty() && mBufferController->GetPacketSize(BUFFER_TYPE_AUDIO) == 0) { + mMasterClock.setReferenceClock(nullptr, nullptr); + } - if (mAudioPacket) { - int64_t timePosition = mAudioPacket->getInfo().timePosition; - int ret = DecodeAudio(mAudioPacket); + if (mSeekFlag) { + mSeekFlag = false; - if (mAudioPacket == nullptr && timePosition >= 0) { - mCurrentPos = timePosition; - //printTimePosition(mCurrentPos); - } - if (ret == -EAGAIN) { - break; - } - } else if (mEof) { - unique_ptr packet{}; - DecodeAudio(packet); - } else - break; + if (!mMessageControl->findMsgByType(MSG_SEEKTO)) { + // update position when seek end. in case of when paused. + // update position before reset seek status, so getCurrentPosition return mSeekPos instead of mCurrentPos + // fix bug the mCurrentPos not accuracy + NotifyPosition(getCurrentPosition()); + ResetSeekStatus(); + mPNotifier->NotifySeekEnd(mSeekInCache); + mSeekInCache = false; } - -// AF_LOGD("mAudioFrameQue.size is %d\n", mAudioFrameQue.size()); } } +} - void SuperMediaPlayer::checkEOS() - { - if (!mEof || PLAYER_COMPLETION == mPlayStatus) { - return; - } +void SuperMediaPlayer::doDeCode() +{ + //get video packet to decode + if (HAVE_VIDEO && !videoDecoderEOS && mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)) { + int max_cache_size = VIDEO_PICTURE_MAX_CACHE_SIZE; - //in case of ONLY AUDIO stream. - if ((HAVE_VIDEO && !videoDecoderEOS && (APP_BACKGROUND != mAppStatus)) - || (HAVE_AUDIO && !audioDecoderEOS)) { - return; + if (mPictureCacheType == picture_cache_type_cannot) { + max_cache_size = 1; } - int packetSize = mBufferController.GetPacketSize(BUFFER_TYPE_VIDEO); - int frameSize = static_cast(mAudioFrameQue.size()); + unsigned long videoFrameSize = mVideoFrameQue.size(); - if ((APP_BACKGROUND != mAppStatus)) { - frameSize += mVideoFrameQue.size(); - packetSize += mBufferController.GetPacketSize(BUFFER_TYPE_AUDIO); - } + if (videoFrameSize < max_cache_size) { + int64_t startDecodeTime = af_getsteady_ms(); + int64_t videoEarlyUs = 0; - if (frameSize > 0 || packetSize > 0) { - AF_TRACE; - return; - } + do { + if (mCanceled) { + break; + } + // if still in seeking, don't send data to decoder in background + // due to the playback position could be changed later. + if ((APP_BACKGROUND == mAppStatus) && isSeeking()) { + break; + } - if (mAudioRender != nullptr) { - uint64_t audioQueDuration = mAudioRender->getQueDuration(); + if (mVideoPacket == nullptr) { + mVideoPacket = mBufferController->getPacket(BUFFER_TYPE_VIDEO); + } - if (audioQueDuration != 0) { - AF_TRACE; -//work around: xiaomi 5X 7.1.2 audioTrack getPosition always is 0 when seek to end - int64_t now = af_getsteady_ms(); + videoEarlyUs = mVideoPacket ? mVideoPacket->getInfo().dts - mMasterClock.GetTime() : 0; - if (mCheckAudioQueEOSTime == INT64_MIN || mAudioQueDuration != audioQueDuration) { - mCheckAudioQueEOSTime = now; - mAudioQueDuration = audioQueDuration; + // don't send too much data when in background + if (mVideoPacket && APP_BACKGROUND == mAppStatus && videoEarlyUs > 0) { + break; } - if ((now - mCheckAudioQueEOSTime) * 1000 <= audioQueDuration) { - return; - } - } - } + FillVideoFrame(); - NotifyPosition(mDuration / 1000); + if (nullptr == mVideoPacket && !mEof) { + break; + } - if (mSet.bLooping && mDuration > 0) { - mSeekPos = 0; //19644161: need reset seek position - ProcessSeekToMsg(0, false); - mPNotifier->NotifyLoopStart(); - NotifyPosition(0); - } else { - if (mPlayStatus != PLAYER_COMPLETION) { - if (APP_BACKGROUND == mAppStatus) { - FlushVideoPath(); - mBufferController.ClearPacket(BUFFER_TYPE_VIDEO); + if (mVideoPacket && (!HAVE_AUDIO || audioDecoderEOS)) { + if (!(mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)->getFlags() & DECFLAG_PASSTHROUGH_INFO) && + mVideoPacket->getInfo().timePosition >= 0) { + mCurrentPos = mVideoPacket->getInfo().timePosition; + //printTimePosition(mCurrentPos); + } } - mPNotifier->NotifyCompletion(); - ChangePlayerStatus(PLAYER_COMPLETION); - mUtil.reset(); - } - } - } + int ret = DecodeVideoPacket(mVideoPacket); - int SuperMediaPlayer::DecodeVideoPacket(unique_ptr &pVideoPacket) - { - int ret = 0; + if (ret & STATUS_RETRY_IN) { + break; + } - if (videoDecoderEOS) { - return ret; + if (af_getsteady_ms() - startDecodeTime > 50) { + break; + } + } while ((mSeekNeedCatch || dropLateVideoFrames) && (videoEarlyUs < 200 * 1000)); } + } - int64_t pos = getCurrentPosition(); + //get audio packet to decode + if (HAVE_AUDIO && mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_AUDIO)) { - if (pVideoPacket != nullptr) { - // for cache video, or seeking accurate, check whether drop output frame - if (mSeekNeedCatch || dropLateVideoFrames) { - int64_t checkPos = mSeekNeedCatch ? mSeekPos.load() : pos; + while (mAudioFrameQue.size() < 2 && !audioDecoderEOS && !mCanceled) { - // only decode and don't need output to render if too old - if ((pVideoPacket->getInfo().timePosition < checkPos) - && (pVideoPacket->getInfo().timePosition < mDuration - 200 * 1000)) { - pVideoPacket->setDiscard(true); - } + if (mAudioPacket == nullptr) { + mAudioPacket = mBufferController->getPacket(BUFFER_TYPE_AUDIO); } - ret = mVideoDecoder->send_packet(pVideoPacket, 0); + if (mAudioPacket) { + int64_t timePosition = mAudioPacket->getInfo().timePosition; + int ret = DecodeAudio(mAudioPacket); + if (mAudioPacket == nullptr && timePosition >= 0 && + !(mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_AUDIO)->getFlags() & DECFLAG_PASSTHROUGH_INFO)) { + mCurrentPos = timePosition; + //printTimePosition(mCurrentPos); + } + if (ret == -EAGAIN) { + break; + } + } else if (mEof) { + unique_ptr packet{}; + DecodeAudio(packet); + } else + break; - // don't need pop if need retry later - if (!(ret & STATUS_RETRY_IN)) { - // mBufferController.PopFrontPacket(BUFFER_TYPE_VIDEO); - assert(pVideoPacket == nullptr); + int64_t duration = mBufferController->GetPacketDuration(BUFFER_TYPE_AUDIO); + if (duration < 0 && !mAudioFrameQue.empty()) { + //If audio duration is unknow when demux , update duration after decode one frame. + IAFFrame::AFFrameInfo frameInfo = mAudioFrameQue.front()->getInfo(); + int64_t packetDuration = (int64_t) frameInfo.audio.nb_samples * 1000000 / frameInfo.audio.sample_rate; + mBufferController->SetOnePacketDuration(BUFFER_TYPE_AUDIO, packetDuration); } - } else if (mEof) { - mVideoDecoder->setEOF(); - mVideoDecoder->send_packet(pVideoPacket, 0); - ret = 0; } - if (ret > 0) { - bool haveError = false; + // AF_LOGD("mAudioFrameQue.size is %d\n", mAudioFrameQue.size()); + } +} - if (ret & STATUS_HAVE_ERROR) { - if (mVideoDecoder->get_error_frame_no() > MAX_DECODE_ERROR_FRAME) { - haveError = true; - } - } +void SuperMediaPlayer::checkEOS() +{ + if (!mEof || PLAYER_COMPLETION == mPlayStatus) { + return; + } -// if (ret & STATUS_CREATE_FAIL) { -// haveError = true; -// } + //in case of ONLY AUDIO stream. + if ((HAVE_VIDEO && mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_VIDEO) && !videoDecoderEOS && + (APP_BACKGROUND != mAppStatus)) || + (HAVE_AUDIO && !audioDecoderEOS)) { + return; + } - if (haveError) { - ChangePlayerStatus(PLAYER_ERROR); - mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DECODE_VIDEO, "video decode error"); - } - } + int packetSize = mBufferController->GetPacketSize(BUFFER_TYPE_AUDIO); + int frameSize = static_cast(mAudioFrameQue.size()); - return ret; + if ((APP_BACKGROUND != mAppStatus) && mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)) { + frameSize += mVideoFrameQue.size(); + packetSize += mBufferController->GetPacketSize(BUFFER_TYPE_VIDEO); } - int SuperMediaPlayer::FillVideoFrame() - { - int64_t pos = getCurrentPosition(); - unique_ptr pFrame{}; - int ret = mVideoDecoder->getFrame(pFrame, 0); + if (frameSize > 0 || packetSize > 0) { + AF_TRACE; + return; + } - if (ret == STATUS_EOS) { - videoDecoderEOS = true; - } + uint64_t audioQueDuration = mAVDeviceManager->getAudioRenderQueDuration(); - if (pFrame != nullptr) { - mVideoDecoder->clean_error(); - int64_t pts = pFrame->getInfo().pts; + if (audioQueDuration != 0) { + AF_TRACE; + //work around: xiaomi 5X 7.1.2 audioTrack getPosition always is 0 when seek to end + int64_t now = af_getsteady_ms(); - if (mSeekFlag && mSeekNeedCatch) { - mSeekNeedCatch = false; - } + if (mCheckAudioQueEOSTime == INT64_MIN || mAudioQueDuration != audioQueDuration) { + mCheckAudioQueEOSTime = now; + mAudioQueDuration = audioQueDuration; + } + + if ((now - mCheckAudioQueEOSTime) * 1000 <= audioQueDuration) { + return; + } + } - Stream_meta *meta = (Stream_meta *) (mCurrentVideoMeta.get()); + NotifyPosition(mDuration); + playCompleted(); +} - if (meta->displayWidth > 0 && meta->displayHeight > 0) { - pFrame->getInfo().video.dar = 1.0 * meta->displayWidth / meta->displayHeight; - } else { - //if not get displaywidth/height , set dar with width/height - pFrame->getInfo().video.dar = 1.0 * pFrame->getInfo().video.width / pFrame->getInfo().video.height; +void SuperMediaPlayer::playCompleted() +{ + if (mSet->bLooping && mDuration > 0) { + mSeekPos = 0;//19644161: need reset seek position + mMsgCtrlListener->ProcessSeekToMsg(0, false); + mPNotifier->NotifyLoopStart(); + NotifyPosition(0); + } else { + if (mPlayStatus != PLAYER_COMPLETION) { + if (APP_BACKGROUND == mAppStatus) { + FlushVideoPath(); + mBufferController->ClearPacket(BUFFER_TYPE_VIDEO); } - mDemuxerService->SetOption("FRAME_DECODED", pts); -// AF_LOGI("DecodeVideoPacket p_dec_delay frame :%lld pos:%lld, mPlayedAudioPts:%lld, posdiff:%lld audiodiff:%lld videodiff:%lld", -// pFrame->GetPts()/1000, pos/1000, mPlayedAudioPts/1000, (pos - pFrame->GetPts())/1000, -// (mLastInputAudio - mPlayedAudioPts)/1000, (mLastInputVideo - pFrame->GetPts())/1000); - mVideoFrameQue.push(move(pFrame)); - videoDecoderFull = true; + mPNotifier->NotifyCompletion(); + ChangePlayerStatus(PLAYER_COMPLETION); + mUtil->reset(); } + } +} +int SuperMediaPlayer::DecodeVideoPacket(unique_ptr &pVideoPacket) +{ + int ret = 0; + + if (videoDecoderEOS) { return ret; } - bool SuperMediaPlayer::render() - { - //send to audio render - bool audioRendered = false; - bool videoRendered = false; - - if ((mCurrentAudioIndex >= 0) && !mSeekNeedCatch) { - int ret; - do { - ret = RenderAudio(); - if (RENDER_NONE != ret) { - audioRendered = true; - } - } while (ret == RENDER_FULL); - } + int64_t pos = getCurrentPosition(); - if (HAVE_VIDEO) { - videoRendered = RenderVideo(false); - } + if (pVideoPacket != nullptr) { + // for cache video, or seeking accurate, check whether drop output frame + if (mSeekNeedCatch || dropLateVideoFrames) { + int64_t checkPos = mSeekNeedCatch ? mSeekPos.load() : pos; - if (HAVE_VIDEO && HAVE_AUDIO) { - // TODO: do it in reset() - if (!PTS_REVERTING) { - if (videoRendered && audioRendered) { - mVideoPtsRevert = mAudioPtsRevert = false; - } - } else { - AF_LOGW("PTS_REVERTING audio pts is %lld ,video pts is %lld\n", mPlayedAudioPts, mPlayedVideoPts); + // only decode and don't need output to render if too old + if ((pVideoPacket->getInfo().timePosition < checkPos) && (pVideoPacket->getInfo().timePosition < mDuration - 200 * 1000)) { + pVideoPacket->setDiscard(true); } - } else { - mVideoPtsRevert = mAudioPtsRevert = false; } - if ((HAVE_SUBTITLE || mSubPlayer) && !mSeekFlag) { - RenderSubtitle(mCurVideoPts); + if (!mRecorderSet->decodeFirstVideoFrameInfo.isFirstPacketSendToDecoder) { + DecodeFirstFrameInfo& info = mRecorderSet->decodeFirstVideoFrameInfo; + info.isFirstPacketSendToDecoder = true; + info.firstPacketSize = pVideoPacket->getSize(); + info.firstPacketPts = pVideoPacket->getInfo().pts; + info.waitFirstFrame = true; + info.sendFirstPacketTimeMs = af_getsteady_ms(); } - return audioRendered || videoRendered; + ret = mAVDeviceManager->sendPacket(pVideoPacket, SMPAVDeviceManager::DEVICE_TYPE_VIDEO, 0); + // don't need pop if need retry later + if (!(ret & STATUS_RETRY_IN)) { + // mBufferController->PopFrontPacket(BUFFER_TYPE_VIDEO); + assert(pVideoPacket == nullptr); + } + } else if (mEof) { + // mVideoDecoder->setEOF(); + mAVDeviceManager->sendPacket(pVideoPacket, SMPAVDeviceManager::DEVICE_TYPE_VIDEO, 0); + ret = 0; } - RENDER_RESULT SuperMediaPlayer::RenderAudio() - { - RENDER_RESULT ret = RENDER_NONE; - int64_t pts = INT64_MIN; - int64_t duration = INT64_MIN; - int render_ret; -//#define DUMP_PCM -#ifdef DUMP_PCM - static int fd = open("out.pcm", O_CREAT | O_RDWR, 0666); -#endif + if (ret > 0) { + bool haveError = false; - if (mAudioFrameQue.empty()) { - if (audioDecoderEOS && mAudioRender->getQueDuration() == 0) { - mMasterClock.setReferenceClock(nullptr, nullptr); + if (ret & STATUS_HAVE_ERROR) { + if (mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)->get_error_frame_no() > MAX_DECODE_ERROR_FRAME) { + haveError = true; } - return ret; } - pts = mAudioFrameQue.front()->getInfo().pts; + // if (ret & STATUS_CREATE_FAIL) { + // haveError = true; + // } - if (pts == INT64_MIN) { - mAudioFrameQue.pop_front(); - return ret; + if (haveError) { + ChangePlayerStatus(PLAYER_ERROR); + mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DECODE_VIDEO, "video decode error"); } + } + + return ret; +} - auto *avafFrame = dynamic_cast(mAudioFrameQue.front().get()); +int SuperMediaPlayer::FillVideoFrame() +{ + int64_t pos = getCurrentPosition(); + unique_ptr pFrame{}; + int ret = mAVDeviceManager->getFrame(pFrame, SMPAVDeviceManager::DEVICE_TYPE_VIDEO, 0); - if (avafFrame) { - duration = getPCMFrameDuration(avafFrame->ToAVFrame()); + if (ret == STATUS_EOS) { + videoDecoderEOS = true; + + if (mSeekFlag && mSeekNeedCatch) { + mSeekNeedCatch = false; } + } - if (mFrameCb && !mSecretPlayBack) { - mFrameCb(mFrameCbUserData, avafFrame); + if (pFrame != nullptr) { + + if(mRecorderSet->decodeFirstVideoFrameInfo.waitFirstFrame) { + DecodeFirstFrameInfo& info = mRecorderSet->decodeFirstVideoFrameInfo; + info.getFirstFrameTimeMs = af_getsteady_ms(); + info.waitFirstFrame = false; } - render_ret = mAudioRender->renderFrame(mAudioFrameQue.front(), 0); + mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)->clean_error(); - if (render_ret == IAudioRender::FORMAT_NOT_SUPPORT) { - if (mAudioRender->getQueDuration() == 0) { - std::lock_guard uMutex(mCreateMutex); - mAudioRender = nullptr; - mAudioTime.startTime = mAudioFrameQue.front()->getInfo().pts; - mAudioTime.deltaTimeTmp = 0; - mAudioTime.deltaTime = 0; - mLastAudioFrameDuration = -1; - setUpAudioRender(mAudioFrameQue.front()->getInfo().audio); - if (mBRendingStart) { - mAudioRender->pause(false); - } - mAudioRender->renderFrame(mAudioFrameQue.front(), 0); - } + if (mSecretPlayBack) { + pFrame->setProtect(true); } + int64_t pts = pFrame->getInfo().pts; - if (mAudioFrameQue.front() == nullptr) { - mAudioFrameQue.pop_front(); - ret = RENDER_FULL; - } else { - return ret; + if (mSeekFlag && mSeekNeedCatch) { + mSeekNeedCatch = false; } -#ifdef DUMP_PCM - write(fd, buffer + mWriteAudioLen, len); -#endif + Stream_meta *meta = (Stream_meta *) (mCurrentVideoMeta.get()); - if (!HAVE_VIDEO) { - checkFirstRender(); + if (meta->displayWidth > 0 && meta->displayHeight > 0) { + pFrame->getInfo().video.dar = 1.0 * meta->displayWidth / meta->displayHeight; + } else { + //if not get displaywidth/height , set dar with width/height + pFrame->getInfo().video.dar = 1.0 * pFrame->getInfo().video.width / pFrame->getInfo().video.height; } - if (mPlayedAudioPts == INT64_MIN) { - mAudioTime.startTime = pts; - mAudioTime.deltaTime = 0; - mAudioTime.deltaTimeTmp = 0; - mMasterClock.setReferenceClock(getAudioPlayTimeStampCB, this); - } else { - if (mLastAudioFrameDuration > 0) { - int64_t offset = pts - (mPlayedAudioPts + mLastAudioFrameDuration); + mDemuxerService->SetOption("FRAME_DECODED", pts); + // AF_LOGI("DecodeVideoPacket p_dec_delay frame :%lld pos:%lld, mPlayedAudioPts:%lld, posdiff:%lld audiodiff:%lld videodiff:%lld", + // pFrame->GetPts()/1000, pos/1000, mPlayedAudioPts/1000, (pos - pFrame->GetPts())/1000, + // (mLastInputAudio - mPlayedAudioPts)/1000, (mLastInputVideo - pFrame->GetPts())/1000); + mVideoFrameQue.push(move(pFrame)); + videoDecoderFull = true; + } - if (llabs(offset) > 200000) { - AF_LOGW("offset is %lld,pts is %lld", offset, pts); - } + return ret; +} - mAudioTime.deltaTimeTmp += offset; +bool SuperMediaPlayer::render() +{ + //send to audio render + bool audioRendered = false; + bool videoRendered = false; - if (llabs(mAudioTime.deltaTimeTmp) > 100000) { - AF_LOGW("correct audio and master clock offset is %lld, frameDuration :%lld", mAudioTime.deltaTimeTmp, - mLastAudioFrameDuration); - mAudioTime.deltaTime += mAudioTime.deltaTimeTmp; - mAudioTime.deltaTimeTmp = 0; - } + if ((mCurrentAudioIndex >= 0) && !mSeekNeedCatch) { + int ret; + do { + ret = RenderAudio(); + if (RENDER_NONE != ret) { + audioRendered = true; } - } + } while (ret == RENDER_FULL); + } - if (!mAudioPtsRevert) { - mAudioPtsRevert = mPlayedAudioPts != INT64_MIN && pts < mPlayedAudioPts - PTS_DISCONTINUE_DELTA; + if (HAVE_VIDEO) { + videoRendered = RenderVideo(false); + } - if (mAudioPtsRevert) { - AF_LOGI("PTS_REVERTING audio start\n"); + if (HAVE_VIDEO && HAVE_AUDIO) { + // TODO: do it in reset() + if (!PTS_REVERTING) { + if (videoRendered && audioRendered) { + mVideoPtsRevert = mAudioPtsRevert = false; } + } else { + AF_LOGW("PTS_REVERTING audio pts is %lld ,video pts is %lld\n", mPlayedAudioPts, mPlayedVideoPts); } + } else { + mVideoPtsRevert = mAudioPtsRevert = false; + } - mPlayedAudioPts = pts; - mLastAudioFrameDuration = duration; + if ((HAVE_SUBTITLE || mSubPlayer) && !mSeekFlag) { + RenderSubtitle(mCurVideoPts); + } - if (mAudioChangedFirstPts == pts && !mMixMode) { - StreamInfo *info = GetCurrentStreamInfo(ST_TYPE_AUDIO); - mPNotifier->NotifyStreamChanged(info, ST_TYPE_AUDIO); - AF_LOGD("audio changed\n"); - mAudioChangedFirstPts = INT64_MIN; - } + return audioRendered || videoRendered; +} - return ret; - } +RENDER_RESULT SuperMediaPlayer::RenderAudio() +{ + RENDER_RESULT ret = RENDER_NONE; + int64_t pts = INT64_MIN; + int64_t duration = INT64_MIN; + int64_t position = INT64_MIN; + int render_ret; +//#define DUMP_PCM +#ifdef DUMP_PCM + static int fd = open("out.pcm", O_CREAT | O_RDWR, 0666); +#endif - bool SuperMediaPlayer::RenderVideo(bool force_render) - { - //send to video render - if (mVideoFrameQue.empty()) { - return false; + if (mAudioFrameQue.empty()) { + if (audioDecoderEOS && mAVDeviceManager->getAudioRenderQueDuration() == 0) { + mMasterClock.setReferenceClock(nullptr, nullptr); } + return ret; + } - unique_ptr &videoFrame = mVideoFrameQue.front(); + pts = mAudioFrameQue.front()->getInfo().pts; + position = mAudioFrameQue.front()->getInfo().timePosition; - if (videoFrame == nullptr) { - return false; - } + if (pts == INT64_MIN) { + mAudioFrameQue.pop_front(); + return ret; + } - int64_t videoPts = videoFrame->getInfo().pts; + auto *avafFrame = dynamic_cast(mAudioFrameQue.front().get()); - // work around for huaweiP20 pro hardware decode get pts = INT64_MIN when change resolution. - if (videoPts == INT64_MIN && videoPts < mPlayedVideoPts) { - videoPts = mPlayedVideoPts + 1; - } + if (avafFrame) { + duration = getPCMFrameDuration(avafFrame->ToAVFrame()); + } - int frameWidth = videoFrame->getInfo().video.width; - int frameHeight = videoFrame->getInfo().video.height; - int frameRotate = videoFrame->getInfo().video.rotate; + if (mFrameCb && (!mSecretPlayBack || mDrmKeyValid)) { + mFrameCb(mFrameCbUserData, avafFrame); + } - if (!mVideoPtsRevert) { - mVideoPtsRevert = mPlayedVideoPts != INT64_MIN && videoPts < mPlayedVideoPts - PTS_DISCONTINUE_DELTA; + render_ret = mAVDeviceManager->renderAudioFrame(mAudioFrameQue.front(), 0); - if (mVideoPtsRevert) { - AF_LOGI("PTS_REVERTING video start\n"); + if (render_ret == IAudioRender::FORMAT_NOT_SUPPORT) { + if (mAVDeviceManager->getAudioRenderQueDuration() == 0) { + std::lock_guard uMutex(mCreateMutex); + mAudioTime.startTime = mAudioFrameQue.front()->getInfo().pts; + mAudioTime.deltaTimeTmp = 0; + mAudioTime.deltaTime = 0; + mLastAudioFrameDuration = -1; + setUpAudioRender(mAudioFrameQue.front()->getInfo().audio); + if (mBRendingStart) { + mAVDeviceManager->pauseAudioRender(false); } + mAVDeviceManager->renderAudioFrame(mAudioFrameQue.front(), 0); } - - // audio pts first revert to small, force render the old video frame - if (PTS_REVERTING && mAudioPtsRevert && videoPts - PTS_DISCONTINUE_DELTA > mPlayedAudioPts) { - AF_LOGI("PTS_REVERTING force render the old video frame"); - force_render = true; - // video pts first revert to small,the new video data wait audio pts to revert - } else if (PTS_REVERTING && mVideoPtsRevert && videoPts + PTS_DISCONTINUE_DELTA < mPlayedAudioPts) { - AF_LOGI("PTS_REVERTING wait audio to revert"); - return false; + } else if (render_ret == IAudioRender::OPEN_AUDIO_DEVICE_FAILED) { + AF_LOGE("render audio failed due to can not open device, close audio stream"); + mDemuxerService->CloseStream(mCurrentAudioIndex); + mCurrentAudioIndex = -1; + mMasterClock.setReferenceClock(nullptr, nullptr); + mAudioFrameQue.clear(); + mBufferController->ClearPacket(BUFFER_TYPE_AUDIO); + if (HAVE_VIDEO) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_OPEN_AUDIO_DEVICE_FAILED, "open audio device failed"); + } else { + ChangePlayerStatus(PLAYER_ERROR); + mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_RENDER_AUDIO_OPEN_DEVICE_FAILED, "open audio device failed"); + return ret; } + } - int64_t masterPlayedTime = mMasterClock.GetTime(); - int64_t videoLateUs = masterPlayedTime - videoPts;// if videoLateUs > 0, video is late - - /* + if (mAudioFrameQue.size() > 0 && mAudioFrameQue.front() == nullptr) { + mAudioFrameQue.pop_front(); + ret = RENDER_FULL; + } else { + return ret; + } + +#ifdef DUMP_PCM + write(fd, buffer + mWriteAudioLen, len); +#endif + + if (!HAVE_VIDEO) { + checkFirstRender(); + } + + if (mPlayedAudioPts == INT64_MIN) { + mAudioTime.startTime = pts; + mAudioTime.deltaTime = 0; + mAudioTime.deltaTimeTmp = 0; + mMasterClock.setReferenceClock(getAudioPlayTimeStampCB, this); + } else { + if (mLastAudioFrameDuration > 0) { + int64_t offset = pts - (mPlayedAudioPts + mLastAudioFrameDuration); + + /* + * the mLastAudioFrameDuration and the pts are all not the accurate value, + * the mLastAudioFrameDuration accurate for 1/1000000 s, + * the pts maybe accurate for 1/1000 s (eg. flv file), so can't increase the deltaTimeTmp when + * offset little than 1ms. + */ + if (llabs(offset) > mCATimeBase) { + AF_LOGW("offset is %lld,pts is %lld", offset, pts); + mAudioTime.deltaTimeTmp += offset; + } + + if (llabs(mAudioTime.deltaTimeTmp) > 100000) { + AF_LOGW("correct audio and master clock offset is %lld, frameDuration :%lld", mAudioTime.deltaTimeTmp, + mLastAudioFrameDuration); + mAudioTime.deltaTime += mAudioTime.deltaTimeTmp; + mAudioTime.deltaTimeTmp = 0; + } + } + } + + if (!mAudioPtsRevert) { + mAudioPtsRevert = mPlayedAudioPts != INT64_MIN && pts < mPlayedAudioPts - mPtsDiscontinueDelta; + + if (mAudioPtsRevert) { + AF_LOGI("PTS_REVERTING audio start\n"); + } + } + + if (mPlayedAudioPts == INT64_MIN && isSeeking()) { + // update after send first frame in seeking, because audio render callback is async. + // sometimes notify position before audio rendered callback , will cause position not right. + mCurrentPos = position; + } + + mPlayedAudioPts = pts; + mLastAudioFrameDuration = duration; + + if (mAudioChangedFirstPts == pts && !mMixMode) { + StreamInfo *info = GetCurrentStreamInfo(ST_TYPE_AUDIO); + mPNotifier->NotifyStreamChanged(info, ST_TYPE_AUDIO); + AF_LOGD("audio changed\n"); + mAudioChangedFirstPts = INT64_MIN; + } + + return ret; +} + +bool SuperMediaPlayer::RenderVideo(bool force_render) +{ + + if (!mAVDeviceManager->isVideoRenderValid()) { + return false; + } + //send to video render + if (mVideoFrameQue.empty()) { + return false; + } + + unique_ptr &videoFrame = mVideoFrameQue.front(); + + if (videoFrame == nullptr) { + return false; + } + + int64_t videoPts = videoFrame->getInfo().pts; + + // work around for huaweiP20 pro hardware decode get pts = INT64_MIN when change resolution. + if (videoPts == INT64_MIN && videoPts < mPlayedVideoPts) { + videoPts = mPlayedVideoPts + 1; + } + + int frameWidth = videoFrame->getInfo().video.width; + int frameHeight = videoFrame->getInfo().video.height; + videoFrame->getInfo().video.rotate = mVideoRotation; + + if (!mVideoPtsRevert) { + mVideoPtsRevert = mPlayedVideoPts != INT64_MIN && videoPts < mPlayedVideoPts - mPtsDiscontinueDelta; + + if (mVideoPtsRevert) { + AF_LOGI("PTS_REVERTING video start\n"); + } + } + + // audio pts first revert to small, force render the old video frame + if (PTS_REVERTING && mAudioPtsRevert && videoPts - mPtsDiscontinueDelta > mPlayedAudioPts) { + AF_LOGI("PTS_REVERTING force render the old video frame"); + force_render = true; + // video pts first revert to small,the new video data wait audio pts to revert + } else if (PTS_REVERTING && mVideoPtsRevert && videoPts + mPtsDiscontinueDelta < mPlayedAudioPts) { + AF_LOGI("PTS_REVERTING wait audio to revert"); + return false; + } + + int64_t masterPlayedTime = mMasterClock.GetTime(); + int64_t videoLateUs = masterPlayedTime - videoPts;// if videoLateUs > 0, video is late + + /* * if stc is free, video rectify it */ - if ((llabs(videoLateUs) > 1000 * 1000) - || (llabs(videoLateUs) > mSet.maxBufferDuration)) { - // don't have master, or master not in valid status - if (!mMasterClock.haveMaster() || !mMasterClock.isMasterValid()) { - mMasterClock.setTime(videoPts); - masterPlayedTime = videoPts; - } + if ((llabs(videoLateUs) > 1000 * 1000) || (llabs(videoLateUs) > mSet->maxBufferDuration)) { + // don't have master, or master not in valid status + if (!mMasterClock.haveMaster() || !mMasterClock.isMasterValid()) { + mMasterClock.setTime(videoPts); + masterPlayedTime = videoPts; } + } - /* video early more than 10ms, don't (render||drop) it, deal it next time + /* video early more than 10ms, don't (render||drop) it, deal it next time * if the pts is not continue drop it -------------------early----------------------------|------------------------late-------------- @@ -2082,2164 +2180,1629 @@ namespace Cicada { */ - bool render = force_render; + bool render = force_render; - if (!force_render) { - if (videoLateUs < -10 * 1000 && videoLateUs > -PTS_DISCONTINUE_DELTA) { - return false; - } + if (!force_render) { + if (videoLateUs < -10 * 1000 && videoLateUs > -mPtsDiscontinueDelta) { + return false; + } - // To avoid video frame packet overload when video decoder can't catch up. - if (videoLateUs >= 500 * 1000 && !PTS_REVERTING) { - int64_t lastVideoKeyPts = mBufferController.GetKeyPTSBefore(BUFFER_TYPE_VIDEO, masterPlayedTime); + // To avoid video frame packet overload when video decoder can't catch up. + if (videoLateUs >= 500 * 1000 && !PTS_REVERTING) { + int64_t lastVideoKeyPts = mBufferController->GetKeyPTSBefore(BUFFER_TYPE_VIDEO, masterPlayedTime); - if (lastVideoKeyPts != INT64_MIN) { - int64_t dropVideoCount = mBufferController.ClearPacketBeforePts(BUFFER_TYPE_VIDEO, lastVideoKeyPts); + if (lastVideoKeyPts != INT64_MIN) { + int64_t dropVideoCount = mBufferController->ClearPacketBeforePts(BUFFER_TYPE_VIDEO, lastVideoKeyPts); - if (dropVideoCount > 0) { - FlushVideoPath(); - AF_LOGD("videolaterUs is %lld,drop video count is %d", videoLateUs, dropVideoCount); - return false; - } + if (dropVideoCount > 0) { + FlushVideoPath(); + AF_LOGD("videolaterUs is %lld,drop video count is %d", videoLateUs, dropVideoCount); + return false; } } + } - if (dropLateVideoFrames) { - if (videoLateUs > 10 * 1000) { - render = false; - } else { - dropLateVideoFrames = false; - } - } else if (videoLateUs < 500 * 1000) { // video early litter than 10ms or late litter than 500ms,render it - render = true; - } - // Try to render if already haven't render more then 60ms - else if (INT64_MIN == mPlayedVideoPts || (videoPts - mPlayedVideoPts) > 60 * 1000) { - render = true; + if (dropLateVideoFrames) { + if (videoLateUs > 10 * 1000) { + render = false; + } else { + dropLateVideoFrames = false; } + } else if (videoLateUs < 500 * 1000) {// video early litter than 10ms or late litter than 500ms,render it + render = true; } + // Try to render if already haven't render more then 60ms + else if (INT64_MIN == mPlayedVideoPts || (videoPts - mPlayedVideoPts) > 60 * 1000) { + render = true; + } + } - if (render) { - SendVideoFrameToRender(move(videoFrame)); - - if (frameWidth != mVideoWidth || frameHeight != mVideoHeight) { - mVideoWidth = frameWidth; - mVideoHeight = frameHeight; - mVideoRotation = frameRotate; - mPNotifier->NotifyVideoSizeChanged(mVideoWidth, mVideoHeight); - } + if (render) { + SendVideoFrameToRender(move(videoFrame)); - if (!HAVE_AUDIO) { - if (mPlayedVideoPts == INT64_MIN) { - mMasterClock.setTime(videoPts); - mMasterClock.setReferenceClock(mClockRef, mCRArg); - } - } - } else { - AF_LOGW("drop frame,master played time is %lld,video pts is %lld\n", masterPlayedTime, videoPts); - videoFrame->setDiscard(true); + if (frameWidth != mVideoWidth || frameHeight != mVideoHeight) { + mVideoWidth = frameWidth; + mVideoHeight = frameHeight; + mPNotifier->NotifyVideoSizeChanged(mVideoWidth, mVideoHeight); + } - if (mFrameCb && !mSecretPlayBack) { - mFrameCb(mFrameCbUserData, videoFrame.get()); + if (!HAVE_AUDIO) { + if (mPlayedVideoPts == INT64_MIN) { + mMasterClock.setTime(videoPts); + mMasterClock.setReferenceClock(mClockRef, mCRArg); } - VideoRenderCallback(this, videoPts, nullptr); } + } else { + AF_LOGW("drop frame,master played time is %lld,video pts is %lld\n", masterPlayedTime, videoPts); + videoFrame->setDiscard(true); - mPlayedVideoPts = videoPts; - mVideoFrameQue.pop(); - return render; + if (mFrameCb && (!mSecretPlayBack || mDrmKeyValid)) { + mFrameCb(mFrameCbUserData, videoFrame.get()); + } + VideoRenderCallback(this, videoPts, false, nullptr); } - void SuperMediaPlayer::RenderSubtitle(int64_t pts) - { - if (mSubPlayer && mSubPlayer->isActive()) { - mSubPlayer->update(pts); - return; - } + mPlayedVideoPts = videoPts; + mVideoFrameQue.pop(); + return render; +} - auto iter = mSubtitleShowedQueue.begin(); +void SuperMediaPlayer::RenderSubtitle(int64_t pts) +{ + if (mSubPlayer && mSubPlayer->isActive()) { + mSubPlayer->update(getCurrentPosition()); + return; + } - while (iter != mSubtitleShowedQueue.end()) { - if ((*iter).get()) { - if (((*iter)->getInfo().pts + (*iter)->getInfo().duration) <= pts) { - mPNotifier->NotifySubtitleEvent(subTitle_event_hide, (*iter).release(), 0, nullptr); - iter = mSubtitleShowedQueue.erase(iter); - continue; - } - } + auto iter = mSubtitleShowedQueue.begin(); - iter++; + while (iter != mSubtitleShowedQueue.end()) { + if ((*iter).get()) { + if (((*iter)->getInfo().pts + (*iter)->getInfo().duration) <= pts) { + mPNotifier->NotifySubtitleEvent(subTitle_event_hide, (*iter).release(), 0, nullptr); + iter = mSubtitleShowedQueue.erase(iter); + continue; + } } - int64_t subTitlePts = mBufferController.GetPacketPts(BUFFER_TYPE_SUBTITLE); + iter++; + } - if (subTitlePts == INT64_MIN || subTitlePts > pts) { - return; - } + int64_t subTitlePts = mBufferController->GetPacketPts(BUFFER_TYPE_SUBTITLE); - unique_ptr pFrame = mBufferController.getPacket(BUFFER_TYPE_SUBTITLE); + if (subTitlePts == INT64_MIN || subTitlePts > pts) { + return; + } - if (pFrame == nullptr) { - return; - } + unique_ptr pFrame = mBufferController->getPacket(BUFFER_TYPE_SUBTITLE); - bool changed = false; + if (pFrame == nullptr) { + return; + } - if (pFrame->getInfo().pts + pFrame->getInfo().duration < pts) { - AF_LOGD("read subtitle pts is long before pts is %lld,subtitle pts is %lld", pts, subTitlePts); - mBufferController.ClearPacketBeforePts(BUFFER_TYPE_SUBTITLE, subTitlePts); + bool changed = false; - if (subTitlePts <= mSubtitleChangedFirstPts && pts > mSubtitleChangedFirstPts) { - changed = true; - } - } else { - if (subTitlePts == mSubtitleChangedFirstPts) { - changed = true; - } + if (pFrame->getInfo().pts + pFrame->getInfo().duration < pts) { + AF_LOGD("read subtitle pts is long before pts is %lld,subtitle pts is %lld", pts, subTitlePts); + mBufferController->ClearPacketBeforePts(BUFFER_TYPE_SUBTITLE, subTitlePts); - AF_LOGD("read subtitle pts show pts is %lld,subtitle pts is %lld", pts, subTitlePts); - pFrame->getInfo().dts = mSubtitleShowIndex++; -// pFrame->pBuffer[pFrame->size] = 0; - mPNotifier->NotifySubtitleEvent(subTitle_event_show, pFrame.get(), 0, nullptr); - mSubtitleShowedQueue.push_back(move(pFrame)); + if (subTitlePts <= mSubtitleChangedFirstPts && pts > mSubtitleChangedFirstPts) { + changed = true; } - - if (changed) { - AF_LOGD("subtitle changed"); - StreamInfo *info = GetCurrentStreamInfo(ST_TYPE_SUB); - mPNotifier->NotifyStreamChanged(info, ST_TYPE_SUB); - mSubtitleChangedFirstPts = INT64_MIN; + } else { + if (subTitlePts == mSubtitleChangedFirstPts) { + changed = true; } + + AF_LOGD("read subtitle pts show pts is %lld,subtitle pts is %lld", pts, subTitlePts); + pFrame->getInfo().dts = mSubtitleShowIndex++; + // pFrame->pBuffer[pFrame->size] = 0; + mPNotifier->NotifySubtitleEvent(subTitle_event_show, pFrame.get(), 0, nullptr); + mSubtitleShowedQueue.push_back(move(pFrame)); } - void SuperMediaPlayer::OnTimer(int64_t curTime) - { - if (mPlayedAudioPts != INT64_MIN || mPlayedVideoPts != INT64_MIN) { - updateLoopGap(); + if (changed) { + AF_LOGD("subtitle changed"); + StreamInfo *info = GetCurrentStreamInfo(ST_TYPE_SUB); + mPNotifier->NotifyStreamChanged(info, ST_TYPE_SUB); + mSubtitleChangedFirstPts = INT64_MIN; + } +} - /* +void SuperMediaPlayer::OnTimer(int64_t curTime) +{ + if (mPlayedAudioPts != INT64_MIN || mPlayedVideoPts != INT64_MIN) { + /* * if have seek not completed,DO NOT update the position,it will lead process bar * jumping */ - if ((mPlayStatus == PLAYER_PLAYING) && - !isSeeking()) { - int64_t pos = getCurrentPosition() / 1000; - //AF_LOGD("TIMEPOS OnTimer :%lld", pos); - NotifyPosition(pos); - } - - PostBufferPositionMsg(); + if ((mPlayStatus == PLAYER_PLAYING) && !isSeeking()) { + //AF_LOGD("TIMEPOS OnTimer :%lld", getCurrentPosition()); + NotifyPosition(getCurrentPosition()); } + + PostBufferPositionMsg(); } + mPNotifier->NotifyCurrentDownloadSpeed(mUtil->getCurrentDownloadSpeed()); +} - void SuperMediaPlayer::SendVideoFrameToRender(unique_ptr frame, bool valid) - { - if (mFrameCb && !mSecretPlayBack) { - bool rendered = mFrameCb(mFrameCbUserData, frame.get()); - if (rendered) { - VideoRenderCallback(this, frame->getInfo().pts, nullptr); - return; - } +void SuperMediaPlayer::SendVideoFrameToRender(unique_ptr frame, bool valid) +{ + if (mFrameCb && (!mSecretPlayBack || mDrmKeyValid)) { + bool rendered = mFrameCb(mFrameCbUserData, frame.get()); + if (rendered) { + VideoRenderCallback(this, frame->getInfo().pts, true, nullptr); + return; } - if (mVideoRender) { - int ret = mVideoRender->renderFrame(frame); + } + if (mAVDeviceManager->isVideoRenderValid()) { + int ret = mAVDeviceManager->renderVideoFrame(frame); - if (ret < 0) { - AF_LOGE("renderFrame error \n"); - // for windows init failed, which may need change render type in future. - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_RENDER_INIT_ERROR, "init video render failed"); - } + if (ret < 0) { + AF_LOGE("renderFrame error \n"); + // for windows init failed, which may need change render type in future. + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_RENDER_INIT_ERROR, "init video render failed"); } + } else { + assert(0); + //render directly + VideoRenderCallback(this, frame->getInfo().pts, !frame->getDiscard(), nullptr); } +} - int SuperMediaPlayer::DecodeAudio(unique_ptr &pPacket) - { - if (mAudioDecoder == nullptr) { - return -EINVAL; - } +int SuperMediaPlayer::DecodeAudio(unique_ptr &pPacket) +{ + if (audioDecoderEOS) { + return 0; + } - if (audioDecoderEOS) { - return 0; - } + unique_ptr frame{}; + int ret; - unique_ptr frame{}; - int ret; + do { + ret = mAVDeviceManager->getFrame(frame, SMPAVDeviceManager::DEVICE_TYPE_AUDIO, 0); + if (ret == STATUS_EOS) { + audioDecoderEOS = true; + break; + } - do { - ret = mAudioDecoder->getFrame(frame, 0); + if (frame != nullptr) { - if (ret == STATUS_EOS) { - audioDecoderEOS = true; - break; + if(mRecorderSet->decodeFirstAudioFrameInfo.waitFirstFrame) { + DecodeFirstFrameInfo &info = mRecorderSet->decodeFirstAudioFrameInfo; + info.getFirstFrameTimeMs = af_getsteady_ms(); + info.waitFirstFrame = false; } - if (frame != nullptr) { - if (frame->getInfo().pts == INT64_MIN) { - // TODO: why mAudioFrameQue.back()->getInfo().pts is INT64_MIN - if (!mAudioFrameQue.empty() && mAudioFrameQue.back()->getInfo().pts != INT64_MIN) { - double duration = ((double) frame->getInfo().audio.nb_samples) / frame->getInfo().audio.sample_rate; - frame->getInfo().pts = mAudioFrameQue.back()->getInfo().pts + duration * 1000000; - } else { - // assert(0); - } + if (mSecretPlayBack) { + frame->setProtect(true); + } + if (frame->getInfo().pts == INT64_MIN) { + // TODO: why mAudioFrameQue.back()->getInfo().pts is INT64_MIN + if (!mAudioFrameQue.empty() && mAudioFrameQue.back()->getInfo().pts != INT64_MIN) { + double duration = ((double) frame->getInfo().audio.nb_samples) / frame->getInfo().audio.sample_rate; + frame->getInfo().pts = mAudioFrameQue.back()->getInfo().pts + duration * 1000000; + } else { + // assert(0); } - - mAudioFrameQue.push_back(move(frame)); } - } while (ret != -EAGAIN); - ret = mAudioDecoder->send_packet(pPacket, 0); + mAudioFrameQue.push_back(move(frame)); + } + } while (ret != -EAGAIN && ret != -EINVAL); - if (ret > 0) { - bool haveError = false; + if (!mRecorderSet->decodeFirstAudioFrameInfo.isFirstPacketSendToDecoder) { + DecodeFirstFrameInfo& info = mRecorderSet->decodeFirstAudioFrameInfo; + info.isFirstPacketSendToDecoder = true; + info.waitFirstFrame = true; + info.firstPacketSize = pPacket->getSize(); + info.firstPacketPts = pPacket->getInfo().pts; + info.sendFirstPacketTimeMs = af_getsteady_ms(); + } - if (ret & STATUS_HAVE_ERROR) { - if (mAudioDecoder->get_error_frame_no() > MAX_DECODE_ERROR_FRAME) { - haveError = true; - } - } + ret = mAVDeviceManager->sendPacket(pPacket, SMPAVDeviceManager::DEVICE_TYPE_AUDIO, 0); - if (ret & STATUS_CREATE_FAIL) { + if (ret > 0) { + bool haveError = false; + + if (ret & STATUS_HAVE_ERROR) { + if (mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_AUDIO)->get_error_frame_no() > MAX_DECODE_ERROR_FRAME) { haveError = true; } + } - if (ret &= STATUS_RETRY_IN) { - ret = -EAGAIN; - } + if (ret & STATUS_CREATE_FAIL) { + haveError = true; + } - if (haveError) { - ChangePlayerStatus(PLAYER_ERROR); - mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DECODE_AUDIO, "audio decode error"); - } + if (ret &= STATUS_RETRY_IN) { + ret = -EAGAIN; } - return ret; + if (haveError) { + ChangePlayerStatus(PLAYER_ERROR); + mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DECODE_AUDIO, "audio decode error"); + } } - void SuperMediaPlayer::ProcessOpenStreamInit(int streamIndex) - { - AF_LOGD("ProcessOpenStreamInit ProcessOpenStreamInit start"); - int streamCount = (int) mStreamInfoQueue.size(); - int videoStreams = 0; + return ret; +} - for (int i = 0; i < streamCount; i++) { - StreamInfo *info = mStreamInfoQueue[i]; +void SuperMediaPlayer::ProcessOpenStreamInit(int streamIndex) +{ + AF_LOGD("ProcessOpenStreamInit ProcessOpenStreamInit start"); + int streamCount = (int) mStreamInfoQueue.size(); + int videoStreams = 0; - if (info->type == ST_TYPE_VIDEO) { - videoStreams++; - } - } + for (int i = 0; i < streamCount; i++) { + StreamInfo *info = mStreamInfoQueue[i]; - if (videoStreams > 1) { - mAdaptiveVideo = true; - } else { - mAdaptiveVideo = false; + if (info->type == ST_TYPE_VIDEO) { + videoStreams++; } + } - if (mMixMode) { - if (mCurrentVideoIndex < 0 && mCurrentAudioIndex < 0) { - unique_ptr pMeta; - Stream_meta *meta{}; - int nbSubStream = mDemuxerService->GetNbSubStream(mMainStreamId); - - for (int j = 0; j < nbSubStream; j++) { - mDemuxerService->GetStreamMeta(pMeta, GEN_STREAM_ID(mMainStreamId, j), true); - meta = (Stream_meta *) (pMeta.get()); - AF_LOGD("get a stream %d\n", meta->type); - - if (!mSet.bDisableVideo && meta->type == STREAM_TYPE_VIDEO && mCurrentVideoIndex < 0 && - meta->height > 0 && - meta->attached_pic == 0) { - AF_LOGD("get a video stream\n"); - mCurrentVideoIndex = GEN_STREAM_ID(mMainStreamId, j); - mVideoInterlaced = meta->interlaced; - } else if (!mSet.bDisableAudio && meta->type == STREAM_TYPE_AUDIO && mCurrentAudioIndex < 0 && - meta->channels > 0) { - AF_LOGD("get a audio stream\n"); - mCurrentAudioIndex = GEN_STREAM_ID(mMainStreamId, j); - } else if (meta->type == STREAM_TYPE_SUB && mCurrentSubtitleIndex < 0) { - AF_LOGD("get a subtitle stream\n"); - mCurrentSubtitleIndex = GEN_STREAM_ID(mMainStreamId, j); - } + if (videoStreams > 1) { + mAdaptiveVideo = true; + } else { + mAdaptiveVideo = false; + } + + if (mMixMode) { + if (mCurrentVideoIndex < 0 && mCurrentAudioIndex < 0) { + unique_ptr pMeta; + Stream_meta *meta{}; + int nbSubStream = mDemuxerService->GetNbSubStream(mMainStreamId); + + for (int j = 0; j < nbSubStream; j++) { + mDemuxerService->GetStreamMeta(pMeta, GEN_STREAM_ID(mMainStreamId, j), true); + meta = (Stream_meta *) (pMeta.get()); + AF_LOGD("get a stream %d\n", meta->type); + + if (!mSet->bDisableVideo && meta->type == STREAM_TYPE_VIDEO && mCurrentVideoIndex < 0 && meta->height > 0 && + meta->attached_pic == 0) { + AF_LOGD("get a video stream\n"); + mCurrentVideoIndex = GEN_STREAM_ID(mMainStreamId, j); + mVideoInterlaced = meta->interlaced; + updateVideoMeta(); + } else if (!mSet->bDisableAudio && meta->type == STREAM_TYPE_AUDIO && mCurrentAudioIndex < 0 && meta->channels > 0) { + AF_LOGD("get a audio stream\n"); + mCurrentAudioIndex = GEN_STREAM_ID(mMainStreamId, j); + mCATimeBase = meta->ptsTimeBase; + } else if (meta->type == STREAM_TYPE_SUB && mCurrentSubtitleIndex < 0) { + AF_LOGD("get a subtitle stream\n"); + mCurrentSubtitleIndex = GEN_STREAM_ID(mMainStreamId, j); } } } + } - mPNotifier->CancelNotifyStreamInfo(); - //post before PostMsg(PreparedReq). sdk will get these infos on Prepared callback. - delete[] mStreamInfos; - mStreamInfos = new StreamInfo *[mStreamInfoQueue.size()]; - int infoIndex = 0; - - for (StreamInfo *info : mStreamInfoQueue) { - mStreamInfos[infoIndex++] = info; - } + mPNotifier->CancelNotifyStreamInfo(); + //post before PostMsg(PreparedReq). sdk will get these infos on Prepared callback. + delete[] mStreamInfos; + mStreamInfos = new StreamInfo *[mStreamInfoQueue.size()]; + int infoIndex = 0; - mPNotifier->NotifyStreamInfo(mStreamInfos, static_cast(mStreamInfoQueue.size())); + for (StreamInfo *info : mStreamInfoQueue) { + mStreamInfos[infoIndex++] = info; } - void SuperMediaPlayer::setUpAVPath() - { - if (!mInited) { - return; - } - - if (!mSet.mFastStart && mPlayStatus < PLAYER_PLAYING) { - AF_LOGI("not fast start mode\n"); - return; - } + mPNotifier->NotifyStreamInfo(mStreamInfos, static_cast(mStreamInfoQueue.size())); +} - if (mCurrentAudioIndex >= 0 && mAudioDecoder == nullptr) { - AF_LOGD("SetUpAudioPath start"); - int ret = SetUpAudioPath(); +void SuperMediaPlayer::setUpAVPath() +{ + if (!mInited) { + return; + } - if (ret < 0) { - AF_LOGE("%s SetUpAudioPath failed,url is %s %s", __FUNCTION__, mSet.url.c_str(), framework_err2_string(ret)); - mDemuxerService->CloseStream(mCurrentAudioIndex); - mCurrentAudioIndex = -1; - } else { - } - } + if (!mSet->mFastStart && mPlayStatus < PLAYER_PLAYING) { + AF_LOGI("not fast start mode\n"); + return; + } - if (mCurrentVideoIndex >= 0) { - int ret = SetUpVideoPath(); + if (mCurrentAudioIndex >= 0 && + (!mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_AUDIO) || !mAVDeviceManager->isAudioRenderValid())) { + AF_LOGD("SetUpAudioPath start"); + int ret = SetUpAudioPath(); - if (ret < 0) { - AF_LOGE("%s SetUpVideoPath failed,url is %s %s", __FUNCTION__, mSet.url.c_str(), framework_err2_string(ret)); - mDemuxerService->CloseStream(mCurrentVideoIndex); - mCurrentVideoIndex = -1; - } + if (ret < 0) { + AF_LOGE("%s SetUpAudioPath failed,url is %s %s", __FUNCTION__, mSet->url.c_str(), framework_err2_string(ret)); + mDemuxerService->CloseStream(mCurrentAudioIndex); + mCurrentAudioIndex = -1; + mCATimeBase = 0; + } else { } + } - if (mCurrentVideoIndex < 0 && mCurrentAudioIndex < 0) { - ChangePlayerStatus(PLAYER_ERROR); - mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DEMUXER_NO_VALID_STREAM, "No valid stream"); + if (mCurrentVideoIndex >= 0) { + int ret = SetUpVideoPath(); + + if (ret < 0) { + AF_LOGE("%s SetUpVideoPath failed,url is %s %s", __FUNCTION__, mSet->url.c_str(), framework_err2_string(ret)); + closeVideo(); } } - bool SuperMediaPlayer::NeedDrop(int64_t pts, int64_t refer) - { - return (pts < refer) && (pts < mDuration - 200 * 1000); + if (mCurrentVideoIndex < 0 && mCurrentAudioIndex < 0) { + ChangePlayerStatus(PLAYER_ERROR); + mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DEMUXER_NO_VALID_STREAM, "No valid stream"); } +} - int SuperMediaPlayer::ReadPacket() - { - IAFPacket *pFrame = nullptr; - std::unique_ptr pMedia_Frame{}; +bool SuperMediaPlayer::NeedDrop(int64_t pts, int64_t refer) +{ + return (pts < refer) && (pts < mDuration - 200 * 1000); +} - if (mDemuxerService == nullptr) { - assert(0); - } +int SuperMediaPlayer::ReadPacket() +{ + IAFPacket *pFrame = nullptr; + std::unique_ptr pMedia_Frame{}; + + if (mDemuxerService == nullptr) { + assert(0); + } - int index = -1; + int index = -1; - if (HAVE_SUBTITLE && !mSubtitleEOS) { - if (mBufferController.GetPacketDuration(BUFFER_TYPE_SUBTITLE) <= 0) { - if (mSubtitleChangedFirstPts != INT64_MIN || 1) { - index = mCurrentSubtitleIndex; - } + if (HAVE_SUBTITLE && !mSubtitleEOS) { + if (mBufferController->GetPacketDuration(BUFFER_TYPE_SUBTITLE) <= 0) { + if (mSubtitleChangedFirstPts != INT64_MIN || 1) { + index = mCurrentSubtitleIndex; } } + } - int ret = mDemuxerService->readPacket(pMedia_Frame, index); + int ret = mDemuxerService->readPacket(pMedia_Frame, index); - if (pMedia_Frame == nullptr) { - // AF_LOGD("Can't read packet %d\n", ret); - if (ret == 0) { - mSubtitleEOS = true; + if (pMedia_Frame == nullptr) { + // AF_LOGD("Can't read packet %d\n", ret); + if (ret == 0) { + mSubtitleEOS = true; - if (index != -1) { - ret = -EAGAIN; - } + if (index != -1) { + ret = -EAGAIN; } - - return ret; } - // FIXME: transfer to frame - if (pMedia_Frame->isProtected() && !mSecretPlayBack) { - AF_LOGI("SecretPlayBack\n"); - mSecretPlayBack = true; + return ret; + } + + if (mPtsDiscontinueDelta == INT64_MIN) { + int64_t maxGopTimeUs = mDemuxerService->getDemuxerHandle()->getMaxGopTimeUs(); + if (maxGopTimeUs > 0) { + mPtsDiscontinueDelta = maxGopTimeUs; + } else { + mPtsDiscontinueDelta = PTS_DISCONTINUE_DELTA; } + AF_LOGI("mPtsDiscontinueDelta = %lld", mPtsDiscontinueDelta); + } - pFrame = pMedia_Frame.get(); - mUtil.notifyRead(MediaPlayerUtil::readEvent_Got); + // FIXME: transfer to frame + if (pMedia_Frame->isProtected() && !mSecretPlayBack) { + AF_LOGI("SecretPlayBack\n"); + mSecretPlayBack = true; - // TODO: get the min first stream pts - if (pFrame->getInfo().timePosition >= 0 && mMediaStartPts == INT64_MIN - && pFrame->getInfo().streamIndex != mCurrentSubtitleIndex - && pFrame->getInfo().streamIndex != mWillChangedSubtitleStreamIndex) { - mMediaStartPts = pFrame->getInfo().pts - pFrame->getInfo().timePosition; + if (!pMedia_Frame->getMagicKey().empty() && pMedia_Frame->getMagicKey() == mSet->drmMagicKey){ + mDrmKeyValid = true; } + } - if (mSeekFlag && mSeekNeedCatch) { - if (pFrame->getInfo().timePosition < (mSeekPos - mSet.maxASeekDelta)) { - // first frame is far away from seek position, don't suppport accurate seek - mSeekNeedCatch = false; - } - } + pFrame = pMedia_Frame.get(); + mUtil->notifyRead(MediaPlayerUtil::readEvent_Got, pFrame->getSize()); - int id = GEN_STREAM_INDEX(pFrame->getInfo().streamIndex); + // TODO: get the min first stream pts + if (pFrame->getInfo().timePosition >= 0 && mMediaStartPts == INT64_MIN && pFrame->getInfo().streamIndex != mCurrentSubtitleIndex && + pFrame->getInfo().streamIndex != mWillChangedSubtitleStreamIndex) { + mMediaStartPts = pFrame->getInfo().pts - pFrame->getInfo().timePosition; + } - if (mDuration < 0) { - unique_ptr pMeta; - mDemuxerService->GetStreamMeta(pMeta, pFrame->getInfo().streamIndex, false); - mDuration = ((Stream_meta *) (pMeta.get()))->duration; + if (mSeekFlag && mSeekNeedCatch) { + if (pFrame->getInfo().timePosition < (mSeekPos - mSet->maxASeekDelta)) { + // first frame is far away from seek position, don't suppport accurate seek + mSeekNeedCatch = false; } + } - if (id < mStreamInfoQueue.size() - && mStreamInfoQueue[id]->type == ST_TYPE_VIDEO - && mMainStreamId != -1 && id != mMainStreamId) { - unique_ptr pMeta; - Stream_meta *meta{}; - int count = mDemuxerService->GetNbSubStream(id); + int id = GEN_STREAM_INDEX(pFrame->getInfo().streamIndex); - for (int i = 0; i < count; i++) { - mDemuxerService->GetStreamMeta(pMeta, GEN_STREAM_ID(id, i), true); - meta = (Stream_meta *) (pMeta.get()); - int streamId = GEN_STREAM_ID(id, i); - - if (meta->type == STREAM_TYPE_VIDEO && meta->height > 0 && streamId != mCurrentVideoIndex) { - mWillChangedVideoStreamIndex = streamId; - } else if (meta->type == STREAM_TYPE_AUDIO && meta->channels > 0 && streamId != mCurrentAudioIndex) { - mWillChangedAudioStreamIndex = streamId; - } else if (meta->type == STREAM_TYPE_SUB && streamId != mCurrentSubtitleIndex) { - mWillChangedSubtitleStreamIndex = streamId; - } - } + if (mDuration < 0) { + unique_ptr pMeta; + mDemuxerService->GetStreamMeta(pMeta, pFrame->getInfo().streamIndex, false); + mDuration = ((Stream_meta *) (pMeta.get()))->duration; + } - mMainStreamId = id; - } + if (id < mStreamInfoQueue.size() && mStreamInfoQueue[id]->type == ST_TYPE_VIDEO && mMainStreamId != -1 && id != mMainStreamId) { + unique_ptr pMeta; + Stream_meta *meta{}; + int count = mDemuxerService->GetNbSubStream(id); + + for (int i = 0; i < count; i++) { + mDemuxerService->GetStreamMeta(pMeta, GEN_STREAM_ID(id, i), true); + meta = (Stream_meta *) (pMeta.get()); + int streamId = GEN_STREAM_ID(id, i); - if (!mInited) { - ProcessOpenStreamInit(pFrame->getInfo().streamIndex); - mInited = true; + if (meta->type == STREAM_TYPE_VIDEO && meta->height > 0 && streamId != mCurrentVideoIndex) { + mWillChangedVideoStreamIndex = streamId; + } else if (meta->type == STREAM_TYPE_AUDIO && meta->channels > 0 && streamId != mCurrentAudioIndex) { + mWillChangedAudioStreamIndex = streamId; + mWATimeBase = meta->ptsTimeBase; + } else if (meta->type == STREAM_TYPE_SUB && streamId != mCurrentSubtitleIndex) { + mWillChangedSubtitleStreamIndex = streamId; + } } -// AF_LOGD("read packet pts is %lld,streamIndex is %d duration is %d\n", pFrame->getInfo().pts, pFrame->getInfo().streamIndex, -// pFrame->getInfo().duration); + mMainStreamId = id; + } + + if (!mInited) { + ProcessOpenStreamInit(pFrame->getInfo().streamIndex); + ProcessUpdateView(); + mInited = true; + } + + // AF_LOGD("read packet pts is %lld,streamIndex is %d duration is %d\n", pFrame->getInfo().pts, pFrame->getInfo().streamIndex, + // pFrame->getInfo().duration); - if (pFrame->getInfo().streamIndex == mCurrentVideoIndex || - pFrame->getInfo().streamIndex == mWillChangedVideoStreamIndex) { + if (pFrame->getInfo().streamIndex == mCurrentVideoIndex || pFrame->getInfo().streamIndex == mWillChangedVideoStreamIndex) { - // FIXME: return non slice nal only when protected packet - if (mMediaFrameCb) { - // TODO: change to std::unique_ptr - mMediaFrameCb(mMediaFrameCbArg, pMedia_Frame, ST_TYPE_VIDEO); - } + if (mMediaFrameCb && (!pMedia_Frame->isProtected() || mDrmKeyValid)) { + mMediaFrameCb(mMediaFrameCbArg, pMedia_Frame.get(), ST_TYPE_VIDEO); + } - mBufferController.AddPacket(move(pMedia_Frame), BUFFER_TYPE_VIDEO); - mDemuxerService->SetOption("FRAME_RECEIVE", pFrame->getInfo().pts); + mBufferController->AddPacket(move(pMedia_Frame), BUFFER_TYPE_VIDEO); + mDemuxerService->SetOption("FRAME_RECEIVE", pFrame->getInfo().pts); - if (mVideoInterlaced == InterlacedType_UNKNOWN) { - if (mVideoParser == nullptr) { -//FIXME mCurrentVideoMeta can not be update here. - mDemuxerService->GetStreamMeta(mCurrentVideoMeta, pFrame->getInfo().streamIndex, false); - auto *meta = (Stream_meta *) (mCurrentVideoMeta.get()); - mVideoParser = new bitStreamParser(); - mVideoParser->init(meta); - } + if (mVideoInterlaced == InterlacedType_UNKNOWN) { + if (mVideoParser == nullptr) { + //FIXME mCurrentVideoMeta can not be update here. + mDemuxerService->GetStreamMeta(mCurrentVideoMeta, pFrame->getInfo().streamIndex, false); + auto *meta = (Stream_meta *) (mCurrentVideoMeta.get()); + mVideoParser = new bitStreamParser(); + mVideoParser->init(meta); + } - mVideoParser->parser(pFrame->getData(), static_cast(pFrame->getSize())); - mVideoInterlaced = mVideoParser->getInterlaced(); + mVideoParser->parser(pFrame->getData(), static_cast(pFrame->getSize())); + mVideoInterlaced = mVideoParser->getInterlaced(); - if (mVideoInterlaced != InterlacedType_UNKNOWN) { + if (mVideoInterlaced != InterlacedType_UNKNOWN) { + delete mVideoParser; + mVideoParser = nullptr; + } else { + mVideoParserTimes++; + + if (mVideoParserTimes > 10) { + mVideoInterlaced = InterlacedType_NO; delete mVideoParser; mVideoParser = nullptr; - } else { - mVideoParserTimes++; - - if (mVideoParserTimes > 10) { - mVideoInterlaced = InterlacedType_NO; - delete mVideoParser; - mVideoParser = nullptr; - mVideoParserTimes = 0; - } + mVideoParserTimes = 0; } } + } - if (mFirstVideoPts == INT64_MIN) { - mFirstVideoPts = pFrame->getInfo().pts - pFrame->getInfo().timePosition; + if (mFirstVideoPts == INT64_MIN) { + mFirstVideoPts = pFrame->getInfo().pts - pFrame->getInfo().timePosition; - if (!HAVE_AUDIO) { - mFirstSeekStartTime = pFrame->getInfo().timePosition; - } + if (!HAVE_AUDIO) { + mFirstSeekStartTime = pFrame->getInfo().timePosition; } + } - if (mSoughtVideoPos == INT64_MIN) { + if (mSoughtVideoPos == INT64_MIN) { + if (mSeekNeedCatch) { + mSoughtVideoPos = mSeekPos; + } else { mSoughtVideoPos = pFrame->getInfo().timePosition; + } - /* + /* * seek would clean the packet which have ExtraData in decoder queue, * so add the ExtraData after seek on key frame */ - if (mAdaptiveVideo && pFrame->getInfo().flags) { - unique_ptr pMeta; - mDemuxerService->GetStreamMeta(pMeta, pFrame->getInfo().streamIndex, false); - pFrame->setExtraData(((Stream_meta *) (*pMeta))->extradata, ((Stream_meta *) (*pMeta))->extradata_size); - } - } - - //this time video bitrate changed - if (mVideoChangedFirstPts == INT64_MAX && pFrame->getInfo().streamIndex == mWillChangedVideoStreamIndex) { - AF_LOGD("video stream first pts is %lld", pFrame->getInfo().pts); - mVideoChangedFirstPts = pFrame->getInfo().pts; + if (/*mAdaptiveVideo &&*/ pFrame->getInfo().flags) { unique_ptr pMeta; - mDemuxerService->GetStreamMeta(pMeta, mWillChangedVideoStreamIndex, false); + mDemuxerService->GetStreamMeta(pMeta, pFrame->getInfo().streamIndex, false); pFrame->setExtraData(((Stream_meta *) (*pMeta))->extradata, ((Stream_meta *) (*pMeta))->extradata_size); - mCurrentVideoIndex = mWillChangedVideoStreamIndex; - mWillChangedVideoStreamIndex = -1; - } - } else if (pFrame->getInfo().streamIndex == mCurrentAudioIndex || pFrame->getInfo().streamIndex == mWillChangedAudioStreamIndex) { - // printTimePosition(pFrame->getInfo().timePosition); - if (mFirstAudioPts == INT64_MIN) { - mFirstAudioPts = pFrame->getInfo().pts - pFrame->getInfo().timePosition; - mFirstSeekStartTime = pFrame->getInfo().timePosition; - } - - if (mSeekFlag && mSeekNeedCatch && NeedDrop(pFrame->getInfo().timePosition, mSeekPos)) { - return ret; - } - - if (pFrame->getInfo().streamIndex == mWillChangedAudioStreamIndex) { - mCurrentAudioIndex = mWillChangedAudioStreamIndex; - mWillChangedAudioStreamIndex = -1; - } - - if (mAudioChangedFirstPts == INT64_MAX) { - int64_t playedTime = mMasterClock.GetTime(); - - if (pFrame->getInfo().pts < playedTime) { - return ret; - } else { - //recodr 64MAX for audio stream changed for first frame - mAudioChangedFirstPts = pFrame->getInfo().pts; - } - } - - //TODO : cache depends on this callback. need find another way - if (mMediaFrameCb /*&& !pMedia_Frame->isProtected()*/) { - // TODO: change to std::unique_ptr - mMediaFrameCb(mMediaFrameCbArg, pMedia_Frame, ST_TYPE_AUDIO); - } - - mBufferController.AddPacket(move(pMedia_Frame), BUFFER_TYPE_AUDIO); - } else if (pFrame->getInfo().streamIndex == mCurrentSubtitleIndex || - pFrame->getInfo().streamIndex == mWillChangedSubtitleStreamIndex) { - //TODO : cache depends on this callback. need find another way - if (mMediaFrameCb /*&& !pMedia_Frame->isProtected()*/) { - // TODO: change to std::unique_ptr - mMediaFrameCb(mMediaFrameCbArg, pMedia_Frame, ST_TYPE_SUB); - } - - mBufferController.AddPacket(move(pMedia_Frame), BUFFER_TYPE_SUBTITLE); - AF_LOGD("read subtitle pts is %lld", pFrame->getInfo().pts); - - if (pFrame->getInfo().streamIndex == mWillChangedSubtitleStreamIndex) { - mCurrentSubtitleIndex = mWillChangedSubtitleStreamIndex; - mWillChangedSubtitleStreamIndex = -1; - } - - if (mSubtitleChangedFirstPts == INT64_MAX) { - mSubtitleChangedFirstPts = pFrame->getInfo().pts; - } - } else { - AF_LOGD("unknown stream %x, read packet pts is %lld\n", pFrame->getInfo().streamIndex, - pFrame->getInfo().pts); - } - - if (mWillSwitchVideo) { - int videoCount = 0; - int64_t startTime = mBufferController.FindSeamlessPointTimePosition(BUFFER_TYPE_VIDEO, videoCount); - - if (startTime == 0 || videoCount < 40) { - return ret; - } - - if (mMixMode) { - int64_t startTimeA = mBufferController.FindSeamlessPointTimePosition(BUFFER_TYPE_AUDIO, videoCount); - - if (startTimeA == 0 || videoCount < 40) { - return ret; - } - - startTime = std::max(startTime, startTimeA); - } - - SwitchVideo(startTime); - mWillSwitchVideo = false; - } - - return ret; - } - - void SuperMediaPlayer::printTimePosition(int64_t time) const - { - if (time > 0) { - time /= 1000; - int timeM = static_cast((time / 1000) / 60); - int timeS = static_cast((time / 1000) % 60); - int timeMS = static_cast(time - timeM * 60000 - timeS * 1000); - AF_LOGD("timePosition is %d:%d:%d\n", timeM, timeS, timeMS); - } - } - - void SuperMediaPlayer::FlushAudioPath() - { - if (mAudioRender) { - mAudioRender->flush(); - } - - if (mAudioDecoder != nullptr) { - mAudioDecoder->flush(); - } - - audioDecoderEOS = false; - - //flush frame queue - while (!mAudioFrameQue.empty()) { - mAudioFrameQue.pop_front(); - } - - mPlayedAudioPts = INT64_MIN; - mAudioPtsRevert = false; - mAudioTime.startTime = 0; - mAudioTime.deltaTime = 0; - mAudioTime.deltaTimeTmp = 0; - mAudioPacket = nullptr; - } - - void SuperMediaPlayer::FlushVideoPath() - { - if (mVideoRender) { - unique_ptr frame{nullptr}; - mVideoRender->renderFrame(frame); - } - - if (mVideoDecoder) { - mVideoDecoder->flush(); - } - - videoDecoderEOS = false; - //flush frame queue - - while (!mVideoFrameQue.empty()) { - ProcessVideoRenderedMsg(mVideoFrameQue.front()->getInfo().pts, af_getsteady_ms(), nullptr); - mVideoFrameQue.pop(); - } - - mPlayedVideoPts = INT64_MIN; - mCurVideoPts = INT64_MIN; - videoDecoderFull = false; - mVideoPtsRevert = false; - mVideoPacket = nullptr; - dropLateVideoFrames = false; - } - - void SuperMediaPlayer::FlushSubtitleInfo() - { - while (!mSubtitleShowedQueue.empty()) { - if (mSubtitleShowedQueue.front()) { - mPNotifier->NotifySubtitleEvent(subTitle_event_hide, mSubtitleShowedQueue.front().release(), 0, nullptr); } - - mSubtitleShowedQueue.pop_front(); } - mSubtitleShowedQueue.clear(); - mSubtitleShowIndex = 0; - mSubtitleEOS = false; - } - - void SuperMediaPlayer::PostBufferPositionMsg() - { - if (mPlayStatus == PLAYER_PAUSED || mPlayStatus == PLAYER_PLAYING) { - int64_t duration; - - if (isSeeking()) { - duration = 0; - } else { - duration = getPlayerBufferDuration(false); - } - - if (duration >= 0) { - mBufferPosition = getCurrentPosition() + duration; - - if (mEof) { - mBufferPosition = mDuration; - } - - mPNotifier->NotifyBufferPosition((mBufferPosition <= mDuration ? mBufferPosition : mDuration) / 1000); - } + //this time video bitrate changed + if (mVideoChangedFirstPts == INT64_MAX && pFrame->getInfo().streamIndex == mWillChangedVideoStreamIndex) { + AF_LOGD("video stream first pts is %lld", pFrame->getInfo().pts); + mVideoChangedFirstPts = pFrame->getInfo().pts; + unique_ptr pMeta; + mDemuxerService->GetStreamMeta(pMeta, mWillChangedVideoStreamIndex, false); + pFrame->setExtraData(((Stream_meta *) (*pMeta))->extradata, ((Stream_meta *) (*pMeta))->extradata_size); + mCurrentVideoIndex = mWillChangedVideoStreamIndex; + mWillChangedVideoStreamIndex = -1; } - } - - int64_t SuperMediaPlayer::getPlayerBufferDuration(bool gotMax) - { - int64_t durations[3] = {-1, -1, -1}; - int i = 0; - int64_t duration = -1; - - if (HAVE_VIDEO) { - int64_t &duration_c = durations[i++]; - duration_c = mBufferController.GetPacketDuration(BUFFER_TYPE_VIDEO); - -// AF_LOGD("videoDuration is %lld\n",videoDuration); - if (duration_c < 0 && !HAVE_AUDIO) { - duration_c = - mBufferController.GetPacketLastPTS(BUFFER_TYPE_VIDEO) - - mBufferController.GetPacketPts(BUFFER_TYPE_VIDEO); - - if (duration_c <= 0) { - duration_c = (int64_t) mBufferController.GetPacketSize(BUFFER_TYPE_VIDEO) * 40 * 1000; - } - } - if (mDemuxerService && mDemuxerService->getDemuxerHandle()) { - duration_c += mDemuxerService->getDemuxerHandle()->getBufferDuration(mCurrentVideoIndex); - } + } else if (pFrame->getInfo().streamIndex == mCurrentAudioIndex || pFrame->getInfo().streamIndex == mWillChangedAudioStreamIndex) { + // printTimePosition(pFrame->getInfo().timePosition); + if (mFirstAudioPts == INT64_MIN) { + mFirstAudioPts = pFrame->getInfo().pts - pFrame->getInfo().timePosition; + mFirstSeekStartTime = pFrame->getInfo().timePosition; } - if (HAVE_AUDIO) { - int64_t &duration_c = durations[i++]; - duration_c = mBufferController.GetPacketDuration(BUFFER_TYPE_AUDIO); -// AF_LOGD("audioDuration is %lld\n",audioDuration); - if (mDemuxerService && mDemuxerService->getDemuxerHandle()) { - duration_c += mDemuxerService->getDemuxerHandle()->getBufferDuration(mCurrentAudioIndex); - } + if (mSeekFlag && mSeekNeedCatch && NeedDrop(pFrame->getInfo().timePosition, mSeekPos)) { + return ret; } - /* - * Do not let player loading when switching subtitle, we'll read subtitle first - * in ReadPacket() - */ - if (HAVE_SUBTITLE && !mSubtitleEOS && mSubtitleChangedFirstPts == INT64_MIN) { - int64_t &duration_c = durations[i++]; - duration_c = mBufferController.GetPacketDuration(BUFFER_TYPE_SUBTITLE); - if (mDemuxerService && mDemuxerService->getDemuxerHandle()) { - duration_c += mDemuxerService->getDemuxerHandle()->getBufferDuration(mCurrentSubtitleIndex); - } + if (pFrame->getInfo().streamIndex == mWillChangedAudioStreamIndex) { + mCurrentAudioIndex = mWillChangedAudioStreamIndex; + mCATimeBase = mWATimeBase; + mWillChangedAudioStreamIndex = -1; } - int num = i; + if (mAudioChangedFirstPts == INT64_MAX) { + int64_t playedTime = mMasterClock.GetTime(); - for (i = 0; i < num; i++) { - if (duration < 0) { - duration = durations[i]; + if (pFrame->getInfo().pts < playedTime) { + return ret; } else { - duration = gotMax ? std::max(duration, durations[i]) : std::min(duration, durations[i]); + //recodr 64MAX for audio stream changed for first frame + mAudioChangedFirstPts = pFrame->getInfo().pts; } } - return duration; - } - - bool SuperMediaPlayer::SeekInCache(int64_t pos) - { - int64_t audioLastPos = mBufferController.GetPacketLastTimePos(BUFFER_TYPE_AUDIO); - int64_t videoLastPos = mBufferController.GetPacketLastTimePos(BUFFER_TYPE_VIDEO); - int64_t minLastPos = -1; - - if (HAVE_VIDEO && HAVE_AUDIO) { - minLastPos = audioLastPos < videoLastPos ? audioLastPos : videoLastPos; - } else if (HAVE_VIDEO) { - minLastPos = videoLastPos; - } else if (HAVE_AUDIO) { - minLastPos = audioLastPos; - } else { - // no video and audio ? - } - - //seek before - if (pos < getCurrentPosition()) { - return false; - } - - //seek bigger than last frame - if (pos > minLastPos) { - return false; - } - - int64_t keyPosBefore = INT64_MIN; - - //can not find last key video pts,return - if (HAVE_VIDEO) { - keyPosBefore = mBufferController.GetKeyTimePositionBefore(BUFFER_TYPE_VIDEO, pos); - } else if (HAVE_AUDIO) { - keyPosBefore = mBufferController.GetKeyTimePositionBefore(BUFFER_TYPE_AUDIO, pos); - } - - if (keyPosBefore == INT64_MIN) { - return false; - } - - mBufferController.ClearPacketBeforeTimePos(BUFFER_TYPE_ALL, keyPosBefore); - mSoughtVideoPos = keyPosBefore; - return true; - } - - void SuperMediaPlayer::SwitchVideo(int64_t startTime) - { - AF_LOGD("video change find start time is %lld", startTime); - int ret = mDemuxerService->OpenStream(mWillChangedVideoStreamIndex); - - if (ret < 0) { - AF_LOGD("video", "switch video open stream failed,stream index %d\n", - mCurrentVideoIndex); - return; - } - - if (mMixMode) { - mDemuxerService->CloseStream(GEN_STREAM_INDEX(mCurrentVideoIndex)); - } else { - mDemuxerService->CloseStream(mCurrentVideoIndex); - } - - mDemuxerService->Seek(startTime / 1000 * 1000, 0, mWillChangedVideoStreamIndex); - - if (mMixMode) { - mBufferController.ClearPacketAfterTimePosition(BUFFER_TYPE_AV, startTime); - } else { - mBufferController.ClearPacketAfterTimePosition(BUFFER_TYPE_VIDEO, startTime); - } - - mWillSwitchVideo = false; - mVideoChangedFirstPts = INT64_MAX; - mEof = false; - } - - int64_t SuperMediaPlayer::getAudioPlayTimeStampCB(void *arg) - { - SuperMediaPlayer *pHandle = static_cast(arg); - return pHandle->getAudioPlayTimeStamp(); - } - - int64_t SuperMediaPlayer::getAudioPlayTimeStamp() - { - if (mAudioRender == nullptr) { - return INT64_MIN; - } - - if (mSeekFlag) { - return INT64_MIN; - } - - int64_t aoutPos; - aoutPos = mAudioRender->getPosition(); - return mAudioTime.startTime + mAudioTime.deltaTime + aoutPos; - } - - void SuperMediaPlayer::GetVideoResolution(int &width, int &height) - { - width = mVideoWidth; - height = mVideoHeight; - } - - void SuperMediaPlayer::GetVideoRotation(int &rotation) - { - rotation = mVideoRotation; - } - - int SuperMediaPlayer::setUpAudioDecoder(const Stream_meta *meta) - { - if (mAudioDecoder != nullptr) { - return 0; - } - - int ret = 0; - - if (meta->samplerate <= 0) { // meta.frame_size maybe 0 when playing artp - ret = 0; - return 0; - } - - if (meta->duration > mDuration) { - mDuration = meta->duration; - } - - //setVolume to current setting after create new. - SetVolume(mSet.mVolume); - - if (mSet.bMute) { - ProcessMuteMsg(); - } - - mAudioDecoder = decoderFactory::create(meta->codec, DECFLAG_SW, 0); - - if (mAudioDecoder == nullptr) { - ret = gen_framework_errno(error_class_codec, codec_error_audio_not_support); - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_AUDIO_CODEC_NOT_SUPPORT, framework_err2_string(ret)); - return ret; + if (mMediaFrameCb && (!pMedia_Frame->isProtected() || mDrmKeyValid)) { + mMediaFrameCb(mMediaFrameCbArg, pMedia_Frame.get(), ST_TYPE_AUDIO); } - ret = mAudioDecoder->open(meta, nullptr, 0); - - if (ret < 0) { - AF_LOGE("mAudioDecoder init error %d\n", ret); - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_AUDIO_DECODER_DEVICE_ERROR, framework_err2_string(ret)); - mAudioDecoder = nullptr; - return ret; + mBufferController->AddPacket(move(pMedia_Frame), BUFFER_TYPE_AUDIO); + } else if (pFrame->getInfo().streamIndex == mCurrentSubtitleIndex || pFrame->getInfo().streamIndex == mWillChangedSubtitleStreamIndex) { + if (mMediaFrameCb && (!pMedia_Frame->isProtected() || mDrmKeyValid)) { + mMediaFrameCb(mMediaFrameCbArg, pMedia_Frame.get(), ST_TYPE_SUB); } - return ret; - } - - int SuperMediaPlayer::SetUpAudioPath() - { - if (mBufferController.IsPacketEmtpy(BUFFER_TYPE_AUDIO)) { - return 0; - } - - unique_ptr pMeta{}; - mDemuxerService->GetStreamMeta(pMeta, mCurrentAudioIndex, false); - Stream_meta *meta = (Stream_meta *) (pMeta.get()); - int ret = setUpAudioDecoder(meta); - - if (ret < 0) { - return ret; - } - - IAFFrame::audioInfo info{}; - info.channels = meta->channels; - info.sample_rate = meta->samplerate; - info.format = meta->sample_fmt; - info.nb_samples = meta->frame_size; - info.channel_layout = meta->channel_layout; - setUpAudioRender(info); - return ret; - } - - int SuperMediaPlayer::setUpAudioRender(const IAFFrame::audioInfo &info) - { - if (mAudioRender == nullptr) { - mAudioRender = AudioRenderFactory::create(); - } - - assert(mAudioRender); - mAudioRender->setListener(&mAudioRenderCB); - int audioInitRet = mAudioRender->init(&info); - - if (audioInitRet < 0) { - AF_LOGE("AudioOutHandle Init Error is %d", audioInitRet); - // don't release audio handle because we only new it in constructor - // PS: we should try to recover it later, or notify error -// mAudioOutHandle = 0; - mCurrentAudioIndex = -1; - return -1; - } else { - mAudioRender->setSpeed(mSet.rate); - mAudioRender->mute(mSet.bMute); - mAudioRender->setVolume(mSet.mVolume); - return 0; - } - } - - int SuperMediaPlayer::SetUpVideoPath() - { - if (mVideoDecoder && mVideoRender) { - return 0; - } - - if (mBufferController.IsPacketEmtpy(BUFFER_TYPE_VIDEO)) { - return 0; - } - - mDemuxerService->GetStreamMeta(mCurrentVideoMeta, mCurrentVideoIndex, false); - auto *meta = (Stream_meta *) (mCurrentVideoMeta.get()); - - if (mVideoWidth != meta->width || mVideoHeight != meta->height) { - mVideoWidth = meta->width; - mVideoHeight = meta->height; - mVideoRotation = meta->rotate; - mPNotifier->NotifyVideoSizeChanged(mVideoWidth, mVideoHeight); - } - - if (mSet.mView == nullptr && mFrameCb == nullptr) { - return 0; - } - - if (mVideoInterlaced == InterlacedType_UNKNOWN) { - AF_LOGW("Wait for parser video interlaced Type"); - return 0; - } - - int ret = 0; - if (meta->interlaced == InterlacedType_UNKNOWN) { - meta->interlaced = mVideoInterlaced; - } - - if (!mSet.bEnableTunnelRender && mSet.mView != nullptr && mVideoRender == nullptr) { - if (mAppStatus == APP_BACKGROUND) { - AF_LOGW("create video render in background"); - } - AF_LOGD("SetUpVideoRender start"); - CreateVideoRender(); - if (mVideoRender) { - IVideoRender::Rotate finalRotate = IVideoRender::Rotate::Rotate_None; - - if (meta->rotate == 0) { - finalRotate = IVideoRender::Rotate::Rotate_None; - } else if (meta->rotate == 90) { - finalRotate = IVideoRender::Rotate::Rotate_90; - } else if (meta->rotate == 180) { - finalRotate = IVideoRender::Rotate::Rotate_180; - } else if (meta->rotate == 270) { - finalRotate = IVideoRender::Rotate::Rotate_270; - } - - mVideoRender->setVideoRotate(finalRotate); - } - } + mBufferController->AddPacket(move(pMedia_Frame), BUFFER_TYPE_SUBTITLE); + AF_LOGD("read subtitle pts is %lld", pFrame->getInfo().pts); - //re set view in case for not set view before - if (mSet.mView) { - if (mVideoRender) { - mVideoRender->setDisPlay(mSet.mView); - } + if (pFrame->getInfo().streamIndex == mWillChangedSubtitleStreamIndex) { + mCurrentSubtitleIndex = mWillChangedSubtitleStreamIndex; + mWillChangedSubtitleStreamIndex = -1; } - - if (mVideoDecoder != nullptr) { - return 0; + if (mSubtitleChangedFirstPts == INT64_MAX) { + mSubtitleChangedFirstPts = pFrame->getInfo().pts; } - - AF_LOGD("SetUpVideoDecoder start"); - - bool bHW = false; - if (mSet.bEnableHwVideoDecode) { - switch (meta->codec) { - case AF_CODEC_ID_H264: { - string value = getProperty("ro.video.dec.h264"); - bHW = !(value == "OFF"); - break; + } else { + AF_LOGD("unknown stream %x, read packet pts is %lld\n", pFrame->getInfo().streamIndex, pFrame->getInfo().pts); + if (mCurrentAudioIndex < 0 || mCurrentVideoIndex < 0) { + std::unique_ptr meta; + int ret1 = mDemuxerService->GetStreamMeta(meta, pFrame->getInfo().streamIndex, true); + if (ret1 >= 0) { + switch (((Stream_meta *) (*meta))->type) { + case STREAM_TYPE_VIDEO: { + if (!mSet->bDisableVideo && mCurrentVideoIndex < 0 && ((Stream_meta *) (*meta))->width > 0) { + mCurrentVideoIndex = pFrame->getInfo().streamIndex; + updateVideoMeta(); + } + break; + } + case STREAM_TYPE_AUDIO: { + if (!mSet->bDisableAudio && mCurrentAudioIndex < 0 && ((Stream_meta *) (*meta))->channels > 0) { + mCurrentAudioIndex = pFrame->getInfo().streamIndex; + mCATimeBase = ((Stream_meta *) (*meta))->ptsTimeBase; + } + break; + } + default: + break; } - - default: - bHW = true; - break; - } - } - - ret = CreateVideoDecoder(bHW, *meta); - - if (ret < 0) { - if (bHW) { - ret = CreateVideoDecoder(false, *meta); } } + } - if (ret < 0) { - AF_LOGE("%s CreateVideoDecoder failed, error msg is %s", __FUNCTION__, framework_err2_string(ret)); - - if (ret == gen_framework_errno(error_class_codec, codec_error_video_not_support)) { - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_CODEC_NOT_SUPPORT, framework_err2_string(ret)); - } else if (ret == gen_framework_errno(error_class_codec, codec_error_video_device_error)) { - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_DECODER_DEVICE_ERROR, framework_err2_string(ret)); - } + if (mWillSwitchVideo) { + int videoCount = 0; + int64_t startTime = mBufferController->FindSeamlessPointTimePosition(BUFFER_TYPE_VIDEO, videoCount); + if (startTime == 0 || videoCount < 40) { return ret; } - if (mVideoDecoder->getFlags() & DECFLAG_HW) { - } else { - if (mSet.bEnableHwVideoDecode) { - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SW_VIDEO_DECODER, "Switch to software video decoder"); + if (mMixMode) { + int64_t startTimeA = mBufferController->FindSeamlessPointTimePosition(BUFFER_TYPE_AUDIO, videoCount); + + if (startTimeA == 0 || videoCount < 40) { + return ret; } - } - if (meta->duration > mDuration) { - mDuration = meta->duration; + startTime = std::max(startTime, startTimeA); } - return ret; + SwitchVideo(startTime); + mWillSwitchVideo = false; } - bool SuperMediaPlayer::CreateVideoRender() - { - if (nullptr != mVideoRender) { - return true; - } - - // lock mAppStatusMutex before mCreateMutex - std::lock_guard uMutex(mCreateMutex); - mVideoRender = videoRenderFactory::create(); - mVideoRender->setScale(convertScaleMode(mSet.scaleMode)); - mVideoRender->setRotate(convertRotateMode(mSet.rotateMode)); - mVideoRender->setBackgroundColor(mSet.mVideoBackgroundColor); - mVideoRender->setFlip(convertMirrorMode(mSet.mirrorMode)); - mVideoRender->setDisPlay(mSet.mView); - mVideoRender->setRenderResultCallback([this](int64_t pts, bool rendered) -> void { - VideoRenderCallback(this, pts, nullptr); - }); - int renderRet = mVideoRender->init(); - - if (renderRet != 0) { - // for windows init failed, which may need change render type in future. - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_RENDER_INIT_ERROR, "init video render failed"); - } + return ret; +} - mVideoRender->setSpeed(mSet.rate); - return true; +void SuperMediaPlayer::printTimePosition(int64_t time) const +{ + if (time > 0) { + time /= 1000; + int timeM = static_cast((time / 1000) / 60); + int timeS = static_cast((time / 1000) % 60); + int timeMS = static_cast(time - timeM * 60000 - timeS * 1000); + AF_LOGD("timePosition is %d:%d:%d\n", timeM, timeS, timeMS); } +} - int SuperMediaPlayer::CreateVideoDecoder(bool bHW, Stream_meta &meta) - { - int ret; - uint64_t decFlag = 0; - - if (bHW) { - decFlag |= DECFLAG_HW; - } else { - decFlag |= DECFLAG_SW; - } - - if (mAdaptiveVideo) { - decFlag |= DECFLAG_ADAPTIVE; - } - - { - std::lock_guard uMutex(mCreateMutex); - mVideoDecoder = decoderFactory::create(meta.codec, decFlag, max(meta.height, meta.width)); - } - - if (mVideoDecoder == nullptr) { - return gen_framework_errno(error_class_codec, codec_error_video_not_support); - } -#ifdef __APPLE__ - if (mFrameCb && mSet.pixelBufferOutputFormat) { - auto *vtbDecoder = dynamic_cast(mVideoDecoder.get()); - if (vtbDecoder) { - int ret1 = vtbDecoder->setPixelBufferFormat(mSet.pixelBufferOutputFormat); - if (ret1 < 0) { - AF_LOGW("setPixelBufferFormat error\n"); - } - } - } -#endif +void SuperMediaPlayer::FlushAudioPath() +{ + mAVDeviceManager->flushDevice(SMPAVDeviceManager::DEVICE_TYPE_AUDIO); - void *view = nullptr; + audioDecoderEOS = false; - if (bHW) { - if (mSet.bEnableTunnelRender) { - view = mSet.mView; - decFlag |= DECFLAG_DIRECT; - } else { - if (mVideoRender) { - view = mVideoRender->getSurface(); - } - } - } - - if (!mSet.bLowLatency) { - mSet.bLowLatency = mDemuxerService->getDemuxerHandle()->isLowLatency(); - } - - if (mSet.bLowLatency) { - // artp disable b frame to reduce delay at present - decFlag |= DECFLAG_OUTPUT_FRAME_ASAP; - } - - ret = mVideoDecoder->open(&meta, view, decFlag); - - if (ret < 0) { - AF_LOGE("config decoder error ret= %d \n", ret); - //TODO:PostErrorMsg - std::lock_guard uMutex(mCreateMutex); - mVideoDecoder = nullptr; - return gen_framework_errno(error_class_codec, codec_error_video_device_error); - } - - return 0; + //flush frame queue + while (!mAudioFrameQue.empty()) { + mAudioFrameQue.pop_front(); } + mPlayedAudioPts = INT64_MIN; + mAudioPtsRevert = false; + mAudioTime.startTime = 0; + mAudioTime.deltaTime = 0; + mAudioTime.deltaTimeTmp = 0; + mAudioPacket = nullptr; +} - void SuperMediaPlayer::Reset() - { - mCurrentVideoIndex = -1; - mCurrentAudioIndex = -1; - mCurrentSubtitleIndex = -1; - mVideoWidth = 0; - mVideoHeight = 0; - mVideoRotation = 0; - mDuration = INT64_MIN; - mBufferPosition = 0; - mSeekPos = INT64_MIN; - mPlayedVideoPts = INT64_MIN; - mPlayedAudioPts = INT64_MIN; - mSeekFlag = false; - mFirstAudioPts = INT64_MIN; - mFirstVideoPts = INT64_MIN; - mMediaStartPts = INT64_MIN; - mEof = false; - mFirstBufferFlag = true; - mBufferingFlag = false; - mCurVideoPts = INT64_MIN; - mLastAudioFrameDuration = INT64_MIN; - mTimeoutStartTime = INT64_MIN; - mSubtitleShowIndex = 0; - mWillChangedVideoStreamIndex = -1; - mWillChangedAudioStreamIndex = -1; - mWillChangedSubtitleStreamIndex = -1; - mBufferIsFull = false; - mWillSwitchVideo = false; - mMixMode = false; - mFirstRendered = false; - mInited = false; - mSeekNeedCatch = false; - mMainStreamId = -1; - mRemovedFirstAudioPts = INT64_MIN; - mFirstSeekStartTime = 0; - mAudioChangedFirstPts = INT64_MIN; - mVideoChangedFirstPts = INT64_MIN; - mSubtitleChangedFirstPts = INT64_MIN; - mSoughtVideoPos = INT64_MIN; - mPlayingPosition = 0; - mFirstReadPacketSucMS = 0; - mCanceled = false; - mPNotifier->Enable(true); - mMasterClock.reset(); - FlushSubtitleInfo(); - mSubtitleShowedQueue.clear(); - mBSReadCb = nullptr; - mBSCbArg = nullptr; - mBSSeekCb = nullptr; - mBSCbArg = nullptr; - mUtil.reset(); - mDcaManager.reset(); - mVideoInterlaced = InterlacedType_UNKNOWN; - mVideoParserTimes = 0; - mVideoPtsRevert = mAudioPtsRevert = false; - mLowMem = false; - mCurrentVideoMeta = nullptr; - mAdaptiveVideo = false; - dropLateVideoFrames = false; - mBRendingStart = false; - mSubtitleEOS = false; +void SuperMediaPlayer::FlushVideoPath() +{ + mAVDeviceManager->flushDevice(SMPAVDeviceManager::DEVICE_TYPE_VIDEO); - if (mVideoRender) { - mVideoRender->setSpeed(1); - } + videoDecoderEOS = false; + //flush frame queue - mSecretPlayBack = false; + while (!mVideoFrameQue.empty()) { + mVideoFrameQue.front()->setDiscard(true); + mMsgCtrlListener->ProcessVideoRenderedMsg(mVideoFrameQue.front()->getInfo().pts, af_getsteady_ms(), false, nullptr); + mVideoFrameQue.pop(); } - int SuperMediaPlayer::GetCurrentStreamIndex(StreamType type) - { - int streamIndex = -1; - - if (mMixMode && type != ST_TYPE_SUB) { - if (HAVE_VIDEO) { - streamIndex = GEN_STREAM_INDEX(mCurrentVideoIndex); - } else if (HAVE_AUDIO) { - streamIndex = GEN_STREAM_INDEX(mCurrentAudioIndex); - } - } else { - switch (type) { - case ST_TYPE_AUDIO: - streamIndex = mCurrentAudioIndex; - break; - - case ST_TYPE_VIDEO: - streamIndex = mCurrentVideoIndex; - break; + mPlayedVideoPts = INT64_MIN; + mCurVideoPts = INT64_MIN; + videoDecoderFull = false; + mVideoPtsRevert = false; + mVideoPacket = nullptr; + dropLateVideoFrames = false; +} - case ST_TYPE_SUB: - streamIndex = mCurrentSubtitleIndex; - break; - - default: - break; - } +void SuperMediaPlayer::FlushSubtitleInfo() +{ + while (!mSubtitleShowedQueue.empty()) { + if (mSubtitleShowedQueue.front()) { + mPNotifier->NotifySubtitleEvent(subTitle_event_hide, mSubtitleShowedQueue.front().release(), 0, nullptr); } - return streamIndex; + mSubtitleShowedQueue.pop_front(); } - StreamInfo *SuperMediaPlayer::GetCurrentStreamInfo(StreamType type) - { - int streamIndex = GetCurrentStreamIndex(type); - - if (streamIndex != -1) { - for (StreamInfo *info : mStreamInfoQueue) { - if (info->streamIndex == streamIndex) { - return info; - } - } - } - - return nullptr; + mSubtitleShowedQueue.clear(); + mSubtitleShowIndex = 0; + mSubtitleEOS = false; + if (mSubPlayer) { + mSubPlayer->flush(); } +} - void SuperMediaPlayer::ProcessPrepareMsg() - { - AF_LOGD("ProcessPrepareMsg start"); - int ret; - - if (mSet.url.empty() && mBSReadCb == nullptr) { - AF_LOGD("ProcessPrepareMsg url is empty"); - ChangePlayerStatus(PLAYER_ERROR); - mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DATASOURCE_EMPTYURL, "Prepare url is empty"); - return; - } +void SuperMediaPlayer::PostBufferPositionMsg() +{ + if (mPlayStatus == PLAYER_PAUSED || mPlayStatus == PLAYER_PLAYING) { + int64_t duration; - if (mPlayStatus != PLAYER_INITIALZED && mPlayStatus != PLAYER_STOPPED) { - AF_LOGD("ProcessPrepareMsg status is %d", mPlayStatus.load()); - return; - } - - mPlayStatus = PLAYER_PREPARINIT; - bool noFile = false; - - if (!(mBSReadCb != nullptr && mBSSeekCb != nullptr && mBSCbArg != nullptr)) { - if (!mSet.url.empty()) { - ret = openUrl(); - - if (ret < 0) { - AF_LOGD("%s mDataSource open failed,url is %s %s", __FUNCTION__, mSet.url.c_str(), - framework_err2_string(ret)); - - if (ret == FRAMEWORK_ERR_EXIT) { - // stop by user. - //ChangePlayerStatus(PLAYER_STOPPED); - return; - } else if (ret == FRAMEWORK_ERR_PROTOCOL_NOT_SUPPORT) { - noFile = true; - } else { - NotifyError(ret); - return; - } - } - } - } - - if (mCanceled) { - return; - } - - { - std::lock_guard locker(mCreateMutex); - mDemuxerService = new demuxer_service(mDataSource); - mDemuxerService->setOptions(&mSet.mOptions); - } - - std::function demuxerCB = [this](const std::string & key, const std::string & value) -> void { - this->OnDemuxerCallback(key, value); - }; - mDemuxerService->setDemuxerCb(demuxerCB); - mDemuxerService->setNoFile(noFile); - - if (!noFile) { - mDemuxerService->SetDataCallBack(mBSReadCb, mBSCbArg, mBSSeekCb, mBSCbArg, nullptr); - } - - - //prepare之前seek - if (mSeekPos > 0) { - mDemuxerService->Seek(mSeekPos, 0, -1); - mSeekFlag = true; + if (isSeeking()) { + duration = 0; } else { - ResetSeekStatus(); + duration = getPlayerBufferDuration(false, false); } - AF_LOGD("initOpen start"); - ret = mDemuxerService->createDemuxer((mBSReadCb || noFile) ? demuxer_type_bit_stream : demuxer_type_unknown); - - // TODO: video tool box HW decoder not merge the header - if (mDemuxerService->getDemuxerHandle()) { -#ifdef __APPLE__ - mDemuxerService->getDemuxerHandle()->setBitStreamFormat(false, false); -#else - mDemuxerService->getDemuxerHandle()->setBitStreamFormat(true, true); -#endif - if (noFile) { - IDataSource::SourceConfig config; - mDataSource->Get_config(config); - mDemuxerService->getDemuxerHandle()->setDataSourceConfig(config); - } - mDcaManager.createObservers(); - } - - //step2: Demuxer init and getstream index - ret = mDemuxerService->initOpen((mBSReadCb || noFile) ? demuxer_type_bit_stream : demuxer_type_unknown); - - if (ret < 0) { - if (ret != FRAMEWORK_ERR_EXIT && !mCanceled) { - NotifyError(ret); - } - - return; - } - - int nbStream = mDemuxerService->GetNbStreams(); - AF_LOGD("Demuxer service get nubmer streams is %d", nbStream); - unique_ptr pMeta; - int bandWidthNearStreamIndex = -1; - int minBandWidthDelta = INT_MAX; - int mDefaultBandWidth = mSet.mDefaultBandWidth; - - for (int i = 0; i < nbStream; ++i) { - mDemuxerService->GetStreamMeta(pMeta, i, false); - auto *meta = (Stream_meta *) (pMeta.get()); - - if (meta->type == STREAM_TYPE_MIXED) { - mMixMode = true; - } - - if (meta->type == STREAM_TYPE_MIXED || meta->type == STREAM_TYPE_VIDEO) { - int metaBandWidth = (int) meta->bandwidth; - - if (abs(mDefaultBandWidth - metaBandWidth) < minBandWidthDelta) { - bandWidthNearStreamIndex = i; - minBandWidthDelta = abs(mDefaultBandWidth - metaBandWidth); - } - } - } - - for (int i = 0; i < nbStream; ++i) { - int openStreamRet = 0; - mDemuxerService->GetStreamMeta(pMeta, i, false); - auto *meta = (Stream_meta *) (pMeta.get()); - auto *info = new StreamInfo(); - info->streamIndex = i; - info->subtitleLang = nullptr; - info->audioLang = nullptr; - info->description = nullptr; - AF_LOGD("get a stream %d\n", meta->type); - - if (!mSet.bDisableVideo && meta->type == STREAM_TYPE_VIDEO) { - info->type = ST_TYPE_VIDEO; - info->videoWidth = meta->width; - info->videoHeight = meta->height; - info->videoBandwidth = (int) meta->bandwidth; - - if (meta->description) { - info->description = strdup((const char *) meta->description); - } - - mStreamInfoQueue.push_back(info); - mVideoInterlaced = meta->interlaced; - - if (mCurrentVideoIndex < 0 && !mMixMode && meta->attached_pic == 0) { - if (bandWidthNearStreamIndex == i) { - AF_LOGD("get a video stream\n"); - openStreamRet = mDemuxerService->OpenStream(i); - mCurrentVideoIndex = i; - mDemuxerService->GetStreamMeta(mCurrentVideoMeta, i, false); - } - } - } else if (!mSet.bDisableAudio && meta->type == STREAM_TYPE_AUDIO) { - info->type = ST_TYPE_AUDIO; - - if (meta->lang) { - info->audioLang = strdup((const char *) meta->lang); - } - - if (meta->description) { - info->description = strdup((const char *) meta->description); - } - - info->nChannels = meta->channels; - info->sampleFormat = meta->sample_fmt; - info->sampleRate = meta->samplerate; - mStreamInfoQueue.push_back(info); - - if (mCurrentAudioIndex < 0 && !mMixMode) { - AF_LOGD("get a audio stream\n"); - openStreamRet = mDemuxerService->OpenStream(i); - mCurrentAudioIndex = i; - } - } else if (meta->type == STREAM_TYPE_SUB) { - info->type = ST_TYPE_SUB; + if (duration >= 0) { + mBufferPosition = getCurrentPosition() + duration; - if (meta->lang) { - info->subtitleLang = strdup((const char *) meta->lang); - } - - if (meta->description) { - info->description = strdup((const char *) meta->description); - } - - mStreamInfoQueue.push_back(info); - - if (mCurrentSubtitleIndex < 0) { - AF_LOGD("get a subtitle stream\n"); - openStreamRet = mDemuxerService->OpenStream(i); - mCurrentSubtitleIndex = i; - } - } else if (meta->type == STREAM_TYPE_MIXED) { - info->type = ST_TYPE_VIDEO; - info->streamIndex = i; - info->videoBandwidth = (int) meta->bandwidth; - info->videoWidth = meta->width; - info->videoHeight = meta->height; - AF_LOGD("STREAM_TYPE_MIXED bandwidth is %llu", meta->bandwidth); - - if (mMainStreamId >= 0) { - AF_LOGD("already readed stream"); - } else if (bandWidthNearStreamIndex == i) { - mMixMode = true; - openStreamRet = mDemuxerService->OpenStream(i); - mMainStreamId = i; - } - - mStreamInfoQueue.push_back(info); - } else { - delete info; + if (mEof) { + mBufferPosition = mDuration; } - if (openStreamRet < 0) { - ChangePlayerStatus(PLAYER_ERROR); - mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_DEMUXER_OPENSTREAM, "open stream failed"); - return; - } - } - - if (!HAVE_VIDEO) { - mSeekNeedCatch = false; + mPNotifier->NotifyBufferPosition((mBufferPosition <= mDuration ? mBufferPosition : mDuration) / 1000); } - - AF_LOGD("initOpen end"); - mDemuxerService->start(); - ChangePlayerStatus(PLAYER_PREPARING); - mTimeoutStartTime = INT64_MIN; } +} - int SuperMediaPlayer::openUrl() - { - IDataSource::SourceConfig config{}; - config.low_speed_time_ms = mSet.timeout_ms; - config.low_speed_limit = 1; - - switch (mSet.mIpType) { - case IpResolveWhatEver: - config.resolveType = IDataSource::SourceConfig::IpResolveWhatEver; - break; - case IpResolveV4: - config.resolveType = IDataSource::SourceConfig::IpResolveV4; - break; - case IpResolveV6: - config.resolveType = IDataSource::SourceConfig::IpResolveV6; - break; - } - // config.max_time_ms = mSet.timeout; - config.connect_time_out_ms = mSet.timeout_ms; - config.http_proxy = mSet.http_proxy; - config.refer = mSet.refer; - config.userAgent = mSet.userAgent; - config.customHeaders = mSet.customHeaders; - config.listener = &mSourceListener; - mSourceListener.enableRetry(); - - if (mCanceled) { - return FRAMEWORK_ERR_EXIT; - } - - { - std::lock_guard locker(mCreateMutex); - mDataSource = dataSourcePrototype::create(mSet.url, &mSet.mOptions); - } - - if (mDataSource) { - mDataSource->Set_config(config); - int ret = mDataSource->Open(0); - return ret; - } - - return -1; - } - - void SuperMediaPlayer::ProcessSwitchStreamMsg(int index) - { - if (mDemuxerService == nullptr) { - return; - } - - Stream_type type = STREAM_TYPE_UNKNOWN; - int i; - int number = mDemuxerService->GetNbStreams(); - - for (i = 0; i < number; i++) { - if (index == i) { - unique_ptr pMeta; - mDemuxerService->GetStreamMeta(pMeta, i, false); - auto *meta = (Stream_meta *) (pMeta.get()); - type = meta->type; - break; - } - } - - if (i >= number) { - AF_LOGW("no such stream\n"); - return; - } - - if (mDuration == 0) { - int id = GEN_STREAM_INDEX(index); - - if (mMainStreamId == -1 || mMainStreamId == id) { - AF_LOGD("current stream index is the same"); - return; - } +int64_t SuperMediaPlayer::getPlayerBufferDuration(bool gotMax, bool internal) +{ + int64_t durations[3] = {-1, -1, -1}; + int i = 0; + int64_t duration = -1; - mVideoChangedFirstPts = INT64_MAX; - mAudioChangedFirstPts = INT64_MAX; - mEof = false; - mDemuxerService->SwitchStreamAligned(mMainStreamId, id); - return; - } + if (HAVE_VIDEO) { + int64_t &duration_c = durations[i++]; + duration_c = mBufferController->GetPacketDuration(BUFFER_TYPE_VIDEO); - if (type == STREAM_TYPE_MIXED) { - int id = GEN_STREAM_INDEX(index); + // AF_LOGD("videoDuration is %lld\n",videoDuration); + if (duration_c < 0 && !HAVE_AUDIO) { + duration_c = mBufferController->GetPacketLastPTS(BUFFER_TYPE_VIDEO) - mBufferController->GetPacketPts(BUFFER_TYPE_VIDEO); - if (mMainStreamId == -1 || mMainStreamId == id) { - AF_LOGD("current stream index is the same"); - return; + if (duration_c <= 0) { + duration_c = (int64_t) mBufferController->GetPacketSize(BUFFER_TYPE_VIDEO) * 40 * 1000; } - - mVideoChangedFirstPts = INT64_MAX; - mAudioChangedFirstPts = INT64_MAX; - mEof = false; - switchVideoStream(id, type); - return; + } + if (!internal && mDemuxerService && mDemuxerService->getDemuxerHandle()) { + duration_c += mDemuxerService->getDemuxerHandle()->getBufferDuration(mCurrentVideoIndex); } - if (type == STREAM_TYPE_SUB && mCurrentSubtitleIndex >= 0 && - mCurrentSubtitleIndex != index) { - return switchSubTitle(index); - } else if (type == STREAM_TYPE_AUDIO && mCurrentAudioIndex >= 0 && - mCurrentAudioIndex != index) { - return switchAudio(index); - } else if (type == STREAM_TYPE_VIDEO && mCurrentVideoIndex >= 0 && - mCurrentVideoIndex != index) { - return switchVideoStream(index, type); + if (mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)) { + // FIXME: get the accurate duration + duration_c += mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)->getInputPaddingSize() * 40 * 1000; } } - void SuperMediaPlayer::switchVideoStream(int index, Stream_type type) - { - int count = (int) mStreamInfoQueue.size(); - StreamInfo *currentInfo = nullptr; - StreamInfo *willChangeInfo = nullptr; - int i; - int currentId = mCurrentVideoIndex; - - if (type == STREAM_TYPE_MIXED) { - currentId = GEN_STREAM_INDEX(mCurrentVideoIndex); + if (HAVE_AUDIO) { + int64_t &duration_c = durations[i++]; + duration_c = mBufferController->GetPacketDuration(BUFFER_TYPE_AUDIO); + // AF_LOGD("audioDuration is %lld\n",audioDuration); + if (!internal && mDemuxerService && mDemuxerService->getDemuxerHandle()) { + duration_c += mDemuxerService->getDemuxerHandle()->getBufferDuration(mCurrentAudioIndex); } - for (i = 0; i < count; i++) { - StreamInfo *info = mStreamInfoQueue[i]; - - if (info->streamIndex == index) { - willChangeInfo = info; - } - - if (currentId == info->streamIndex) { - currentInfo = info; + if (mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_AUDIO)) { + int64_t audioPacketDuration = mBufferController->GetOnePacketDuration(BUFFER_TYPE_AUDIO); + if (audioPacketDuration <= 0) { + audioPacketDuration = 23 * 1000; } + duration_c += mAVDeviceManager->getDecoder(SMPAVDeviceManager::DEVICE_TYPE_AUDIO)->getInputPaddingSize() * audioPacketDuration; } + } - if (!willChangeInfo || !currentInfo) { - return; + /* + * Do not let player loading when switching subtitle, we'll read subtitle first + * in ReadPacket() + */ + if (HAVE_SUBTITLE && !mSubtitleEOS && mSubtitleChangedFirstPts == INT64_MIN) { + int64_t &duration_c = durations[i++]; + duration_c = mBufferController->GetPacketDuration(BUFFER_TYPE_SUBTITLE); + if (!internal && mDemuxerService && mDemuxerService->getDemuxerHandle()) { + duration_c += mDemuxerService->getDemuxerHandle()->getBufferDuration(mCurrentSubtitleIndex); } + } - AF_LOGD("video change video bitrate before is %d,after is %d", - currentInfo->videoBandwidth, willChangeInfo->videoBandwidth); - //TODO: different strategy - mWillChangedVideoStreamIndex = index; - mVideoChangedFirstPts = INT64_MAX; + int num = i; - if (willChangeInfo->videoBandwidth < currentInfo->videoBandwidth) { - mDemuxerService->SwitchStreamAligned(currentId, index); + for (i = 0; i < num; i++) { + if (duration < 0) { + duration = durations[i]; } else { - mMixMode = (type == STREAM_TYPE_MIXED); - int videoCount = 0; - int64_t startTime = mBufferController.FindSeamlessPointTimePosition(BUFFER_TYPE_VIDEO, videoCount); + duration = gotMax ? std::max(duration, durations[i]) : std::min(duration, durations[i]); + } + } - if (startTime == 0 || videoCount < 40) { - mWillSwitchVideo = true; - return; - } + return duration; +} - if (mMixMode) { - int64_t startTimeA = mBufferController.FindSeamlessPointTimePosition(BUFFER_TYPE_AUDIO, videoCount); +bool SuperMediaPlayer::SeekInCache(int64_t pos) +{ + int64_t audioLastPos = mBufferController->GetPacketLastTimePos(BUFFER_TYPE_AUDIO); + int64_t videoLastPos = mBufferController->GetPacketLastTimePos(BUFFER_TYPE_VIDEO); + int64_t minLastPos = -1; - if (startTimeA == 0 || videoCount < 40) { - mWillSwitchVideo = true; - return; - } + if (HAVE_VIDEO && HAVE_AUDIO) { + minLastPos = audioLastPos < videoLastPos ? audioLastPos : videoLastPos; + } else if (HAVE_VIDEO) { + minLastPos = videoLastPos; + } else if (HAVE_AUDIO) { + minLastPos = audioLastPos; + } else { + // no video and audio ? + } - startTime = std::max(startTime, startTimeA); - } + //seek before + if (pos < getCurrentPosition()) { + return false; + } - SwitchVideo(startTime); - } + //seek bigger than last frame + if (pos > minLastPos) { + return false; } - void SuperMediaPlayer::switchAudio(int index) - { - // TODO: use position to seek demuxer ,and drop the late packet - int ret = mDemuxerService->OpenStream(index); + int64_t keyPosBefore = INT64_MIN; - if (ret < 0) { - AF_LOGD("subtitle", "switch audio open stream failed,stream index %d\n", - index); - return; - } + //can not find last key video pts,return + if (HAVE_VIDEO) { + keyPosBefore = mBufferController->GetKeyTimePositionBefore(BUFFER_TYPE_VIDEO, pos); + } else if (HAVE_AUDIO) { + keyPosBefore = mBufferController->GetKeyTimePositionBefore(BUFFER_TYPE_AUDIO, pos); + } - mDemuxerService->CloseStream(mCurrentAudioIndex); - mAudioChangedFirstPts = INT64_MAX; - mCurrentAudioIndex = index; - int64_t playTime = mMasterClock.GetTime(); - int64_t pts = playTime - mFirstAudioPts; - mMasterClock.setReferenceClock(nullptr, nullptr); - mBufferController.ClearPacket(BUFFER_TYPE_AUDIO); - mEof = false; - FlushAudioPath(); - mDemuxerService->Seek(pts, 0, index); - mPlayedAudioPts = INT64_MIN; + if (keyPosBefore == INT64_MIN) { + return false; } - void SuperMediaPlayer::switchSubTitle(int index) - { - int ret = mDemuxerService->OpenStream(index); + mBufferController->ClearPacketBeforeTimePos(BUFFER_TYPE_ALL, keyPosBefore); + mSoughtVideoPos = keyPosBefore; + return true; +} - if (ret < 0) { - AF_LOGD("subtitle", "switch subtitle open stream failed,stream index %d\n", index); - return; - } +void SuperMediaPlayer::SwitchVideo(int64_t startTime) +{ + AF_LOGD("video change find start time is %lld", startTime); + int ret = mDemuxerService->OpenStream(mWillChangedVideoStreamIndex); - mSubtitleChangedFirstPts = INT64_MAX; - mDemuxerService->CloseStream(mCurrentSubtitleIndex); - mCurrentSubtitleIndex = index; - mBufferController.ClearPacket(BUFFER_TYPE_SUBTITLE); - mEof = false; - mSubtitleEOS = false; - FlushSubtitleInfo(); - mDemuxerService->Seek(getCurrentPosition(), 0, index); + if (ret < 0) { + AF_LOGD("video", "switch video open stream failed,stream index %d\n", mCurrentVideoIndex); + return; } - void SuperMediaPlayer::ProcessStartMsg() - { - if (mPlayStatus == PLAYER_PAUSED || mPlayStatus == PLAYER_PREPARED || - mPlayStatus == PLAYER_COMPLETION) { - mUtil.reset(); + if (mMixMode) { + mDemuxerService->CloseStream(GEN_STREAM_INDEX(mCurrentVideoIndex)); + } else { + mDemuxerService->CloseStream(mCurrentVideoIndex); + } - if (mPlayStatus != PLAYER_PAUSED) { - if (HAVE_AUDIO) { - mMasterClock.setTime(mFirstAudioPts); - } else { - mMasterClock.setTime(mFirstVideoPts); - } - } + mDemuxerService->Seek(startTime / 1000 * 1000, 0, mWillChangedVideoStreamIndex); - ChangePlayerStatus(PLAYER_PLAYING); - } + if (mMixMode) { + mBufferController->ClearPacketAfterTimePosition(BUFFER_TYPE_AV, startTime); + } else { + mBufferController->ClearPacketAfterTimePosition(BUFFER_TYPE_VIDEO, startTime); } - void SuperMediaPlayer::ProcessPauseMsg() - { - if (mPlayStatus != PLAYER_PLAYING) { - return; - } + mWillSwitchVideo = false; + mVideoChangedFirstPts = INT64_MAX; + mEof = false; +} + +int64_t SuperMediaPlayer::getAudioPlayTimeStampCB(void *arg) +{ + SuperMediaPlayer *pHandle = static_cast(arg); + return pHandle->getAudioPlayTimeStamp(); +} - ChangePlayerStatus(PLAYER_PAUSED); - startRendering(false); +int64_t SuperMediaPlayer::getAudioPlayTimeStamp() +{ + if (!mAVDeviceManager->isAudioRenderValid()) { + return INT64_MIN; } - void SuperMediaPlayer::VideoRenderCallback(void *arg, int64_t pts, void *userData) - { - // AF_LOGD("video stream render pts is %lld", pts); - auto *pHandle = static_cast(arg); + if (mSeekFlag) { + return INT64_MIN; + } - if (pHandle->mCanceled) { - return; - } + int64_t aoutPos; + aoutPos = mAVDeviceManager->getAudioRenderPosition(); + return mAudioTime.startTime + mAudioTime.deltaTime + aoutPos; +} - if ((PLAYER_PREPARED != pHandle->mPlayStatus) - && (PLAYER_PAUSED != pHandle->mPlayStatus) - && (PLAYER_PLAYING != pHandle->mPlayStatus)) { - return; - } +void SuperMediaPlayer::GetVideoResolution(int &width, int &height) +{ + width = mVideoWidth; + height = mVideoHeight; +} - MsgParam param; - param.videoRenderedParam.pts = pts; - param.videoRenderedParam.timeMs = af_getsteady_ms(); - param.videoRenderedParam.userData = userData; - pHandle->putMsg(MSG_INTERNAL_VIDEO_RENDERED, param, false); +void SuperMediaPlayer::GetVideoRotation(int &rotation) +{ + rotation = mVideoRotation; +} + +int SuperMediaPlayer::setUpAudioDecoder(const Stream_meta *meta) +{ + int ret = 0; + + if (meta->samplerate <= 0) {// meta.frame_size maybe 0 when playing artp + ret = 0; + return 0; } - void SuperMediaPlayer::checkFirstRender() - { - if (!mFirstRendered) { - mFirstRendered = true; - updateLoopGap(); - AF_LOGI("Player NotifyFirstFrame"); - mPNotifier->NotifyFirstFrame(); - } + if (meta->duration > mDuration) { + mDuration = meta->duration; } - void SuperMediaPlayer::ProcessVideoRenderedMsg(int64_t pts, int64_t timeMs, void *picUserData) - { - mUtil.render(pts); - checkFirstRender(); + //setVolume to current setting after create new. + SetVolume(mSet->mVolume); - if (!mSeekFlag) { - mCurVideoPts = pts; - } + if (mSet->bMute) { + mMsgCtrlListener->ProcessMuteMsg(); + } - //AF_LOGD("video stream render pts is %lld , mVideoChangedFirstPts = %lld ", pts, mVideoChangedFirstPts); + uint64_t flags = DECFLAG_SW; - if ((INT64_MIN != mVideoChangedFirstPts) - && (pts >= mVideoChangedFirstPts)) { - AF_LOGD("video stream changed"); - StreamInfo *info = GetCurrentStreamInfo(ST_TYPE_VIDEO); - mPNotifier->NotifyStreamChanged(info, ST_TYPE_VIDEO); - mVideoChangedFirstPts = INT64_MIN; - } +#ifdef ANDROID + bool isWideVineVideo = (meta->keyFormat != nullptr && strcmp(meta->keyFormat, "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") == 0); + if (isWideVineVideo) { + flags |= DECFLAG_HW; + } +#endif - mDemuxerService->SetOption("FRAME_RENDERED", pts); + ret = mAVDeviceManager->setUpDecoder(flags, meta, nullptr, SMPAVDeviceManager::DEVICE_TYPE_AUDIO, 0); - if (mSet.bEnableVRC) { - mPNotifier->NotifyVideoRendered(timeMs, pts); + if (ret < 0) { + MediaPlayerEventType type = MEDIA_PLAYER_EVENT_AUDIO_DECODER_DEVICE_ERROR; + if (ret == gen_framework_errno(error_class_codec, codec_error_audio_not_support)) { + type = MEDIA_PLAYER_EVENT_AUDIO_CODEC_NOT_SUPPORT; } - - //TODO packetGotTime + AF_LOGE("setUpAudioDecoder error %d\n", ret); + mPNotifier->NotifyEvent(type, framework_err2_string(ret)); + return ret; } - // TODO: set layout when init videoRender? + return ret; +} - void SuperMediaPlayer::ProcessSetDisplayMode() - { - if (mVideoRender) { - mVideoRender->setScale(convertScaleMode(mSet.scaleMode)); +int SuperMediaPlayer::SetUpAudioPath() +{ + int ret = 0; + if (!mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_AUDIO)) { + + /* + * make sure the audio stream is opened before get stream meta, + * otherwise, will crash in hls stream + */ + if (mBufferController->IsPacketEmtpy(BUFFER_TYPE_AUDIO)) { + return 0; } - } + unique_ptr pMeta{}; + mDemuxerService->GetStreamMeta(pMeta, mCurrentAudioIndex, false); + Stream_meta *meta = (Stream_meta *) (pMeta.get()); - void SuperMediaPlayer::ProcessSetRotationMode() - { - if (mVideoRender) { - mVideoRender->setRotate(convertRotateMode(mSet.rotateMode)); + int64_t startTimeMs = af_getsteady_ms(); + + ret = setUpAudioDecoder(meta); + + int64_t costTimeMs = af_getsteady_ms() - startTimeMs; + mRecorderSet->createAudioDecoderCostMs = costTimeMs; + + if (ret < 0) { + return ret; } } - void SuperMediaPlayer::ProcessSetMirrorMode() - { - if (mVideoRender) { - mVideoRender->setFlip(convertMirrorMode(mSet.mirrorMode)); - } + if (mAudioFrameQue.empty() || mAVDeviceManager->isAudioRenderValid()) { + return 0; } - void SuperMediaPlayer::ProcessSetVideoBackgroundColor() - { - if (mVideoRender) { - mVideoRender->setBackgroundColor(mSet.mVideoBackgroundColor); - } + // IAFFrame::audioInfo info = ; + // info.channels = meta->channels; + // info.sample_rate = meta->samplerate; + // info.format = meta->sample_fmt; + // info.nb_samples = meta->frame_size; + // info.channel_layout = meta->channel_layout; + setUpAudioRender(mAudioFrameQue.front()->getInfo().audio); + return ret; +} + +int SuperMediaPlayer::setUpAudioRender(const IAFFrame::audioInfo &info) +{ + int ret = mAVDeviceManager->setUpAudioRender(info); + + if (ret < 0) { + AF_LOGE("AudioOutHandle Init Error is %d", ret); + // don't release audio handle because we only new it in constructor + // PS: we should try to recover it later, or notify error + // mAudioOutHandle = 0; + mCurrentAudioIndex = -1; + mCATimeBase = 0; + return -1; } + mAVDeviceManager->setAudioRenderListener(mAudioRenderCB.get()); + mAVDeviceManager->setSpeed(mSet->rate); + mAVDeviceManager->setMute(mSet->bMute); + mAVDeviceManager->setVolume(mSet->mVolume); - void SuperMediaPlayer::ProcessSetViewMsg(void *view) - { - if (view == mSet.mView) { - return; - } + if (!mSecretPlayBack) { + mAVDeviceManager->setAudioRenderingCb(mAudioRenderingCb, mAudioRenderingCbUserData); + } + return 0; +} - mSet.mView = view; - std::unique_lock uMutex(mCreateMutex); +int SuperMediaPlayer::SetUpVideoPath() +{ + if (mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_VIDEO) && (mAVDeviceManager->isVideoRenderValid())) { + return 0; + } - if (mVideoRender) { - mVideoRender->setDisPlay(view); - } + if (mBufferController->IsPacketEmtpy(BUFFER_TYPE_VIDEO)) { + return 0; } - void SuperMediaPlayer::ProcessSetDataSourceMsg(const std::string &url) - { - if (mPlayStatus == PLAYER_IDLE || mPlayStatus == PLAYER_STOPPED) { - mSet.url = url; - ChangePlayerStatus(PLAYER_INITIALZED); - } + if (mViewUpdateStatus == ViewUpdateStatus::Yes) { + return 0; } - void SuperMediaPlayer::ChangePlayerStatus(PlayerStatus newStatus) - { - mOldPlayStatus = mPlayStatus; + if (mSet->mView == nullptr && mFrameCb == nullptr) { + return 0; + } - if (mPlayStatus != newStatus) { - mPNotifier->NotifyPlayerStatusChanged(mPlayStatus, newStatus); - mPlayStatus = newStatus; - updateLoopGap(); - } + if (mVideoInterlaced == InterlacedType_UNKNOWN) { + AF_LOGW("Wait for parser video interlaced Type"); + return 0; } + /* + * update the video meta after the first video packet was reached, + * otherwise the video meta is incomplete when playing a master hls playList. + */ + updateVideoMeta(); + auto *meta = (Stream_meta *) (mCurrentVideoMeta.get()); + uint64_t flags = 0; - void SuperMediaPlayer::ResetSeekStatus() - { - mSeekPos = INT64_MIN; - mSeekNeedCatch = false; - updateLoopGap(); + if (isHDRVideo(meta)) { + /* + * HDR video must use mediaCodec to render direct on Android, + * we use a dummy render to release the frame simply + */ +#ifdef ANDROID + flags |= IVideoRender::FLAG_DUMMY; +#else + flags |= IVideoRender::FLAG_HDR; +#endif + } +#ifdef ANDROID + bool isWideVine = isWideVineVideo(meta); +#endif + /* + * HDR video, try use decoder to render first + */ + if (mSet->bEnableTunnelRender +#ifdef ANDROID + || isWideVine +#endif + ) { + flags |= IVideoRender::FLAG_DUMMY; } - void SuperMediaPlayer::ProcessSeekToMsg(int64_t seekPos, bool bAccurate) - { - // seek before prepare, should keep mSeekPos - if (mPlayStatus < PLAYER_PREPARING || - // if reuse player.. - mPlayStatus == PLAYER_STOPPED) { - return; - } + int ret = 0; - //can seek when finished - if ((0 >= mDuration) || (mPlayStatus >= PLAYER_STOPPED && mPlayStatus != PLAYER_COMPLETION)) { - ResetSeekStatus(); - return; + if (mSet->mView != nullptr && !mAVDeviceManager->isVideoRenderValid()) { + if (mAppStatus == APP_BACKGROUND) { + AF_LOGW("create video render in background"); + } + AF_LOGD("SetUpVideoRender start"); + CreateVideoRender(flags); + if (!mAVDeviceManager->isVideoRenderValid()) { + AF_LOGE("can't create video render\n"); + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_RENDER_INIT_ERROR, "init video render failed"); + return -EINVAL; } + } - updateLoopGap(); - //checkPosInPackQueue cache in seek - //TODO: seek sync - mSeekFlag = true; - mPlayedVideoPts = INT64_MIN; - mPlayedAudioPts = INT64_MIN; - mSoughtVideoPos = INT64_MIN; - mCurVideoPts = INT64_MIN; - //flush packet queue - mSeekInCache = SeekInCache(seekPos); + //re set view in case for not set view before + if (mSet->mView) { + if (mAVDeviceManager->isVideoRenderValid()) { + mAVDeviceManager->getVideoRender()->setDisPlay(mSet->mView); + } + } - mPNotifier->NotifySeeking(mSeekInCache); + if (mAVDeviceManager->isDecoderValid(SMPAVDeviceManager::DEVICE_TYPE_VIDEO)) { + return 0; + } - if (mSeekNeedCatch && !HAVE_VIDEO) { - mSeekNeedCatch = false; - } + AF_LOGD("SetUpVideoDecoder start"); - if (!mSeekInCache) { - mBufferController.ClearPacket(BUFFER_TYPE_ALL); - mDemuxerService->Seek(seekPos, 0, -1); - //in case of seekpos larger than duration. - mPNotifier->NotifyBufferPosition((seekPos <= mDuration ? seekPos : mDuration) / 1000); - mEof = false; + if (meta->interlaced == InterlacedType_UNKNOWN) { + meta->interlaced = mVideoInterlaced; + } - if ((mVideoChangedFirstPts != INT64_MAX) - && (INT64_MIN != mVideoChangedFirstPts)) { - mVideoChangedFirstPts = seekPos; + bool bHW = false; + if (mSet->bEnableHwVideoDecode) { + switch (meta->codec) { + case AF_CODEC_ID_H264: { + string value = getProperty("ro.video.dec.h264"); + bHW = !(value == "OFF"); + break; + } + case AF_CODEC_ID_HEVC: { + string value = getProperty("ro.video.dec.hevc"); + bHW = !(value == "OFF"); + break; } - } else { - AF_LOGI("sought in cache"); - if (mSeekNeedCatch) { - int64_t videoPos = mBufferController.GetKeyTimePositionBefore(BUFFER_TYPE_VIDEO, mSeekPos); + default: + bHW = true; + break; + } + } - if (videoPos < (mSeekPos - mSet.maxASeekDelta)) { - // first frame is far away from seek position, don't suppport accurate seek - mSeekNeedCatch = false; - } else { - mBufferController.ClearPacketBeforeTimePos(BUFFER_TYPE_AUDIO, mSeekPos); - } - } + int64_t startTimeMs = af_getsteady_ms(); + ret = CreateVideoDecoder(bHW, *meta); - if ((mVideoChangedFirstPts != INT64_MAX) - && (INT64_MIN != mVideoChangedFirstPts) - && (seekPos > mVideoChangedFirstPts)) { - mVideoChangedFirstPts = seekPos; - } + if (ret < 0) { + if (bHW) { + ret = CreateVideoDecoder(false, *meta); } + } + + int64_t costTimeMs = af_getsteady_ms() - startTimeMs; + mRecorderSet->createVideoDecoderCostMs = costTimeMs; - FlushVideoPath(); - FlushAudioPath(); - FlushSubtitleInfo(); + if (ret < 0) { + AF_LOGE("%s CreateVideoDecoder failed, error msg is %s", __FUNCTION__, framework_err2_string(ret)); - if (mSubPlayer) { - mSubPlayer->seek(seekPos); + if (ret == gen_framework_errno(error_class_codec, codec_error_video_not_support)) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_CODEC_NOT_SUPPORT, framework_err2_string(ret)); + } else if (ret == gen_framework_errno(error_class_codec, codec_error_video_device_error)) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_DECODER_DEVICE_ERROR, framework_err2_string(ret)); } - if ((seekPos / 1000 >= mDuration / 1000) && mDuration > 0) { - //if eof notifySeekEndMsg - ResetSeekStatus(); - notifySeekEndCallback(); - mSeekInCache = false; - mEof = true; - mSeekFlag = false; - return; + return ret; + } + + if (mAVDeviceManager->getVideoDecoderFlags() & DECFLAG_HW) { + } else { + if (mSet->bEnableHwVideoDecode) { + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SW_VIDEO_DECODER, "Switch to software video decoder"); } + } - mFirstBufferFlag = true; - mMasterClock.setTime(seekPos); + if (meta->duration > mDuration) { + mDuration = meta->duration; } - void SuperMediaPlayer::notifySeekEndCallback() - { - mPNotifier->NotifySeekEnd(mSeekInCache); + return ret; +} + +void SuperMediaPlayer::updateVideoMeta() +{ + mDemuxerService->GetStreamMeta(mCurrentVideoMeta, mCurrentVideoIndex, false); + auto *meta = (Stream_meta *) (mCurrentVideoMeta.get()); + if (mVideoWidth != meta->width || mVideoHeight != meta->height || mVideoRotation != meta->rotate) { + mVideoWidth = meta->width; + mVideoHeight = meta->height; + mVideoRotation = meta->rotate; + mPNotifier->NotifyVideoSizeChanged(mVideoWidth, mVideoHeight); } +} - void SuperMediaPlayer::ProcessMuteMsg() - { - if (mAudioRender == nullptr) { - return; - } +bool SuperMediaPlayer::CreateVideoRender(uint64_t flags) +{ + if (mAVDeviceManager->isVideoRenderValid()) { + return true; + } - mAudioRender->mute(mSet.bMute); + // lock mAppStatusMutex before mCreateMutex + std::lock_guard uMutex(mCreateMutex); + mAVDeviceManager->createVideoRender(flags); + if (!mAVDeviceManager->getVideoRender()) { + return false; } + mAVDeviceManager->getVideoRender()->setScale(convertScaleMode(mSet->scaleMode)); + mAVDeviceManager->getVideoRender()->setRotate(convertRotateMode(mSet->rotateMode)); + mAVDeviceManager->getVideoRender()->setBackgroundColor(mSet->mVideoBackgroundColor); + mAVDeviceManager->getVideoRender()->setFlip(convertMirrorMode(mSet->mirrorMode)); + mAVDeviceManager->getVideoRender()->setDisPlay(mSet->mView); + mAVDeviceManager->setVideoRenderListener(mVideoRenderListener.get()); + mAVDeviceManager->getVideoRender()->setRenderResultCallback( + [this](int64_t pts, bool rendered) -> void { VideoRenderCallback(this, pts, rendered, nullptr); }); + int renderRet = mAVDeviceManager->getVideoRender()->init(); - bool SuperMediaPlayer::IsMute() const - { - return mSet.bMute; + if (renderRet != 0) { + // for windows init failed, which may need change render type in future. + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_VIDEO_RENDER_INIT_ERROR, "init video render failed"); } - int SuperMediaPlayer::SetListener(const playerListener &Listener) - { - mSet.mPlayerListener = Listener; + mAVDeviceManager->setSpeed(mSet->rate); + return true; +} - if (mPNotifier) { - mPNotifier->setListener(Listener); - } +int SuperMediaPlayer::CreateVideoDecoder(bool bHW, Stream_meta &meta) +{ + int ret; + uint64_t decFlag = 0; - return 0; + if (bHW) { + decFlag |= DECFLAG_HW; + } else { + decFlag |= DECFLAG_SW; } - void SuperMediaPlayer::SetAutoPlay(bool bAutoPlay) - { - mAutoPlay = bAutoPlay; + if (mAdaptiveVideo) { + decFlag |= DECFLAG_ADAPTIVE; } - - bool SuperMediaPlayer::IsAutoPlay() - { - return mAutoPlay; + if (!mSet->bLowLatency) { + mSet->bLowLatency = mDemuxerService->getDemuxerHandle()->isLowLatency(); } - void SuperMediaPlayer::addExtSubtitle(const char *uri) - { - MsgParam param; - MsgDataSourceParam dataSourceParam = {nullptr}; - dataSourceParam.url = new string(uri ? uri : ""); - param.dataSourceParam = dataSourceParam; - putMsg(MSG_ADD_EXT_SUBTITLE, param); + if (mSet->bLowLatency) { + decFlag |= DECFLAG_OUTPUT_FRAME_ASAP; } - int SuperMediaPlayer::selectExtSubtitle(int index, bool bSelect) - { - if (!(index & EXT_STREAM_BASE)) { - AF_LOGE("select ext subtitle error\n"); - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SUBTITLE_SELECT_ERROR, "Not a ext stream"); - return -1; + void *view = nullptr; + mAVDeviceManager->flushVideoRender(); + + if (bHW) { + if (mAVDeviceManager->isVideoRenderValid() && mAVDeviceManager->getVideoRender()->getFlags() & IVideoRender::FLAG_DUMMY) { + view = mSet->mView; + decFlag |= DECFLAG_DIRECT; + } else { + if (mAVDeviceManager->isVideoRenderValid()) { + /* + * Get a cached surface, then the mediaCodec video decoder can be reuse, + * otherwise the mediaCodec video decoder will be recreate on setUpDecoder. + */ + view = mAVDeviceManager->getVideoRender()->getSurface(false); + } } + } - MsgParam param; - MsgSelectExtSubtitleParam track = {index, bSelect}; - param.msgSelectExtSubtitleParam = track; - putMsg(MSG_SELECT_EXT_SUBTITLE, param); - return 0; + if (!mSet->bLowLatency) { + mSet->bLowLatency = mDemuxerService->getDemuxerHandle()->isLowLatency(); } - void SuperMediaPlayer::ProcessAddExtSubtitleMsg(const std::string &url) + if (mSet->bLowLatency) { + decFlag |= DECFLAG_OUTPUT_FRAME_ASAP; + } + uint32_t dstFormat = 0; +#ifdef __APPLE__ + dstFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; + if (mFrameCb && mSet->pixelBufferOutputFormat) { + dstFormat = mSet->pixelBufferOutputFormat; + } else { + // TODO: move to VTB decoder + /* + * must set hdr video to output x420, otherwise vtb will output a p420 format + * if iOS version little than 14 that can't be displayed by AVSampleBufferDisplayLayer + */ + if (meta.pixel_fmt == AF_PIX_FMT_YUV420P10BE || meta.pixel_fmt == AF_PIX_FMT_YUV420P10LE) { + dstFormat = kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange; + } + } +#endif + ret = mAVDeviceManager->setUpDecoder(decFlag, (const Stream_meta *) (&meta), view, SMPAVDeviceManager::DEVICE_TYPE_VIDEO, dstFormat); + if (ret < 0) { + return ret; + } { - lock_guard uMutex(mCreateMutex); - - if (mSubPlayer == nullptr) { - mSubListener = unique_ptr(new mediaPlayerSubTitleListener(*mPNotifier)); - mSubPlayer = unique_ptr(new subTitlePlayer(*mSubListener)); + std::lock_guard lock(mAppStatusMutex); + mMsgCtrlListener->ProcessVideoHoldMsg(mAppStatus == APP_BACKGROUND); + } + return ret; +} + + +void SuperMediaPlayer::Reset() +{ + mCurrentVideoIndex = -1; + mCurrentAudioIndex = -1; + mCurrentSubtitleIndex = -1; + mVideoWidth = 0; + mVideoHeight = 0; + mVideoRotation = 0; + mDuration = INT64_MIN; + mBufferPosition = 0; + mSeekPos = INT64_MIN; + mPlayedVideoPts = INT64_MIN; + mPlayedAudioPts = INT64_MIN; + mSeekFlag = false; + mFirstAudioPts = INT64_MIN; + mFirstVideoPts = INT64_MIN; + mMediaStartPts = INT64_MIN; + mEof = false; + mFirstBufferFlag = true; + mBufferingFlag = false; + mCurVideoPts = INT64_MIN; + mLastAudioFrameDuration = INT64_MIN; + mTimeoutStartTime = INT64_MIN; + mSubtitleShowIndex = 0; + mWillChangedVideoStreamIndex = -1; + mWillChangedAudioStreamIndex = -1; + mWillChangedSubtitleStreamIndex = -1; + mBufferIsFull = false; + mWillSwitchVideo = false; + mMixMode = false; + mFirstRendered = false; + mInited = false; + mSeekNeedCatch = false; + mMainStreamId = -1; + mRemovedFirstAudioPts = INT64_MIN; + mFirstSeekStartTime = 0; + mAudioChangedFirstPts = INT64_MIN; + mVideoChangedFirstPts = INT64_MIN; + mSubtitleChangedFirstPts = INT64_MIN; + mSoughtVideoPos = INT64_MIN; + mFirstReadPacketSucMS = 0; + mCanceled = false; + mPNotifier->Enable(true); + FlushSubtitleInfo(); + mSubtitleShowedQueue.clear(); + mSubPlayer = nullptr; + mBSReadCb = nullptr; + mBSCbArg = nullptr; + mBSSeekCb = nullptr; + mBSCbArg = nullptr; + mUtil->reset(); + mDcaManager->reset(); + mVideoInterlaced = InterlacedType_UNKNOWN; + mVideoParserTimes = 0; + mVideoPtsRevert = mAudioPtsRevert = false; + mLowMem = false; + mCurrentVideoMeta = nullptr; + mAdaptiveVideo = false; + dropLateVideoFrames = false; + mBRendingStart = false; + mSubtitleEOS = false; + mPausedByAudioInterrupted = false; + mSecretPlayBack = false; + mDrmKeyValid = false; + mPtsDiscontinueDelta = INT64_MIN; + mCurrentPos = 0; + mCATimeBase = 0; + mWATimeBase = 0; + mViewUpdateStatus = ViewUpdateStatus::Unknown; +} + +int SuperMediaPlayer::GetCurrentStreamIndex(StreamType type) +{ + int streamIndex = -1; + + if (mMixMode && type != ST_TYPE_SUB) { + if (HAVE_VIDEO) { + streamIndex = GEN_STREAM_INDEX(mCurrentVideoIndex); + } else if (HAVE_AUDIO) { + streamIndex = GEN_STREAM_INDEX(mCurrentAudioIndex); } + } else { + switch (type) { + case ST_TYPE_AUDIO: + streamIndex = mCurrentAudioIndex; + break; - mSubPlayer->add(url); - } + case ST_TYPE_VIDEO: + streamIndex = mCurrentVideoIndex; + break; - void SuperMediaPlayer::ProcessSelectExtSubtitleMsg(int index, bool select) - { - lock_guard uMutex(mCreateMutex); + case ST_TYPE_SUB: + streamIndex = mCurrentSubtitleIndex; + break; - if (mSubPlayer == nullptr) { - AF_LOGE("select ext subtitle error\n"); - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SUBTITLE_SELECT_ERROR, "No such subtitle stream"); - return; + default: + break; } + } - int ret = mSubPlayer->select(index, select); + return streamIndex; +} - if (ret < 0) { - AF_LOGE("select ext subtitle error\n"); - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SUBTITLE_SELECT_ERROR, "No such subtitle stream"); - } +StreamInfo *SuperMediaPlayer::GetCurrentStreamInfo(StreamType type) +{ + int streamIndex = GetCurrentStreamIndex(type); - if (select) { - mSubPlayer->seek(mCurVideoPts - mMediaStartPts); + if (streamIndex != -1) { + for (StreamInfo *info : mStreamInfoQueue) { + if (info->streamIndex == streamIndex) { + return info; + } } } - void SuperMediaPlayer::ProcessVideoCleanFrameMsg() - { - while (!mVideoFrameQue.empty()) { - int64_t pts = mVideoFrameQue.front()->getInfo().pts; - ProcessVideoRenderedMsg(pts, af_getsteady_ms(), nullptr); - mVideoFrameQue.pop(); - } + return nullptr; +} - if (mVideoRender) { - unique_ptr frame = nullptr; - mVideoRender->renderFrame(frame); - } +void SuperMediaPlayer::VideoRenderCallback(void *arg, int64_t pts, bool rendered, void *userData) +{ + // AF_LOGD("video stream render pts is %lld", pts); + auto *pHandle = static_cast(arg); + + if (pHandle->mCanceled) { + return; + } - mPlayedVideoPts = INT64_MIN; - mCurVideoPts = INT64_MIN; - videoDecoderFull = false; - mVideoPtsRevert = false; - dropLateVideoFrames = true; + if ((PLAYER_PREPARED != pHandle->mPlayStatus) && (PLAYER_PAUSED != pHandle->mPlayStatus) && (PLAYER_PLAYING != pHandle->mPlayStatus)) { + return; } - void SuperMediaPlayer::ProcessVideoHoldMsg(bool hold) - { - if (mVideoDecoder) { - mVideoDecoder->holdOn(hold); + MsgParam param; + param.videoRenderedParam.pts = pts; + param.videoRenderedParam.rendered = rendered; + param.videoRenderedParam.timeMs = af_getsteady_ms(); + param.videoRenderedParam.userData = userData; + pHandle->putMsg(MSG_INTERNAL_VIDEO_RENDERED, param, false); +} + +void SuperMediaPlayer::checkFirstRender() +{ + if (!mFirstRendered) { + mFirstRendered = true; + AF_LOGI("Player NotifyFirstFrame"); + mPNotifier->NotifyFirstFrame(); + } +} + +void SuperMediaPlayer::ChangePlayerStatus(PlayerStatus newStatus) +{ + mOldPlayStatus = mPlayStatus; + + if (mPlayStatus != newStatus) { + mPNotifier->NotifyPlayerStatusChanged(mPlayStatus, newStatus); + mPlayStatus = newStatus; + } +} + +void SuperMediaPlayer::ResetSeekStatus() +{ + mSeekPos = INT64_MIN; + mSeekNeedCatch = false; +} + +void SuperMediaPlayer::notifySeekEndCallback() +{ + mPNotifier->NotifySeekEnd(mSeekInCache); +} + +bool SuperMediaPlayer::IsMute() const +{ + return mSet->bMute; +} + +int SuperMediaPlayer::SetListener(const playerListener &Listener) +{ + mSet->mPlayerListener = Listener; + + if (mPNotifier) { + mPNotifier->setListener(Listener); + } + + return 0; +} + +void SuperMediaPlayer::SetAutoPlay(bool bAutoPlay) +{ + mAutoPlay = bAutoPlay; +} + +bool SuperMediaPlayer::IsAutoPlay() +{ + return mAutoPlay; +} + +void SuperMediaPlayer::addExtSubtitle(const char *uri) +{ + MsgParam param; + MsgDataSourceParam dataSourceParam = {nullptr}; + dataSourceParam.url = new string(uri ? uri : ""); + param.dataSourceParam = dataSourceParam; + putMsg(MSG_ADD_EXT_SUBTITLE, param); +} + +int SuperMediaPlayer::selectExtSubtitle(int index, bool bSelect) +{ + if (!(index & EXT_STREAM_BASE)) { + AF_LOGE("select ext subtitle error\n"); + mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_SUBTITLE_SELECT_ERROR, "Not a ext stream"); + return -1; + } - if (!hold) { - int size = mVideoDecoder->getRecoverQueueSize(); + MsgParam param; + MsgSelectExtSubtitleParam track = {index, bSelect}; + param.msgSelectExtSubtitleParam = track; + putMsg(MSG_SELECT_EXT_SUBTITLE, param); + return 0; +} - if (size > mSet.maxVideoRecoverSize) { - string des = "video decoder recover size too large:" + AfString::to_string(size); - mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_DECODER_RECOVER_SIZE, des.c_str()); - } - } - } +int SuperMediaPlayer::setStreamDelay(int index, int64_t time) +{ + if (!(index & EXT_STREAM_BASE) || !mSubPlayer) { + AF_LOGE("setStreamDelay support ext subtitle only for now\n"); + return -ENOSYS; + } + if (mSubPlayer) { + mSubPlayer->setDelayTime(index, time * 1000); + mSubPlayer->seek(std::max(getCurrentPosition() + time * 1000, (int64_t) 0)); } + return 0; +} - void SuperMediaPlayer::ProcessSetSpeed(float speed) - { - if (!CicadaUtils::isEqual(mSet.rate, speed)) { - if (HAVE_AUDIO) { - if (mAudioRender != nullptr) { - mAudioRender->setSpeed(speed); - } - } +void SuperMediaPlayer::startRendering(bool start) +{ + if (start == mBRendingStart) { + return; + } - if (mVideoRender) { - mVideoRender->setSpeed(speed); - } + mBRendingStart = start; - mSet.rate = speed; - updateLoopGap(); - mMasterClock.SetScale(speed); - } + if (start) { + mMasterClock.start(); + } else { + mMasterClock.pause(); + } + mAVDeviceManager->pauseAudioRender(!start); +} +void SuperMediaPlayer::SetOnRenderCallBack(onRenderFrame cb, void *userData) +{ + mFrameCb = cb; + mFrameCbUserData = userData; +} + +void SuperMediaPlayer::SetAudioRenderingCallBack(onRenderFrame cb, void *userData) +{ + mAudioRenderingCb = cb; + mAudioRenderingCbUserData = userData; +} + +void SuperMediaPlayer::SetUpdateViewCB(UpdateViewCB cb, void *userData) +{ + mUpdateViewCB = cb; + mUpdateViewCBUserData = userData; +} + +int SuperMediaPlayer::invokeComponent(std::string content) +{ + return mDcaManager->invoke(content); +} + +void SuperMediaPlayer::setDrmRequestCallback(const std::function &drmCallback) { + mDrmManager->setDrmCallback(drmCallback); +} + +void SuperMediaPlayer::ProcessUpdateView() +{ + + if (mCurrentVideoIndex < 0) { + return; + } + + int videoTag = VideoTag::VIDEO_TAG_NONE; + + updateVideoMeta(); + auto *meta = (Stream_meta *) (mCurrentVideoMeta.get()); + + bool isHDR = isHDRVideo(meta); + if (isHDR) { + videoTag |= VideoTag::VIDEO_TAG_HDR10; + } + +#ifdef ANDROID + bool isWideVine = isWideVineVideo(meta); + if (isWideVine) { + //TODO set widevine level by user + videoTag |= VideoTag::VIDEO_TAG_WIDEVINE_L1; } +#endif - void SuperMediaPlayer::startRendering(bool start) { - if (start == mBRendingStart) { - return; + std::lock_guard lockGuard(mViewUpdateMutex); + + if (mViewUpdateStatus == ViewUpdateStatus::Unknown) { + if (mUpdateViewCB != nullptr) { + bool ret = mUpdateViewCB(videoTag, mUpdateViewCBUserData); + mViewUpdateStatus = ret ? ViewUpdateStatus::Yes : ViewUpdateStatus::No; + } else { + mViewUpdateStatus = ViewUpdateStatus::No; + } } + } +} - mBRendingStart = start; +bool SuperMediaPlayer::isWideVineVideo(const Stream_meta *meta) const +{ + bool isWideVineVideo = (meta->keyFormat != nullptr && strcmp(meta->keyFormat, "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed") == 0); + return isWideVineVideo; +} - if (start) { - mMasterClock.start(); - } else { - mMasterClock.pause(); - } +bool SuperMediaPlayer::isHDRVideo(const Stream_meta *meta) const +{ + bool isHDRVideo = false; - if (mAudioRender) { - mAudioRender->pause(!start); - } + if (meta->pixel_fmt == AF_PIX_FMT_YUV420P10BE || meta->pixel_fmt == AF_PIX_FMT_YUV420P10LE) { + AF_LOGI("HDR video\n"); + isHDRVideo = true; } - void SuperMediaPlayer::SetOnRenderCallBack(onRenderFrame cb, void *userData) - { - mFrameCb = cb; - mFrameCbUserData = userData; + return isHDRVideo; +} +float SuperMediaPlayer::getCurrentDownloadSpeed() +{ + return mUtil->getCurrentDownloadSpeed(); +} + +void SuperMediaPlayer::ApsaraAudioRenderCallback::onUpdateTimePosition(int64_t pos) +{ + if (!mPlayer.isSeeking() && pos >= 0) { + mPlayer.mCurrentPos = pos; } - int SuperMediaPlayer::invokeComponent(std::string content) - { - return mDcaManager.invoke(content); +} +void SuperMediaPlayer::ApsaraVideoRenderListener::onFrameInfoUpdate(IAFFrame::AFFrameInfo &info) +{ + if (mPlayer.mCurrentAudioIndex < 0 && info.timePosition >= 0 && !mPlayer.isSeeking()) { + mPlayer.mCurrentPos = info.timePosition; + // AF_LOGD("timePosition %lld\n", info.timePosition); } -}//namespace Cicada +} diff --git a/mediaPlayer/SuperMediaPlayer.h b/mediaPlayer/SuperMediaPlayer.h index 5ef9590f2..aab24b62f 100644 --- a/mediaPlayer/SuperMediaPlayer.h +++ b/mediaPlayer/SuperMediaPlayer.h @@ -16,9 +16,10 @@ using namespace std; #include #include "system_refer_clock.h" +#include "SMPAVDeviceManager.h" +#include "SMPMessageControllerListener.h" #include "SMP_DCAManager.h" #include "SuperMediaPlayerDataSourceListener.h" -#include "codec/videoDecoderFactory.h" #include "hls_adaptive_manager.h" #include "player_notifier.h" #include "player_types.h" @@ -28,8 +29,11 @@ using namespace std; #include #include +#include "CicadaPlayerPrototype.h" #include #include +#include +#include #ifdef __APPLE__ @@ -38,13 +42,9 @@ using namespace std; #endif #include "mediaPlayerSubTitleListener.h" +#include "SMPRecorderSet.h" namespace Cicada { - using namespace Cicada; -#define HAVE_VIDEO (mCurrentVideoIndex >= 0) -#define HAVE_AUDIO (mCurrentAudioIndex >= 0) -#define HAVE_SUBTITLE (mCurrentSubtitleIndex >= 0) - typedef struct streamTime_t { int64_t startTime; int64_t deltaTime; @@ -63,12 +63,17 @@ namespace Cicada { APP_BACKGROUND, } APP_STATUS; + enum class ViewUpdateStatus { + Unknown, + No, + Yes, + }; - class SuperMediaPlayer : public ICicadaPlayer, - private PlayerMessageControllerListener { + class SuperMediaPlayer : public ICicadaPlayer, private CicadaPlayerPrototype { friend class SuperMediaPlayerDataSourceListener; friend class SMP_DCAManager; + friend class SMPMessageControllerListener; public: @@ -76,10 +81,19 @@ namespace Cicada { ~SuperMediaPlayer() override; + string getName() override + { + return "SuperMediaPlayer"; + } + int SetListener(const playerListener &Listener) override; void SetOnRenderCallBack(onRenderFrame cb, void *userData) override; + void SetAudioRenderingCallBack(onRenderFrame cb, void *userData) override; + + void SetUpdateViewCB(UpdateViewCB cb, void *userData) override; + // TODO: use setParameters and setOpt to set void SetRefer(const char *refer) override; @@ -97,7 +111,10 @@ namespace Cicada { void GetOption(const char *key, char *value) override; - int64_t GetPlayingPosition() override; + int64_t GetPlayingPosition() override + { + return getCurrentPosition() / 1000; + }; int64_t GetBufferPosition() override; @@ -145,6 +162,8 @@ namespace Cicada { void SetDataSource(const char *url) override; + void setBitStreamCb(readCB read, seekCB seek, void *arg) override; + void Prepare() override; void SetVolume(float volume) override; @@ -191,16 +210,20 @@ namespace Cicada { int selectExtSubtitle(int index, bool bSelect) override; + int setStreamDelay(int index, int64_t time) override; + int invokeComponent(std::string content) override; + void setDrmRequestCallback(const std::function &drmCallback) override; + + float getCurrentDownloadSpeed() override; + private: void NotifyPosition(int64_t position); - int64_t GetCurrentPosition(); - void OnTimer(int64_t curTime); - void updateLoopGap(); + int updateLoopGap(); int mainService(); @@ -246,7 +269,7 @@ namespace Cicada { void ResetSeekStatus(); - static void VideoRenderCallback(void *arg, int64_t pts, void *userData); + static void VideoRenderCallback(void *arg, int64_t pts, bool rendered, void *userData); void Reset(); @@ -260,7 +283,7 @@ namespace Cicada { void SwitchVideo(int64_t startTime); - int64_t getPlayerBufferDuration(bool gotMax); + int64_t getPlayerBufferDuration(bool gotMax, bool internal); void ProcessOpenStreamInit(int streamIndex); @@ -276,8 +299,6 @@ namespace Cicada { void releaseStreamInfo(const StreamInfo *info) const; - int openUrl(); - // mSeekFlag will be set when processing (after remove from mMessageControl), it have gap bool isSeeking() { @@ -286,87 +307,89 @@ namespace Cicada { // void setRotationMode(RotateMode rotateMode, MirrorMode mirrorMode) const; - bool CreateVideoRender(); + bool CreateVideoRender(uint64_t flags); int CreateVideoDecoder(bool bHW, Stream_meta &meta); int64_t getCurrentPosition(); - void switchSubTitle(int index); - - void switchAudio(int index); - - void switchVideoStream(int index, Stream_type type); - void checkEOS(); + void playCompleted(); + void notifySeekEndCallback(); void notifyPreparedCallback(); - class ApsaraAudioRenderCallback : public IAudioRenderListener { - public: - ApsaraAudioRenderCallback(SuperMediaPlayer &player) - : mPlayer(player) - {} + void updateVideoMeta(); - void onEOS() override - {} - - void onInterrupt(bool interrupt) override - { - if (interrupt) { - mPlayer.Pause(); - } else { - mPlayer.Start(); - } - } - - private: - SuperMediaPlayer &mPlayer; - }; + void doDeCode(); - private: - void checkFirstRender(); + void doRender(); - bool OnPlayerMsgIsPadding(PlayMsgType msg, MsgParam msgContent) final; + void doReadPacket(); - void ProcessPrepareMsg() final; + int setUpAudioRender(const IAFFrame::audioInfo &info); - void ProcessStartMsg() final; + std::atomic mCurrentPos{}; - void ProcessSetDisplayMode() final; + void printTimePosition(int64_t time) const; - void ProcessSetRotationMode() final; + void setUpAVPath(); - void ProcessSetMirrorMode() final; + void startRendering(bool start); - void ProcessSetVideoBackgroundColor() final; + void sendDCAMessage(); - void ProcessSetViewMsg(void *view) final; + void ProcessUpdateView(); - void ProcessSetDataSourceMsg(const std::string &url) final; + bool isHDRVideo(const Stream_meta *meta) const; - void ProcessPauseMsg() final; + bool isWideVineVideo(const Stream_meta *meta) const; - void ProcessSeekToMsg(int64_t seekPos, bool bAccurate) final; + void closeVideo(); - void ProcessMuteMsg() final; + void checkFirstRender(); - void ProcessSwitchStreamMsg(int index) final; + class ApsaraAudioRenderCallback : public IAudioRenderListener { + public: + explicit ApsaraAudioRenderCallback(SuperMediaPlayer &player) : mPlayer(player) + {} - void ProcessVideoRenderedMsg(int64_t pts, int64_t timeMs, void *picUserData) final; + void onEOS() override + {} - void ProcessVideoCleanFrameMsg() final; + bool onInterrupt(bool interrupt) override + { + if (interrupt) { + if (mPlayer.mPlayStatus == PLAYER_PLAYING) { + mPlayer.Pause(); + mPlayer.mPausedByAudioInterrupted = true; + } + } else { + if (mPlayer.mPlayStatus == PLAYER_PAUSED && mPlayer.mPausedByAudioInterrupted) { + mPlayer.Start(); + } + } - void ProcessVideoHoldMsg(bool hold) final; + return true; + } + void onUpdateTimePosition(int64_t pos) override; - void ProcessAddExtSubtitleMsg(const std::string &url) final; + private: + SuperMediaPlayer &mPlayer; + }; - void ProcessSelectExtSubtitleMsg(int index, bool select) final; + class ApsaraVideoRenderListener : public IVideoRender::IVideoRenderListener { - void ProcessSetSpeed(float speed) final; + public: + explicit ApsaraVideoRenderListener(SuperMediaPlayer &player) : mPlayer(player) + {} + void onFrameInfoUpdate(IAFFrame::AFFrameInfo &info) override; + private: + SuperMediaPlayer &mPlayer; + }; private: static IVideoRender::Scale convertScaleMode(ScaleMode mode); @@ -376,32 +399,45 @@ namespace Cicada { static IVideoRender::Flip convertMirrorMode(MirrorMode mode); + public: + static bool is_supported(const options *opts) + { + return true; + } + + private: + explicit SuperMediaPlayer(int dummy) + { + mIsDummy = true; + addPrototype(this); + } + ICicadaPlayer *clone() override + { + return new SuperMediaPlayer(); + }; + + static SuperMediaPlayer se; + + private: IDataSource *mDataSource{nullptr}; std::atomic_bool mCanceled{false}; demuxer_service *mDemuxerService{nullptr}; - - std::unique_ptr mVideoDecoder{}; + std::unique_ptr mDrmManager{}; std::queue> mVideoFrameQue{}; - - std::unique_ptr mAudioDecoder{}; std::deque> mAudioFrameQue{}; unique_ptr mCurrentVideoMeta{}; bool videoDecoderEOS = false; bool audioDecoderEOS = false; picture_cache_type mPictureCacheType = picture_cache_type_cannot; bool videoDecoderFull = false; - PlayerMessageControl mMessageControl; - ApsaraAudioRenderCallback mAudioRenderCB; - BufferController mBufferController; - + std::unique_ptr mMsgCtrlListener{nullptr}; + std::unique_ptr mMessageControl{nullptr}; + std::unique_ptr mAudioRenderCB{nullptr}; + std::unique_ptr mVideoRenderListener{nullptr}; + std::unique_ptr mBufferController{nullptr}; std::mutex mAppStatusMutex; std::atomic mAppStatus{APP_FOREGROUND}; - std::unique_ptr mAudioRender{}; - std::unique_ptr mVideoRender{}; -//#ifdef WIN32 -// AlivcDxva2Render m_dxva2Render; -//#endif int mVideoWidth{0}; int mVideoHeight{0}; int mVideoRotation{0}; @@ -419,6 +455,8 @@ namespace Cicada { int mWillChangedVideoStreamIndex{-1}; int mWillChangedAudioStreamIndex{-1}; int mWillChangedSubtitleStreamIndex{-1}; + float mCATimeBase{}; // current audio stream origin pts time base + float mWATimeBase{}; // willChange audio stream origin pts time base int mRemainLiveSegment{0};// To avoid access demuxer multi-thread bool mInited{false}; atomic_bool mSeekNeedCatch{false}; @@ -457,11 +495,8 @@ namespace Cicada { int64_t mSubtitleShowIndex{0}; bool mBufferIsFull{false}; bool mWillSwitchVideo{false}; - player_type_set mSet; + std::unique_ptr mSet{}; int64_t mSoughtVideoPos{INT64_MIN}; - std::atomic mPlayingPosition{0}; - - int mMaxRunningLoopGap = 10; int mTimerInterval = 0; int64_t mTimerLatestTime = 0; std::mutex mCreateMutex{}; // need lock if access pointer outside of loop thread @@ -469,59 +504,43 @@ namespace Cicada { std::mutex mSleepMutex{}; std::condition_variable mPlayerCondition; PlayerNotifier *mPNotifier = nullptr; - afThread mApsaraThread; + std::unique_ptr mApsaraThread{}; int mLoadingProcess{0}; int64_t mPrepareStartTime = 0; - int mVideoParserTimes = 0; InterlacedType mVideoInterlaced = InterlacedType_UNKNOWN; bitStreamParser *mVideoParser = nullptr; - - MediaPlayerUtil mUtil; - - SuperMediaPlayerDataSourceListener mSourceListener; - SMP_DCAManager mDcaManager; - + int64_t mPtsDiscontinueDelta{INT64_MIN}; + std::unique_ptr mUtil{}; + std::unique_ptr mSourceListener{nullptr}; + std::unique_ptr mDcaManager{nullptr}; + std::unique_ptr mAVDeviceManager{nullptr}; std::unique_ptr mVideoPacket{}; std::unique_ptr mAudioPacket{}; std::unique_ptr mSubListener; std::unique_ptr mSubPlayer; - bool dropLateVideoFrames = false; bool waitingForStart = false; bool mBRendingStart {false}; bool mSecretPlayBack{false}; - - private: - + bool mDrmKeyValid{false}; + std::unique_ptr mRecorderSet{nullptr}; bool mAutoPlay = false; - - void doDeCode(); - - void doRender(); - - void doReadPacket(); - - int setUpAudioRender(const IAFFrame::audioInfo &info); - - int64_t mCurrentPos = 0; - - void printTimePosition(int64_t time) const; - - void setUpAVPath(); - - void startRendering(bool start); - int64_t mCheckAudioQueEOSTime{INT64_MIN}; uint64_t mAudioQueDuration{UINT64_MAX}; - onRenderFrame mFrameCb{nullptr}; void *mFrameCbUserData{nullptr}; + std::mutex mViewUpdateMutex{}; + std::atomic mViewUpdateStatus{ViewUpdateStatus::Unknown}; + UpdateViewCB mUpdateViewCB{nullptr}; + void *mUpdateViewCBUserData{nullptr}; + onRenderFrame mAudioRenderingCb{nullptr}; + void *mAudioRenderingCbUserData{nullptr}; + bool mIsDummy{false}; + bool mPausedByAudioInterrupted{false}; + readCB mBSReadCb = nullptr; + seekCB mBSSeekCb = nullptr; + void *mBSCbArg = nullptr; }; }// namespace Cicada -#endif // CICADA_PLAYER_SERVICE_H - - - - - +#endif// CICADA_PLAYER_SERVICE_H diff --git a/mediaPlayer/SuperMediaPlayerDataSourceListener.cpp b/mediaPlayer/SuperMediaPlayerDataSourceListener.cpp index 1260af352..5246706e9 100644 --- a/mediaPlayer/SuperMediaPlayerDataSourceListener.cpp +++ b/mediaPlayer/SuperMediaPlayerDataSourceListener.cpp @@ -4,11 +4,12 @@ #define LOG_TAG "apsaraDataSourceListener" -#include -#include #include "SuperMediaPlayerDataSourceListener.h" -#include "media_player_error_def.h" #include "SuperMediaPlayer.h" +#include "media_player_error_def.h" +#include +#include +#include using namespace Cicada; namespace Cicada { @@ -31,6 +32,21 @@ namespace Cicada { return NetWorkRetryStatusRetry; } + if (mPlayer.mSet->netWorkRetryCount > 0) { + if (mRetryCount < mPlayer.mSet->netWorkRetryCount) { + enableRetry_l(); + mRetryCount++; + return NetWorkRetryStatusRetry; + } else { + if (!bWaitingForRet) { + string err = "network retry timeout for " + AfString::to_string(mPlayer.mSet->netWorkRetryCount) + " times"; + mPlayer.mPNotifier->NotifyError(MEDIA_PLAYER_ERROR_LOADING_TIMEOUT, err.c_str()); + bWaitingForRet = true; + } + return NetWorkRetryStatusPending; + } + } + if (!bWaitingForRet) { mPlayer.mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_NETWORK_RETRY, ""); AF_LOGD("MEDIA_PLAYER_EVENT_NETWORK_RETRY"); @@ -48,7 +64,7 @@ namespace Cicada { void SuperMediaPlayerDataSourceListener::enableRetry_l() { - mEffectiveRetryTime = af_getsteady_ms() + mPlayer.mSet.timeout_ms; + mEffectiveRetryTime = af_getsteady_ms() + mPlayer.mSet->timeout_ms; bWaitingForRet = false; } @@ -57,7 +73,11 @@ namespace Cicada { if (!mNetworkConnected) { AF_LOGD("onNetWorkRetry successful\n"); mNetworkConnected = true; - mPlayer.mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_NETWORK_RETRY_SUCCESS, ""); + mRetryCount = 0; + + if (mPlayer.mSet->netWorkRetryCount <= 0) { + mPlayer.mPNotifier->NotifyEvent(MEDIA_PLAYER_EVENT_NETWORK_RETRY_SUCCESS, ""); + } } } diff --git a/mediaPlayer/SuperMediaPlayerDataSourceListener.h b/mediaPlayer/SuperMediaPlayerDataSourceListener.h index b8b5e2551..1b21422df 100644 --- a/mediaPlayer/SuperMediaPlayerDataSourceListener.h +++ b/mediaPlayer/SuperMediaPlayerDataSourceListener.h @@ -35,6 +35,9 @@ namespace Cicada { return mNetworkConnected; } + private: + void enableRetry_l(); + private: SuperMediaPlayer &mPlayer; NetWorkRetryStatus mStatus = NetWorkRetryStatusRetry; @@ -42,8 +45,7 @@ namespace Cicada { int64_t mEffectiveRetryTime = 0; std::mutex mMutex; std::atomic_bool mNetworkConnected{false}; - - void enableRetry_l(); + int mRetryCount{0}; }; } diff --git a/mediaPlayer/analytics/AnalyticsCollectorImpl.cpp b/mediaPlayer/analytics/AnalyticsCollectorImpl.cpp index 1556baa28..a880bca25 100644 --- a/mediaPlayer/analytics/AnalyticsCollectorImpl.cpp +++ b/mediaPlayer/analytics/AnalyticsCollectorImpl.cpp @@ -30,6 +30,15 @@ namespace Cicada { mListener.remove(listener); } + void AnalyticsCollectorImpl::ReportUpdatePlaySession(const std::string &sessionId) + { + for (AnalyticsCollectorListener *iter : mListener) { + if (nullptr != iter) { + iter->OnPlayerUpdateSessionId(sessionId); + } + } + } + // setting void AnalyticsCollectorImpl::ReportLooping(bool looping) { diff --git a/mediaPlayer/analytics/AnalyticsCollectorImpl.h b/mediaPlayer/analytics/AnalyticsCollectorImpl.h index c4cc8da8f..e664583ee 100644 --- a/mediaPlayer/analytics/AnalyticsCollectorImpl.h +++ b/mediaPlayer/analytics/AnalyticsCollectorImpl.h @@ -40,6 +40,8 @@ namespace Cicada { { return &mData; }; // IAnalyticPlayer + void ReportUpdatePlaySession(const std::string &sessionId) override; + // setting void ReportLooping(bool looping) override; diff --git a/mediaPlayer/analytics/AnalyticsCollectorListener.h b/mediaPlayer/analytics/AnalyticsCollectorListener.h index a22cd1d1a..46ab210b9 100644 --- a/mediaPlayer/analytics/AnalyticsCollectorListener.h +++ b/mediaPlayer/analytics/AnalyticsCollectorListener.h @@ -16,6 +16,9 @@ namespace Cicada { class AnalyticsCollectorListener { public: + + virtual void OnPlayerUpdateSessionId(const std::string& sessionId) {}; + virtual void OnSetLooping(bool looping) {}; virtual void OnSetRenderMirrorMode(MirrorMode mode) {}; diff --git a/mediaPlayer/analytics/IAnalyticsPlayer.h b/mediaPlayer/analytics/IAnalyticsPlayer.h index 6cb243325..84c064114 100644 --- a/mediaPlayer/analytics/IAnalyticsPlayer.h +++ b/mediaPlayer/analytics/IAnalyticsPlayer.h @@ -45,6 +45,8 @@ namespace Cicada { virtual void ReportError(int errCode, const std::string &errMsg, const std::string &requestID) = 0; + virtual void ReportUpdatePlaySession(const std::string &sessionId) = 0; + }; }// namespace Cicada diff --git a/mediaPlayer/buffer_controller.cpp b/mediaPlayer/buffer_controller.cpp index d87807b2a..c7d2059ea 100644 --- a/mediaPlayer/buffer_controller.cpp +++ b/mediaPlayer/buffer_controller.cpp @@ -41,6 +41,39 @@ namespace Cicada { } } + void BufferController::SetOnePacketDuration(BUFFER_TYPE type, int64_t duration) { + switch (type) { + case BUFFER_TYPE_AUDIO: + mAudioPacketQueue.SetOnePacketDuration(duration); + break; + case BUFFER_TYPE_VIDEO: + mVideoPacketQueue.SetOnePacketDuration(duration); + break; + case BUFFER_TYPE_SUBTITLE: + break; + default: + AF_LOGE("error media type"); + break; + } + } + + + int64_t BufferController::GetOnePacketDuration(BUFFER_TYPE type) { + switch (type) { + case BUFFER_TYPE_AUDIO: + return mAudioPacketQueue.GetOnePacketDuration(); + case BUFFER_TYPE_VIDEO: + return mVideoPacketQueue.GetOnePacketDuration(); + case BUFFER_TYPE_SUBTITLE: + break; + default: + AF_LOGE("error media type"); + break; + } + return INT64_MIN; + } + + int64_t BufferController::GetPacketPts(BUFFER_TYPE type) { switch (type) { @@ -322,4 +355,5 @@ namespace Cicada { } } + }//namespace Cicada diff --git a/mediaPlayer/buffer_controller.h b/mediaPlayer/buffer_controller.h index aff8b42c1..33461c606 100644 --- a/mediaPlayer/buffer_controller.h +++ b/mediaPlayer/buffer_controller.h @@ -27,6 +27,10 @@ namespace Cicada { public: int64_t GetPacketDuration(BUFFER_TYPE type); + void SetOnePacketDuration(BUFFER_TYPE type, int64_t duration); + + int64_t GetOnePacketDuration(BUFFER_TYPE type); + int GetPacketSize(BUFFER_TYPE type); bool IsPacketEmtpy(BUFFER_TYPE type); diff --git a/mediaPlayer/externalPlayer/AppleAVPlayer.h b/mediaPlayer/externalPlayer/AppleAVPlayer.h new file mode 100644 index 000000000..651429d7e --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayer.h @@ -0,0 +1,221 @@ +// +// Created by moqi on 2020/7/20. +// + +#ifndef CICADAMEDIA_APPLE_AVPLAYER_H +#define CICADAMEDIA_APPLE_AVPLAYER_H + +#include "native_cicada_player_def.h" +#import + +#include "../CicadaPlayerPrototype.h" +namespace Cicada { + class AppleAVPlayer final : public ICicadaPlayer, private CicadaPlayerPrototype { + public: + AppleAVPlayer(); + ~AppleAVPlayer() final; + + string getName() override + { + return "AppleAVPlayer"; + } + + int SetListener(const playerListener &Listener) override; + + void SetOnRenderCallBack(onRenderFrame cb, void *userData) override; + + void SetView(void *view) override; + + void SetDataSource(const char *url) override; + + void Prepare() override; + + void Start() override; + + void Pause() override; + + + StreamType SwitchStream(int index) override; + + void SeekTo(int64_t seekPos, bool bAccurate) override; + + int Stop() override; + + PlayerStatus GetPlayerStatus() const override; + + int64_t GetDuration() const override; + + int64_t GetPlayingPosition() override; + + int64_t GetBufferPosition() override; + + void Mute(bool bMute) override; + + bool IsMute() const override; + + void SetVolume(float volume) override; + + float GetVideoRenderFps() override; + + void EnterBackGround(bool back) override; + + void SetScaleMode(ScaleMode mode) override; + + ScaleMode GetScaleMode() override; + + void SetRotateMode(RotateMode mode) override; + + RotateMode GetRotateMode() override; + + void SetMirrorMode(MirrorMode mode) override; + + void SetVideoBackgroundColor(uint32_t color) override; + + MirrorMode GetMirrorMode() override; + + int GetCurrentStreamIndex(StreamType type) override; + + StreamInfo *GetCurrentStreamInfo(StreamType type) override; + + int64_t GetMasterClockPts() override; + + void SetTimeout(int timeout) override; + + void SetDropBufferThreshold(int dropValue) override; + + void SetDecoderType(DecoderType type) override; + + DecoderType GetDecoderType() override; + + float GetVolume() const override; + + void SetRefer(const char *refer) override; + + void SetUserAgent(const char *userAgent) override; + + void SetLooping(bool bCirclePlay) override; + + bool isLooping() override; + + void CaptureScreen() override; + + void GetVideoResolution(int &width, int &height) override; + + void GetVideoRotation(int &rotation) override; + + std::string GetPropertyString(PropertyKey key) override; + + int64_t GetPropertyInt(PropertyKey key) override; + + long GetPropertyLong(int key) override + { + return 0; + }; + + float GetVideoDecodeFps() override; + + int SetOption(const char *key, const char *value) override; + + void GetOption(const char *key, char *value) override; + + void setSpeed(float speed) override; + + float getSpeed() override; + + void AddCustomHttpHeader(const char *httpHeader) override; + + void RemoveAllCustomHttpHeader() override; + + void addExtSubtitle(const char *uri) override; + + int selectExtSubtitle(int index, bool bSelect) override; + + int setStreamDelay(int index, int64_t time) override; + + int getCurrentStreamMeta(Stream_meta *meta, StreamType type) override; + + void reLoad() override; + + void SetAutoPlay(bool bAutoPlay) override; + + bool IsAutoPlay() override; + + int invokeComponent(std::string content) override; + + void SetAudioRenderingCallBack(onRenderFrame cb, void *userData) + {} + + void SetUpdateViewCB(UpdateViewCB cb, void *userData) override + {} + + void setDrmRequestCallback(const std::function &drmCallback) override + {} + + float getCurrentDownloadSpeed() override + { + return 0; + } + + public: + static bool is_supported(const options *opts) + { + if (opts) { + string name = opts->get("name"); + if (!name.empty()) { + if (name == "AppleAVPlayer") { + return true; + } else { + return false; + } + } + } + return false; + } + + private: + ICicadaPlayer *clone() override + { + return new AppleAVPlayer(); + } + explicit AppleAVPlayer(int dummy) + { + addPrototype(this); + mIsDummy = true; + } + + int probeScore(const options *opts) override + { + if (is_supported(opts)) { + return SUPPORT_MAX; + } + return SUPPORT_NOT; + } + + static AppleAVPlayer se; + + void recheckHander(); + + void UpdatePlayerStatus(PlayerStatus status); + + private: + mutable std::recursive_mutex mCreateMutex{}; + void *avPlayer{nullptr}; + void *playerHandler{nullptr}; + playerListener mListener{nullptr}; + float recordVolume = 0.0; + StreamInfo **mStreamInfos{nullptr}; + + void *resourceLoaderDelegate{nullptr}; + void *parentLayer{nullptr}; + void *sourceUrl{nullptr}; + void *subtitleUrl{nullptr}; + bool isAutoPlay = false; + + bool mIsDummy{false}; + PlayerStatus mStatus{PLAYER_IDLE}; + + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_APPLE_AVPLAYER_H diff --git a/mediaPlayer/externalPlayer/AppleAVPlayer.mm b/mediaPlayer/externalPlayer/AppleAVPlayer.mm new file mode 100644 index 000000000..bd47fad54 --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayer.mm @@ -0,0 +1,709 @@ +// +// Created by moqi on 2020/7/20. +// + +#ifdef __APPLE__ +#include +#include +#endif + +#import +#import +#if TARGET_OS_IPHONE +#import +#endif +#include "AppleAVPlayer.h" +#include "AppleAVPlayerHandler.h" +#include "AppleAVPlayerLayerProcessor.h" +#include "AppleAVPlayerUtil.h" + +using namespace Cicada; + +AppleAVPlayer AppleAVPlayer::se(1); +AppleAVPlayer::AppleAVPlayer() +{} + +AppleAVPlayer::~AppleAVPlayer() +{ + if (mIsDummy) { + return; + } + Stop(); + if (this->parentLayer) { + CFRelease(this->parentLayer); + } + this->parentLayer = nullptr; +} + +int AppleAVPlayer::SetListener(const playerListener &Listener) +{ + this->mListener = Listener; + lock_guard lock(mCreateMutex); + if (this->playerHandler != NULL) { + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + [playerHandler setmPlayerListener:Listener]; + } + return 0; +} + +void AppleAVPlayer::SetOnRenderCallBack(onRenderFrame cb, void *userData) +{} + +void AppleAVPlayer::recheckHander() +{ + lock_guard lock(mCreateMutex); + if (this->playerHandler == NULL) { + return; + } + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (this->parentLayer != NULL) { + playerHandler.parentLayer = (__bridge CALayer *) this->parentLayer; + } + if (this->avPlayer != NULL) { + playerHandler.avplayer = (__bridge AVPlayer *) this->avPlayer; + } + [playerHandler setmPlayerListener:this->mListener]; +} + +void AppleAVPlayer::SetView(void *view) +{ + { + lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (this->parentLayer) { + if (playerHandler) { + [playerHandler removePlayerLayer]; + } + CFRelease(this->parentLayer); + } + this->parentLayer = view; + } + if (view) { + CFRetain(view); + this->recheckHander(); + } +} + +void AppleAVPlayer::SetDataSource(const char *url) +{ + // this->sourceUrl = (char *)url; + NSString *urlString = [[NSString alloc] initWithUTF8String:url]; + { + std::lock_guard lock(mCreateMutex); + this->sourceUrl = (__bridge_retained void *) urlString; + } + // NSLog(@"SetDataSource url : %@", urlString); + UpdatePlayerStatus(PLAYER_INITIALZED); +} + +void AppleAVPlayer::UpdatePlayerStatus(PlayerStatus status) +{ + if (mListener.StatusChanged) { + mListener.StatusChanged(mStatus, status, mListener.userData); + } + + mStatus = status; +} + +void AppleAVPlayer::Prepare() +{ +#if TARGET_OS_IPHONE + AFAudioSessionWrapper::activeAudio(); +#endif + + NSString *urlString = @""; + { + lock_guard lock(mCreateMutex); + if (this->sourceUrl) { + urlString = (__bridge NSString *) this->sourceUrl; + } + } + + NSLog(@"Prepare url : %@", urlString); + NSURL *mediaURL = [NSURL URLWithString:urlString]; + AVURLAsset *asset = [AVURLAsset assetWithURL:mediaURL]; + + AVURLAsset *subtitleAsset = nil; + if (this->subtitleUrl) { + NSString *subtitleUrlString = (__bridge NSString *) this->subtitleUrl; + NSURL *subtitleURL = [NSURL URLWithString:subtitleUrlString]; + subtitleAsset = [AVURLAsset assetWithURL:subtitleURL]; + } + + AVPlayerItem *item = nil; + if (subtitleAsset && subtitleAsset.tracks.count > 0) { + AVMutableComposition *mutableComposition = [[AVMutableComposition alloc] init]; + // origin tracks + for (AVAssetTrack *track in asset.tracks) { + AVMutableCompositionTrack *compositionTrack = [mutableComposition addMutableTrackWithMediaType:track.mediaType + preferredTrackID:track.trackID]; + [compositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:track atTime:kCMTimeZero error:nil]; + } + // subtitle + for (AVAssetTrack *track in subtitleAsset.tracks) { + AVMutableCompositionTrack *compositionTrack = [mutableComposition addMutableTrackWithMediaType:track.mediaType + preferredTrackID:track.trackID]; + [compositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:track atTime:kCMTimeZero error:nil]; + } + item = [AVPlayerItem playerItemWithAsset:mutableComposition]; + } else { + { + lock_guard lock(mCreateMutex); + if (this->resourceLoaderDelegate) { + [asset.resourceLoader setDelegate:(__bridge id) this->resourceLoaderDelegate + queue:dispatch_get_main_queue()]; + } + } + item = [AVPlayerItem playerItemWithAsset:asset]; + } + + UpdatePlayerStatus(PLAYER_PREPARING); + + AppleAVPlayerHandler *playerHandler = [[AppleAVPlayerHandler alloc] init]; + AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:item]; + { + std::lock_guard lock(mCreateMutex); + this->avPlayer = (__bridge_retained void *) player; + this->playerHandler = (__bridge_retained void *) playerHandler; + } + this->recheckHander(); + + // 初始化轨道信息 + NSArray *array = [asset availableMediaCharacteristicsWithMediaSelectionOptions]; + NSMutableArray *selectionOptionArray = [NSMutableArray array]; + [array enumerateObjectsUsingBlock:^(AVMediaCharacteristic _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + AVMediaSelectionGroup *mediaGroup = [asset mediaSelectionGroupForMediaCharacteristic:obj]; + [mediaGroup.options enumerateObjectsUsingBlock:^(AVMediaSelectionOption *_Nonnull options, NSUInteger idx, BOOL *_Nonnull stop) { + [selectionOptionArray addObject:options]; + }]; + }]; + playerHandler.selectionOptionArray = selectionOptionArray; + + int size = (int) selectionOptionArray.count; + { + std::lock_guard lock(mCreateMutex); + + this->mStreamInfos = new StreamInfo *[size]; + [selectionOptionArray enumerateObjectsUsingBlock:^(AVMediaSelectionOption *_Nonnull options, NSUInteger idx, BOOL *_Nonnull stop) { + auto *info = new StreamInfo(); + info->streamIndex = (int) idx; + if (options.displayName != nullptr) { + info->description = strdup((const char *) [options.displayName UTF8String]); + } + + if ([options.mediaType isEqualToString:AVMediaTypeSubtitle]) { + info->type = ST_TYPE_SUB; + if (options.extendedLanguageTag != nullptr) { + info->subtitleLang = strdup((const char *) [options.extendedLanguageTag UTF8String]); + } + } else if ([options.mediaType isEqualToString:AVMediaTypeAudio]) { + info->type = ST_TYPE_AUDIO; + if (options.extendedLanguageTag != nullptr) { + info->audioLang = strdup((const char *) [options.extendedLanguageTag UTF8String]); + } + } else if ([options.mediaType isEqualToString:AVMediaTypeVideo]) { + info->type = ST_TYPE_VIDEO; + } + this->mStreamInfos[idx] = info; + }]; + } + + if (this->mListener.StreamInfoGet) { + this->mListener.StreamInfoGet((int64_t) size, this->mStreamInfos, this->mListener.userData); + } + + if (this->isAutoPlay) { + [player play]; + UpdatePlayerStatus(PLAYER_PLAYING); + } +} + +void AppleAVPlayer::Start() +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player) { + [player play]; + UpdatePlayerStatus(PLAYER_PLAYING); + } +} + +void AppleAVPlayer::Pause() +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player) { + [player pause]; + UpdatePlayerStatus(PLAYER_PAUSED); + } +} + +StreamType AppleAVPlayer::SwitchStream(int index) +{ + { + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (playerHandler == nullptr || player == nullptr) { + return ST_TYPE_UNKNOWN; + } + + NSArray *optionArray = playerHandler.selectionOptionArray; + AVMediaSelectionOption *option = optionArray[index]; + AVMediaSelectionGroup *mediaGroup = nil; + AVAsset *asset = player.currentItem.asset; + if ([option.mediaType isEqualToString:AVMediaTypeSubtitle]) { + mediaGroup = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible]; + } else if ([option.mediaType isEqualToString:AVMediaTypeAudio]) { + mediaGroup = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible]; + } else if ([option.mediaType isEqualToString:AVMediaTypeVideo]) { + mediaGroup = [asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicVisual]; + } + [player.currentItem selectMediaOption:option inMediaSelectionGroup:mediaGroup]; + } + + StreamType type = ST_TYPE_UNKNOWN; + { + lock_guard lock(mCreateMutex); + + if (mStreamInfos) { + StreamInfo *info = this->mStreamInfos[index]; + type = info->type; + if (this->mListener.StreamSwitchSuc) { + this->mListener.StreamSwitchSuc(type, info, this->mListener.userData); + } + } + } + return type; +} + +void AppleAVPlayer::SeekTo(int64_t seekPos, bool bAccurate) +{ + if (this->mListener.Seeking) { + this->mListener.Seeking(1, this->mListener.userData); + } + + std::lock_guard lock(mCreateMutex); + + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + playerHandler.isSeeking = true; + } + + float rate = 0; + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player) { + rate = player.rate; + [player pause]; + } + + void (^completionHandler)(BOOL finished) = ^(BOOL finished) { + player.rate = rate; + if (this->mListener.SeekEnd) { + this->mListener.SeekEnd(1, this->mListener.userData); + } + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + playerHandler.isSeeking = false; + } + }; + + /* + * Work around seek to end, the avplayer can't perform seek to end + */ + CMTime toleranceTime = kCMTimeZero; + int64_t duration = GetDuration(); + if (seekPos + 1000 >= duration) { + bAccurate = true; + toleranceTime = CMTimeMakeWithSeconds(0.5, 1); + seekPos = duration - 1000; + } + [player.currentItem cancelPendingSeeks]; + if (bAccurate) { + [player seekToTime:CMTimeMakeWithSeconds((Float64) seekPos / 1000, 1) + toleranceBefore:toleranceTime + toleranceAfter:toleranceTime + completionHandler:completionHandler]; + } else { + [player seekToTime:CMTimeMakeWithSeconds((Float64) seekPos / 1000, 1) completionHandler:completionHandler]; + } +} + +int AppleAVPlayer::Stop() +{ + if (mStatus == PLAYER_STOPPED) { + return 0; + } + mStatus = PLAYER_STOPPED; + + std::lock_guard lock(mCreateMutex); + + AVPlayer *avPlayer = (__bridge AVPlayer *) this->avPlayer; + if (avPlayer) { + [avPlayer pause]; + } + + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler && this->parentLayer != NULL) { + [playerHandler removePlayerLayer]; + } + // CALayer *playerLayer = (CALayer *)CFBridgingRelease(this->parentLayer); + if (this->playerHandler) { + CFRelease(this->playerHandler); + } + this->playerHandler = nullptr; + if (this->avPlayer) { + CFRelease(this->avPlayer); + } + this->avPlayer = nullptr; + if (this->sourceUrl) { + CFRelease(this->sourceUrl); + } + this->sourceUrl = nullptr; + if (this->subtitleUrl) { + CFRelease(this->subtitleUrl); + } + if (this->resourceLoaderDelegate) { + CFRelease(this->resourceLoaderDelegate); + } + this->resourceLoaderDelegate = nullptr; + + // this->mListener = { + // nullptr, + // }; + this->mStreamInfos = nullptr; + return 0; +} + +PlayerStatus AppleAVPlayer::GetPlayerStatus() const +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player && player.rate > 0) { + return PLAYER_PLAYING; + } + return PLAYER_STOPPED; +} + +int64_t AppleAVPlayer::GetDuration() const +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player == NULL) { + return 0; + } + + if (isnan(CMTimeGetSeconds(player.currentItem.duration))) { + return 0; + } + return (int64_t)(CMTimeGetSeconds(player.currentItem.duration) * 1000); +} + +int64_t AppleAVPlayer::GetPlayingPosition() +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player == nullptr) { + return 0; + } + + NSTimeInterval currentTimeSeconds = CMTimeGetSeconds(player.currentTime); + if (isnan(currentTimeSeconds)) { + return 0; + } + return (int64_t)(currentTimeSeconds * 1000); +} + +int64_t AppleAVPlayer::GetBufferPosition() +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player == nullptr) { + return 0; + } + + AVPlayerItem *playerItem = player.currentItem; + int64_t position = [AppleAVPlayerUtil getBufferPosition:playerItem]; + return position; +} + +void AppleAVPlayer::Mute(bool bMute) +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player == nullptr) { + return; + } + if (bMute) { + this->recordVolume = player.volume; + player.volume = 0; + } else { + player.volume = this->recordVolume; + } +} + +bool AppleAVPlayer::IsMute() const +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player && [player isMuted]) { + return true; + } + return false; +} + +void AppleAVPlayer::SetVolume(float volume) +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player) { + player.volume = volume; + } +} +float AppleAVPlayer::GetVolume() const +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player) { + return player.volume; + } + return 1; +} +float AppleAVPlayer::GetVideoRenderFps() +{ + return 0; +} + +void AppleAVPlayer::EnterBackGround(bool back) +{} +void AppleAVPlayer::SetScaleMode(ScaleMode mode) +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + playerHandler.layerProcessor.scaleMode = mode; + } +} + +ScaleMode AppleAVPlayer::GetScaleMode() +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + return playerHandler.layerProcessor.scaleMode; + } else { + return SM_FIT; + } +} + +void AppleAVPlayer::SetRotateMode(RotateMode mode) +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + playerHandler.layerProcessor.rotateMode = mode; + } +} + +RotateMode AppleAVPlayer::GetRotateMode() +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + return playerHandler.layerProcessor.rotateMode; + } else { + return ROTATE_MODE_0; + } +} + +void AppleAVPlayer::SetVideoBackgroundColor(uint32_t color) +{} + +void AppleAVPlayer::SetMirrorMode(MirrorMode mode) +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + playerHandler.layerProcessor.mirrorMode = mode; + } +} + +MirrorMode AppleAVPlayer::GetMirrorMode() +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + return playerHandler.layerProcessor.mirrorMode; + } else { + return MIRROR_MODE_NONE; + } +} + +int AppleAVPlayer::GetCurrentStreamIndex(StreamType type) +{ + return 0; +} +StreamInfo *AppleAVPlayer::GetCurrentStreamInfo(StreamType type) +{ + return nullptr; +} +int64_t AppleAVPlayer::GetMasterClockPts() +{ + return 0; +} +void AppleAVPlayer::SetTimeout(int timeout) +{} +void AppleAVPlayer::SetDropBufferThreshold(int dropValue) +{} +void AppleAVPlayer::SetDecoderType(DecoderType type) +{} +DecoderType AppleAVPlayer::GetDecoderType() +{ + return DT_SOFTWARE; +} + +void AppleAVPlayer::SetRefer(const char *refer) +{} +void AppleAVPlayer::SetUserAgent(const char *userAgent) +{} + +void AppleAVPlayer::SetLooping(bool bCirclePlay) +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + playerHandler.isCirclePlay = bCirclePlay; + } +} + +bool AppleAVPlayer::isLooping() +{ + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + return playerHandler.isCirclePlay; + } else { + return false; + } +} + +void AppleAVPlayer::CaptureScreen() +{ +#if TARGET_OS_IPHONE + std::lock_guard lock(mCreateMutex); + AppleAVPlayerHandler *playerHandler = (__bridge AppleAVPlayerHandler *) this->playerHandler; + if (playerHandler) { + UIImage *captureImage = [playerHandler captureScreen]; + NSData *imageData = UIImagePNGRepresentation(captureImage); + CGSize imageSize = playerHandler.parentLayer.bounds.size; + if (this->mListener.CaptureScreen) { + this->mListener.CaptureScreen(imageSize.width, imageSize.height, imageData.bytes, this->mListener.userData); + } + } +#endif +} + +void AppleAVPlayer::GetVideoResolution(int &width, int &height) +{} + +void AppleAVPlayer::GetVideoRotation(int &rotation) +{} + +std::string AppleAVPlayer::GetPropertyString(PropertyKey key) +{ + return ICicadaPlayer::GetPropertyString(key); +} + +int64_t AppleAVPlayer::GetPropertyInt(PropertyKey key) +{ + return ICicadaPlayer::GetPropertyInt(key); +} + +float AppleAVPlayer::GetVideoDecodeFps() +{ + return 0; +} + +int AppleAVPlayer::SetOption(const char *key, const char *value) +{ + NSString *kkey = [[NSString alloc] initWithUTF8String:key]; + if ([kkey isEqualToString:@"AVResourceLoaderDelegate"]) { + NSString *addressStr = [[NSString alloc] initWithUTF8String:value]; + void *resourceLoaderDelegate = (void *) [addressStr integerValue]; + CFRetain(resourceLoaderDelegate); + { + lock_guard lock(mCreateMutex); + this->resourceLoaderDelegate = resourceLoaderDelegate; + } + return 0; + } + return 0; +} + +void AppleAVPlayer::GetOption(const char *key, char *value) +{} + +void AppleAVPlayer::setSpeed(float speed) +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player) { + player.rate = speed; + } +} + +float AppleAVPlayer::getSpeed() +{ + std::lock_guard lock(mCreateMutex); + AVPlayer *player = (__bridge AVPlayer *) this->avPlayer; + if (player) { + return player.rate; + } else { + return 1; + } +} + +void AppleAVPlayer::AddCustomHttpHeader(const char *httpHeader) +{} + +void AppleAVPlayer::RemoveAllCustomHttpHeader() +{} + +void AppleAVPlayer::addExtSubtitle(const char *uri) +{ + NSString *urlString = [[NSString alloc] initWithUTF8String:uri]; + this->subtitleUrl = (__bridge_retained void *) urlString; +} + +int AppleAVPlayer::selectExtSubtitle(int index, bool bSelect) +{ + return 0; +} + +int AppleAVPlayer::getCurrentStreamMeta(Stream_meta *meta, StreamType type) +{ + return 0; +} + +void AppleAVPlayer::reLoad() +{} + +void AppleAVPlayer::SetAutoPlay(bool bAutoPlay) +{ + this->isAutoPlay = bAutoPlay; +} + +bool AppleAVPlayer::IsAutoPlay() +{ + return this->isAutoPlay; +} + +int AppleAVPlayer::invokeComponent(std::string content) +{ + return 0; +} +int AppleAVPlayer::setStreamDelay(int index, int64_t time) +{ + return 0; +} diff --git a/mediaPlayer/externalPlayer/AppleAVPlayerHandler.h b/mediaPlayer/externalPlayer/AppleAVPlayerHandler.h new file mode 100644 index 000000000..e54d66938 --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayerHandler.h @@ -0,0 +1,41 @@ +// +// AppleAVPlayerHandler.h +// CicadaPlayerSDK +// +// Created by zhou on 2020/7/26. +// + +#ifdef __APPLE__ +#include +#endif + +#import "native_cicada_player_def.h" + +#import +#import +#import "AppleAVPlayerLayerProcessor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface AppleAVPlayerHandler : NSObject { + playerListener mPlayerListener; +} + +@property (nonatomic, strong) CALayer *parentLayer; +@property (nonatomic, strong) AVPlayer *avplayer; +@property (nonatomic, strong) NSArray *selectionOptionArray; +@property (nonatomic, strong) id timeObserver; +@property (nonatomic, assign) BOOL isCirclePlay; +@property (nonatomic, strong) AppleAVPlayerLayerProcessor *layerProcessor; +@property(nonatomic, assign) BOOL isSeeking; + +- (instancetype)init; +- (void)setmPlayerListener:(playerListener)playerListener; +- (void)removePlayerLayer; +#if TARGET_OS_IPHONE +- (UIImage *)captureScreen; +#endif +@end + + +NS_ASSUME_NONNULL_END diff --git a/mediaPlayer/externalPlayer/AppleAVPlayerHandler.mm b/mediaPlayer/externalPlayer/AppleAVPlayerHandler.mm new file mode 100644 index 000000000..569d67049 --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayerHandler.mm @@ -0,0 +1,191 @@ +// +// AppleAVPlayerHandler.m +// CicadaPlayerSDK +// +// Created by zhou on 2020/7/26. +// +#import "AppleAVPlayerHandler.h" +#import "AppleAVPlayerUtil.h" +#if TARGET_OS_IPHONE +#import +#endif + +@interface AppleAVPlayerHandler () + +@property (nonatomic, strong) CALayer *playerLayer; + +@end + +@implementation AppleAVPlayerHandler + +- (instancetype) init { + self = [super init]; + if (self) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerTimeJumped:) name:AVPlayerItemTimeJumpedNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerDidPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; + self.layerProcessor = [[AppleAVPlayerLayerProcessor alloc] init]; + self.isSeeking = false; + } + return self; +} + +- (void)setmPlayerListener:(playerListener)playerListener { + mPlayerListener = playerListener; +} + +- (void)setParentLayer:(CALayer *)parentLayer { + _parentLayer = parentLayer; + self.layerProcessor.parentLayer = parentLayer; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ + if ([keyPath isEqualToString:@"status"]) { + //取出status的新值 + AVPlayerItemStatus status = (AVPlayerItemStatus)[change[NSKeyValueChangeNewKey] integerValue]; + if (status == AVPlayerItemStatusFailed) { + auto item = (AVPlayerItem *) object; + if (mPlayerListener.ErrorCallback) { + NSString *errorDesc = item.error.localizedDescription; + if (item.errorLog) { + errorDesc = [errorDesc stringByAppendingFormat:@"\n%@", item.errorLog.description]; + } + mPlayerListener.ErrorCallback(item.error.code, [errorDesc UTF8String], mPlayerListener.userData); + } + } else if (status == AVPlayerItemStatusReadyToPlay) { + if (mPlayerListener.Prepared) { + mPlayerListener.Prepared(mPlayerListener.userData); + } + if (mPlayerListener.StatusChanged) { + mPlayerListener.StatusChanged(PLAYER_PREPARING, PLAYER_PREPARED, mPlayerListener.userData); + } + if (mPlayerListener.FirstFrameShow) { + mPlayerListener.FirstFrameShow(mPlayerListener.userData); + } + AVPlayerItem *item = (AVPlayerItem *)object; + if (mPlayerListener.VideoSizeChanged) { + mPlayerListener.VideoSizeChanged(static_cast(item.presentationSize.width), + static_cast(item.presentationSize.height), mPlayerListener.userData); + } + [self.layerProcessor setVideoSize:item.presentationSize]; + } else if (status == AVPlayerItemStatusUnknown) { + + } + } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { + AVPlayerItem *playerItem = (AVPlayerItem *)object; + int64_t position = [AppleAVPlayerUtil getBufferPosition:playerItem]; + if (mPlayerListener.BufferPositionUpdate) { + mPlayerListener.BufferPositionUpdate(position, mPlayerListener.userData); + } + } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { + AVPlayerItem *playerItem = (AVPlayerItem *)object; + if (mPlayerListener.LoadingStart && playerItem.playbackBufferEmpty) { + mPlayerListener.LoadingStart(mPlayerListener.userData); + } + } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { + AVPlayerItem *playerItem = (AVPlayerItem *)object; + if (mPlayerListener.LoadingEnd && playerItem.playbackLikelyToKeepUp) { + mPlayerListener.LoadingEnd(mPlayerListener.userData); + } + } else if ([keyPath isEqualToString:@"playbackBufferFull"]) { + + } +} + +- (void)setAvplayer:(AVPlayer *)avplayer { + if (_avplayer) { + [_avplayer removeTimeObserver:self.timeObserver]; + [_avplayer.currentItem removeObserver:self forKeyPath:@"status"]; + [_avplayer.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; + } + _avplayer = avplayer; + if (_avplayer) { + __weak typeof(self) weakSelf = self; + self.timeObserver = [_avplayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { + __strong typeof(self) strongSelf = weakSelf; + if (!strongSelf.isSeeking) { + playerListener playerListener = [strongSelf getListener]; + NSTimeInterval currentTime = CMTimeGetSeconds(time); + int64_t position = (int64_t)(currentTime * 1000); + if (playerListener.PositionUpdate) { + playerListener.PositionUpdate(position, playerListener.userData); + } + } + }]; + [avplayer.currentItem addObserver:weakSelf forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; + [avplayer.currentItem addObserver:weakSelf forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; + [avplayer.currentItem addObserver:weakSelf forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; + [avplayer.currentItem addObserver:weakSelf forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; + [avplayer.currentItem addObserver:weakSelf forKeyPath:@"playbackBufferFull" options:NSKeyValueObservingOptionNew context:nil]; + [self setupPlayerLayer]; + } +} + +- (playerListener)getListener{ + return mPlayerListener; +} + +- (void)playerTimeJumped:(NSNotification *)notification { + +} + +- (void)playerDidPlayToEndTime:(NSNotification *)notification { + AVPlayerItem *item = notification.object; + if (item != self.avplayer.currentItem) { + return; + } + if (self.isCirclePlay) { + [self.avplayer seekToTime:CMTimeMakeWithSeconds(0, 1) completionHandler:^(BOOL finished) { + [self.avplayer play]; + if (mPlayerListener.AutoPlayStart) { + mPlayerListener.AutoPlayStart(mPlayerListener.userData); + } + if (mPlayerListener.LoopingStart) { + mPlayerListener.LoopingStart(mPlayerListener.userData); + } + }]; + } else if (mPlayerListener.Completion) { + mPlayerListener.Completion(mPlayerListener.userData); + } +} + +- (void)setupPlayerLayer { + if (self.playerLayer) { + [self.playerLayer removeFromSuperlayer]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.avplayer]; + layer.frame = CGRectMake(0, 0, self.parentLayer.bounds.size.width, self.parentLayer.bounds.size.height); + [self.parentLayer addSublayer:layer]; + self.playerLayer = layer; + self.layerProcessor.playerLayer = layer; + }); +} + +- (void)removePlayerLayer +{ + if (self.playerLayer) { + [self.playerLayer removeFromSuperlayer]; + } + self.playerLayer = nullptr; + self.layerProcessor.playerLayer = nullptr; +} +#if TARGET_OS_IPHONE +- (UIImage *)captureScreen{ + UIGraphicsBeginImageContext(self.parentLayer.bounds.size); + [self.parentLayer renderInContext:UIGraphicsGetCurrentContext()]; + UIImage *captureImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return captureImage; +} +#endif +- (void)dealloc { + [self.avplayer removeTimeObserver:self.timeObserver]; + [self.avplayer.currentItem removeObserver:self forKeyPath:@"status"]; + [self.avplayer.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; + [self.avplayer.currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"]; + [self.avplayer.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; + [self.avplayer.currentItem removeObserver:self forKeyPath:@"playbackBufferFull"]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +@end diff --git a/mediaPlayer/externalPlayer/AppleAVPlayerLayerProcessor.h b/mediaPlayer/externalPlayer/AppleAVPlayerLayerProcessor.h new file mode 100644 index 000000000..53d658a5f --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayerLayerProcessor.h @@ -0,0 +1,27 @@ +// +// AppleAVPlayerLayerProcessor.h +// CicadaPlayerSDK +// +// Created by zhou on 2020/7/26. +// + +#import +#import +#import "native_cicada_player_def.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface AppleAVPlayerLayerProcessor : NSObject + +@property (nonatomic, strong) CALayer *parentLayer; +@property (nonatomic, strong) AVPlayerLayer *playerLayer; + +@property (nonatomic, assign) ScaleMode scaleMode; +@property (nonatomic, assign) MirrorMode mirrorMode; +@property (nonatomic, assign) RotateMode rotateMode; + +- (void)setVideoSize:(CGSize)videoSize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/mediaPlayer/externalPlayer/AppleAVPlayerLayerProcessor.mm b/mediaPlayer/externalPlayer/AppleAVPlayerLayerProcessor.mm new file mode 100644 index 000000000..1023a06a8 --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayerLayerProcessor.mm @@ -0,0 +1,160 @@ +// +// AppleAVPlayerLayerProcessor.m +// CicadaPlayerSDK +// +// Created by zhou on 2020/7/26. +// +#ifdef __APPLE__ +#include +#endif +#import "AppleAVPlayerLayerProcessor.h" +#if TARGET_OS_IPHONE +#import +#endif + +@interface AppleAVPlayerLayerProcessor () + +@property (nonatomic, strong) AVPlayer *playerRecord; + +@property (nonatomic) CGSize videoSize; +@property CATransform3D scaleTransform; +@property CATransform3D mirrorTransform; +@property CATransform3D rotateTransform; + +@property (nonatomic,assign) BOOL isFillWidth; + +@end + + +@implementation AppleAVPlayerLayerProcessor + +- (instancetype)init { + self = [super init]; + if (self) { +#if TARGET_OS_IPHONE + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; +#endif + _scaleTransform = CATransform3DMakeRotation(0, 0, 0, 0); + _mirrorTransform = CATransform3DMakeRotation(0, 0, 0, 0); + _rotateTransform = CATransform3DMakeRotation(0, 0, 0, 0); + } + return self; +} + +- (void)setParentLayer:(CALayer *)parentLayer { + _parentLayer = parentLayer; + _parentLayer.masksToBounds = YES; + [parentLayer addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew context:nil]; +} + +- (void)setScaleMode:(ScaleMode)scaleMode { + _scaleMode = scaleMode; + [self applayTransform]; +} + +- (void)setMirrorMode:(MirrorMode)mirrorMode { + _mirrorMode = mirrorMode; + if (mirrorMode == MIRROR_MODE_NONE) { + _mirrorTransform = CATransform3DMakeRotation(0, 0, 0, 0); + } else if (mirrorMode == MIRROR_MODE_HORIZONTAL) { + _mirrorTransform = CATransform3DMakeRotation(M_PI, 0, 1, 0); + } else if (mirrorMode == MIRROR_MODE_VERTICAL) { + _mirrorTransform = CATransform3DMakeRotation(M_PI, 1, 0, 0); + } + + [self applayTransform]; +} + +- (void)setRotateMode:(RotateMode)rotateMode { + _rotateMode = rotateMode; + if (rotateMode == ROTATE_MODE_0) { + _rotateTransform = CATransform3DMakeRotation(0, 0, 0, 0); + } else if (rotateMode == ROTATE_MODE_90) { + _rotateTransform = CATransform3DMakeRotation(M_PI / 2, 0, 0, 1); + } else if (rotateMode == ROTATE_MODE_180) { + _rotateTransform = CATransform3DMakeRotation(M_PI, 0, 0, 1); + } else if (rotateMode == ROTATE_MODE_270) { + _rotateTransform = CATransform3DMakeRotation(M_PI / 2, 0, 0, -1); + } + + [self applayTransform]; +} + +-(void)applayTransform{ + CATransform3D transform = CATransform3DConcat(_mirrorTransform,_rotateTransform); + + + CGRect bounds = self.playerLayer.bounds; + if (_scaleMode == SM_FIT) { + float scale = 1; + if(_isFillWidth){ + scale = self.playerLayer.bounds.size.width/[self getVideoSize].width; + }else{ + scale = self.playerLayer.bounds.size.height/[self getVideoSize].height; + } + _scaleTransform = CATransform3DMakeScale(scale, scale, 1); + }else if (_scaleMode == SM_CROP){ + float scale = 1; + if(!_isFillWidth){ + scale = self.playerLayer.bounds.size.width/[self getVideoSize].width; + }else{ + scale = self.playerLayer.bounds.size.height/[self getVideoSize].height; + } + _scaleTransform = CATransform3DMakeScale(scale, scale, 1); + }else if (_scaleMode == SM_EXTRACTTOFIT){ + float scalex; + float scaley; + if(!_isFillWidth){ + scalex = self.playerLayer.bounds.size.width/[self getVideoSize].width; + scaley = self.playerLayer.bounds.size.height/[self getVideoSize].height; + }else{ + scalex = self.playerLayer.bounds.size.width/[self getVideoSize].width; + scaley = self.playerLayer.bounds.size.height/[self getVideoSize].height; + } + _scaleTransform = CATransform3DMakeScale(scalex, scaley, 1); + } + + self.playerLayer.transform = CATransform3DConcat(transform,_scaleTransform); +} + +- (void)dealloc { + [self.parentLayer removeObserver:self forKeyPath:@"bounds"]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([keyPath isEqualToString:@"bounds"]) { + CGRect bounds = [change[NSKeyValueChangeNewKey] CGRectValue]; + self.playerLayer.frame = CGRectMake(0, 0, bounds.size.width, bounds.size.height); + } +} + +- (void)applicationDidEnterBackground { + self.playerRecord = self.playerLayer.player; + self.playerLayer.player = nil; +} + +- (void)applicationWillEnterForeground { + self.playerLayer.player = self.playerRecord; +} + +- (void)setVideoSize:(CGSize)videoSize{ + float scale = 1; + _isFillWidth = self.playerLayer.bounds.size.width/self.playerLayer.bounds.size.height < videoSize.width/videoSize.height; + if(_isFillWidth){ + scale = self.playerLayer.bounds.size.width/videoSize.width; + }else{ + scale = self.playerLayer.bounds.size.height/videoSize.height; + } + _videoSize = CGSizeMake(videoSize.width*scale, videoSize.height*scale); +} + +- (CGSize)getVideoSize{ + if (_rotateMode%180) { + return CGSizeMake(_videoSize.height,_videoSize.width); + } + return _videoSize; +} + +@end diff --git a/mediaPlayer/externalPlayer/AppleAVPlayerUtil.h b/mediaPlayer/externalPlayer/AppleAVPlayerUtil.h new file mode 100644 index 000000000..76c3156dc --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayerUtil.h @@ -0,0 +1,19 @@ +// +// AppleAVPlayerUtil.h +// CicadaPlayerSDK +// +// Created by zhou on 2020/7/26. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AppleAVPlayerUtil : NSObject + ++ (int64_t)getBufferPosition:(AVPlayerItem *)playerItem; + +@end + +NS_ASSUME_NONNULL_END diff --git a/mediaPlayer/externalPlayer/AppleAVPlayerUtil.m b/mediaPlayer/externalPlayer/AppleAVPlayerUtil.m new file mode 100644 index 000000000..db2db7f62 --- /dev/null +++ b/mediaPlayer/externalPlayer/AppleAVPlayerUtil.m @@ -0,0 +1,25 @@ +// +// AppleAVPlayerUtil.m +// CicadaPlayerSDK +// +// Created by zhou on 2020/7/26. +// + +#import "AppleAVPlayerUtil.h" + +@implementation AppleAVPlayerUtil + ++ (int64_t)getBufferPosition:(AVPlayerItem *)playerItem { + NSArray *loadedTimeRanges = playerItem.loadedTimeRanges; + CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue]; + NSTimeInterval loadStartSeconds = CMTimeGetSeconds(timeRange.start); + NSTimeInterval loadDurationSeconds = CMTimeGetSeconds(timeRange.duration); + NSTimeInterval currentLoadTotalTime = loadStartSeconds + loadDurationSeconds; + if (isnan(currentLoadTotalTime)) { + return 0; + } + int64_t position = (int64_t)(currentLoadTotalTime * 1000); + return position; +} + +@end diff --git a/mediaPlayer/mediaPlayerSubTitleListener.h b/mediaPlayer/mediaPlayerSubTitleListener.h index aae2a3d7c..0dbaad99f 100644 --- a/mediaPlayer/mediaPlayerSubTitleListener.h +++ b/mediaPlayer/mediaPlayerSubTitleListener.h @@ -5,9 +5,10 @@ #ifndef CICADAPLAYERSDK_MEDIAPLAYERSUBTITLELISTENER_H #define CICADAPLAYERSDK_MEDIAPLAYERSUBTITLELISTENER_H -#include "subTitle/subTitlePlayer.h" #include "player_notifier.h" +#include "subTitle/subTitlePlayer.h" #include +#include namespace Cicada { class mediaPlayerSubTitleListener : public subTitlePlayer::Listener { diff --git a/mediaPlayer/media_packet_queue.cpp b/mediaPlayer/media_packet_queue.cpp index 7859e20a0..2d592f7b2 100644 --- a/mediaPlayer/media_packet_queue.cpp +++ b/mediaPlayer/media_packet_queue.cpp @@ -43,6 +43,26 @@ namespace Cicada { mQueue.push_back(move(frame)); } + void MediaPacketQueue::SetOnePacketDuration(int64_t duration) { + ADD_LOCK; + if (mPacketDuration <= 0) { + mPacketDuration = duration; + + int64_t missedDuration = 0; + for (mediaPacket &item : mQueue) { + if (item.get()->getInfo().duration <= 0) { + item.get()->getInfo().duration = mPacketDuration; + missedDuration += mPacketDuration; + } + } + mDuration += missedDuration; + } + } + + int64_t MediaPacketQueue::GetOnePacketDuration() { + ADD_LOCK; + return mPacketDuration; + } int64_t MediaPacketQueue::GetLastKeyTimePos() { @@ -177,7 +197,7 @@ namespace Cicada { { ADD_LOCK; - if (mMediaType == BUFFER_TYPE_VIDEO && mPacketDuration == 0) { + if ( (mMediaType == BUFFER_TYPE_VIDEO || mMediaType == BUFFER_TYPE_AUDIO) && mPacketDuration == 0) { if (mQueue.empty()) { return 0; } @@ -276,4 +296,6 @@ namespace Cicada { return mQueue.front()->getInfo().pts; } + + }//namespace Cicada diff --git a/mediaPlayer/media_packet_queue.h b/mediaPlayer/media_packet_queue.h index c69030189..132fd3eaa 100644 --- a/mediaPlayer/media_packet_queue.h +++ b/mediaPlayer/media_packet_queue.h @@ -15,6 +15,10 @@ namespace Cicada { typedef std::unique_ptr mediaPacket; + void SetOnePacketDuration(int64_t duration); + + int64_t GetOnePacketDuration(); + public: void ClearQueue(); diff --git a/mediaPlayer/media_player_api.cpp b/mediaPlayer/media_player_api.cpp index fa8f9e254..d013da9c1 100644 --- a/mediaPlayer/media_player_api.cpp +++ b/mediaPlayer/media_player_api.cpp @@ -1,9 +1,9 @@ +#include "media_player_api.h" +#include "CicadaPlayerPrototype.h" #include +#include #include #include -#include "media_player_api.h" -#include "ICicadaPlayer.h" -#include "SuperMediaPlayer.h" using namespace Cicada; @@ -13,10 +13,25 @@ typedef struct playerHandle_t { #define GET_PLAYER ICicadaPlayer *player = pHandle->pPlayer -playerHandle *CicadaCreatePlayer() +playerHandle *CicadaCreatePlayer(const char *opts) { playerHandle *pHandle = new playerHandle(); - pHandle->pPlayer = new SuperMediaPlayer(); + if (opts == nullptr) { + opts = ""; + } + CicadaJSONItem item{opts}; + options createOpt; + const string defaultString{}; + string value; + value = item.getString("name", defaultString); + if (value != defaultString) { + createOpt.set("name", value , options::REPLACE); + } + value = item.getString("playerPointer" , defaultString); + if (value != defaultString) { + createOpt.set("playerPointer", value , options::REPLACE); + } + pHandle->pPlayer = CicadaPlayerPrototype::create(&createOpt); return pHandle; } @@ -57,6 +72,22 @@ void CicadaSetOnRenderCallBack(playerHandle *pHandle, onRenderFrame cb, void *us } } +void CicadaSetAudioRenderingCallBack(playerHandle *pHandle, onRenderFrame cb, void *userData) +{ + GET_PLAYER; + if (player) { + player->SetAudioRenderingCallBack(cb, userData); + } +} + +void CicadaSetUpdateViewCallback(playerHandle *pHandle, UpdateViewCB cb, void *userData) +{ + GET_PLAYER; + if (player) { + player->SetUpdateViewCB(cb, userData); + } +} + void CicadaSetComponentCb(playerHandle *pHandle, player_component_type type, void *factory) { GET_PLAYER; @@ -266,7 +297,7 @@ int64_t CicadaGetDuration(playerHandle *pHandle) return player->GetDuration(); } - return 0; + return -1; } int64_t CicadaGetCurrentPosition(playerHandle *pHandle) @@ -581,6 +612,14 @@ void CicadaSetDefaultBandWidth(playerHandle *pHandle, int bandWidth) } } +void CicadaSetDrmRequestCallback(playerHandle *pHandle, const std::function & drmCallback) { + GET_PLAYER; + + if (player) { + player->setDrmRequestCallback(drmCallback); + } +} + int CicadaInvokeComponent(playerHandle *pHandle, const char *content) { GET_PLAYER; @@ -663,3 +702,21 @@ void CicadaSelectExtSubtitle(playerHandle *pHandle, int index, bool select) player->selectExtSubtitle(index, select); } } + +int CicadaSetStreamDelayTime(playerHandle *pHandle, int index, int64_t time) +{ + GET_PLAYER; + if (player) { + return player->setStreamDelay(index, time); + } + return -EINVAL; +} + +std::string CicadaGetPlayerName(playerHandle *pHandle) +{ + GET_PLAYER; + if (player) { + return player->getName(); + } + return ""; +} diff --git a/mediaPlayer/media_player_api.h b/mediaPlayer/media_player_api.h index a675e1865..b5060df31 100644 --- a/mediaPlayer/media_player_api.h +++ b/mediaPlayer/media_player_api.h @@ -5,6 +5,7 @@ #include #include #include "native_cicada_player_def.h" +#include //typedef struct Stream_meta_t Stream_meta; @@ -13,7 +14,7 @@ typedef struct playerHandle_t playerHandle; /* *create the Cicada player */ -playerHandle *CicadaCreatePlayer(); +playerHandle *CicadaCreatePlayer(const char *opts); /* * release Cicada player @@ -28,6 +29,10 @@ int CicadaSetListener(playerHandle *pHandle, const playerListener &Listener); void CicadaSetOnRenderCallBack(playerHandle *pHandle, onRenderFrame cb, void *userData); +void CicadaSetAudioRenderingCallBack(playerHandle *pHandle, onRenderFrame cb, void *userData); + +void CicadaSetUpdateViewCallback(playerHandle *pHandle, UpdateViewCB cb, void *userData); + /* * set external component callback */ @@ -243,6 +248,8 @@ void CicadaAddExtSubtitle(playerHandle *pHandle, const char *uri); void CicadaSelectExtSubtitle(playerHandle *pHandle, int index, bool select); +int CicadaSetStreamDelayTime(playerHandle *pHandle, int index, int64_t time); + /* * get video with and height */ @@ -283,4 +290,8 @@ void CicadaSetDefaultBandWidth(playerHandle *player, int bandWidth); int CicadaInvokeComponent(playerHandle *player, const char *content); +void CicadaSetDrmRequestCallback(playerHandle *player, const std::function & drmCallback); + +std::string CicadaGetPlayerName(playerHandle *player); + #endif // CICADA_PLAYER_H_ diff --git a/mediaPlayer/media_player_error_def.h b/mediaPlayer/media_player_error_def.h index 07a064847..7d3310b44 100644 --- a/mediaPlayer/media_player_error_def.h +++ b/mediaPlayer/media_player_error_def.h @@ -57,6 +57,10 @@ namespace Cicada{ MEDIA_PLAYER_ERROR_GENERAL_ENOENT = 0x20080002, MEDIA_PLAYER_ERROR_GENERAL_EIO = 0x20080005, + //8.render 错误 + MEDIA_PLAYER_ERROR_RENDER_UNKNOWN = 0x20090000, + MEDIA_PLAYER_ERROR_RENDER_AUDIO_OPEN_DEVICE_FAILED = 0x20090001, + // MEDIA_PLAYER_ERROR_UNKNOWN = 0x30000000 - 1, }; @@ -83,6 +87,8 @@ namespace Cicada{ MEDIA_PLAYER_EVENT_DECODER_RECOVER_SIZE, MEDIA_PLAYER_EVENT_DIRECT_COMPONENT_MSG, + + MEDIA_PLAYER_EVENT_OPEN_AUDIO_DEVICE_FAILED, }; } diff --git a/mediaPlayer/native_cicada_player_def.h b/mediaPlayer/native_cicada_player_def.h index 25351c181..0104fd09c 100644 --- a/mediaPlayer/native_cicada_player_def.h +++ b/mediaPlayer/native_cicada_player_def.h @@ -43,6 +43,11 @@ typedef enum _StreamType { ST_TYPE_SUB, } StreamType; +typedef enum _VideoHDRType { + VideoHDRType_SDR, + VideoHDRType_HDR10, +} VideoHDRType; + typedef enum _IpResolveType { IpResolveWhatEver, IpResolveV4, IpResolveV6 } IpResolveType; #define VIDEO_FLAG 1 << ST_TYPE_VIDEO @@ -58,6 +63,7 @@ typedef struct _StreamInfo { int videoBandwidth; int videoWidth; int videoHeight; + VideoHDRType HDRType; //audio char *audioLang; @@ -94,6 +100,7 @@ typedef struct playerListener_t { playerType1Callback PositionUpdate; playerType1Callback BufferPositionUpdate; playerType1Callback LoadingProgress; + playerType1Callback CurrentDownLoadSpeed; playerType12Callback VideoSizeChanged; playerType12Callback StatusChanged; @@ -138,19 +145,31 @@ typedef enum SeekMode { } SeekMode; typedef enum PropertyKey { - PROPERTY_KEY_RESPONSE_INFO = 0, - PROPERTY_KEY_CONNECT_INFO = 1, - PROPERTY_KEY_OPEN_TIME_STR = 2, - PROPERTY_KEY_PROBE_STR = 3, + PROPERTY_KEY_RESPONSE_INFO = 0, + PROPERTY_KEY_CONNECT_INFO = 1, + PROPERTY_KEY_OPEN_TIME_STR = 2, + PROPERTY_KEY_PROBE_STR = 3, PROPERTY_KEY_VIDEO_BUFFER_LEN = 4, - PROPERTY_KEY_DELAY_INFO = 5, - PROPERTY_KEY_REMAIN_LIVE_SEG = 6, + PROPERTY_KEY_DELAY_INFO = 5, + PROPERTY_KEY_REMAIN_LIVE_SEG = 6, PROPERTY_KEY_NETWORK_IS_CONNECTED = 7, + PROPERTY_KEY_PLAY_CONFIG = 8, + PROPERTY_KEY_DECODE_INFO = 9, + PROPERTY_KEY_HLS_KEY_URL = 10, } PropertyKey; +typedef enum VideoTag { + VIDEO_TAG_NONE = 0, + VIDEO_TAG_SDR = 1 << 0, + VIDEO_TAG_HDR10 = 1 << 1, + VIDEO_TAG_WIDEVINE_L1 = 1 << 2, + VIDEO_TAG_WIDEVINE_L3 = 1 << 3, + VIDEO_TAG_FAIRPLAY = 1 << 4, +} VideoTag; + class AMediaFrame; -typedef void (*playerMediaFrameCb)(void *arg, const std::unique_ptr& frame, StreamType type); +typedef void (*playerMediaFrameCb)(void *arg, const IAFPacket *frame, StreamType type); typedef int (*readCB)(void *arg, uint8_t *buffer, int size); @@ -160,6 +179,8 @@ typedef int64_t(*clockRefer)(void *arg); typedef bool (*onRenderFrame)(void *userData, IAFFrame *frame); +typedef bool (*UpdateViewCB)(int videoType, void *userData); + class ErrorConverter { public: virtual int ConvertErrorCode(int code, int &outCode, std::string &outStr) = 0; diff --git a/mediaPlayer/player_msg_control.cpp b/mediaPlayer/player_msg_control.cpp index 32227da27..1a4a7a603 100644 --- a/mediaPlayer/player_msg_control.cpp +++ b/mediaPlayer/player_msg_control.cpp @@ -15,6 +15,7 @@ namespace Cicada { { switch (type) { case MSG_SETDATASOURCE: + case MSG_SET_BITSTREAM: case MSG_SETVIEW: case MSG_PREPARE: case MSG_CHANGE_VIDEO_STREAM: @@ -218,6 +219,11 @@ namespace Cicada { mProcessor.ProcessSetDataSourceMsg(*(msgContent.dataSourceParam.url)); break; + case MSG_SET_BITSTREAM: + mProcessor.ProcessSetBitStreamMsg(msgContent.msgBitStreamParam.read, msgContent.msgBitStreamParam.seek, + msgContent.msgBitStreamParam.arg); + break; + case MSG_SETVIEW: mProcessor.ProcessSetViewMsg(msgContent.viewParam.view); break; @@ -262,9 +268,8 @@ namespace Cicada { break; case MSG_INTERNAL_VIDEO_RENDERED: - mProcessor.ProcessVideoRenderedMsg(msgContent.videoRenderedParam.pts, - msgContent.videoRenderedParam.timeMs, - msgContent.videoRenderedParam.userData); + mProcessor.ProcessVideoRenderedMsg(msgContent.videoRenderedParam.pts, msgContent.videoRenderedParam.timeMs, + msgContent.videoRenderedParam.rendered, msgContent.videoRenderedParam.userData); break; case MSG_INTERNAL_VIDEO_CLEAN_FRAME: diff --git a/mediaPlayer/player_msg_control.h b/mediaPlayer/player_msg_control.h index bd94ebe8d..aa260e53f 100644 --- a/mediaPlayer/player_msg_control.h +++ b/mediaPlayer/player_msg_control.h @@ -1,9 +1,10 @@ #ifndef CICADA_PLAYER_MSG_CONTROL_H #define CICADA_PLAYER_MSG_CONTROL_H -#include -#include +#include "native_cicada_player_def.h" #include +#include +#include #include using namespace std; @@ -30,6 +31,7 @@ namespace Cicada { MSG_ADD_EXT_SUBTITLE, MSG_SELECT_EXT_SUBTITLE, MSG_SET_SPEED, + MSG_SET_BITSTREAM, MSG_INTERNAL_VIDEO_FIRST = 0x100, MSG_INTERNAL_VIDEO_RENDERED = MSG_INTERNAL_VIDEO_FIRST, @@ -38,59 +40,67 @@ namespace Cicada { } PlayMsgType; - typedef struct _DisplayModeParam { + typedef struct DisplayModeParam { int mode; } DisplayModeParam; - typedef struct _RotateModeParam { + typedef struct RotateModeParam { int mode; } RotateModeParam; - typedef struct _MirrorModeParam { + typedef struct MirrorModeParam { int mode; } MirrorModeParam; - typedef struct _MsgViewParam { + typedef struct MsgViewParam { void *view; } MsgViewParam; - typedef struct _MsgDataSourceParam { + typedef struct MsgDataSourceParam { std::string *url; } MsgDataSourceParam; - typedef struct _MsgSeekParam { + typedef struct MsgBitStreamParam { + readCB read; + seekCB seek; + void *arg; + } MsgBitStreamParam; + + typedef struct MsgSeekParam { int64_t seekPos; bool bAccurate; } MsgSeekParam; - typedef struct _MsgSpeedParam { + typedef struct MsgSpeedParam { float speed; } MsgSpeedParam; - typedef struct _MsgHoldOnVideoParam { + typedef struct MsgHoldOnVideoParam { bool hold; } MsgHoldOnVideoParam; - typedef struct _MsgChangeStreamParam { + typedef struct MsgChangeStreamParam { int index; } MsgChangeStreamParam; - typedef struct _MsgVideoRenderedParam { + typedef struct MsgVideoRenderedParam { int64_t pts; int64_t timeMs; + bool rendered; void *userData; } MsgVideoRenderedParam; - typedef struct _MsgSelectExtSubtitleParam { + typedef struct MsgSelectExtSubtitleParam { int index; bool bSelect; } MsgSelectExtSubtitleParam; - typedef union _MsgParam { + typedef union MsgParam { MsgViewParam viewParam; MsgDataSourceParam dataSourceParam; + MsgBitStreamParam msgBitStreamParam; MsgSeekParam seekParam; MsgChangeStreamParam streamParam; MsgVideoRenderedParam videoRenderedParam; @@ -99,14 +109,12 @@ namespace Cicada { MsgSpeedParam msgSpeedParam; } MsgParam; - typedef struct _QueueMsgStruct { + typedef struct QueueMsgStruct { PlayMsgType msgType; MsgParam msgParam; int64_t msgTime; } QueueMsgStruct; - typedef bool (*OnMsgProcesser)(PlayMsgType msg, MsgParam msgConent, void *userData); - class PlayerMessageControllerListener { public: @@ -133,6 +141,8 @@ namespace Cicada { virtual void ProcessSetDataSourceMsg(const std::string &url) = 0; + virtual void ProcessSetBitStreamMsg(readCB read, seekCB seekCb, void *arg) = 0; + virtual void ProcessPauseMsg() = 0; virtual void ProcessSeekToMsg(int64_t seekPos, bool bAccurate) = 0; @@ -141,7 +151,7 @@ namespace Cicada { virtual void ProcessSwitchStreamMsg(int index) = 0; - virtual void ProcessVideoRenderedMsg(int64_t pts, int64_t timeMs, void *picUserData) = 0; + virtual void ProcessVideoRenderedMsg(int64_t pts, int64_t timeMs, bool rendered, void *picUserData) = 0; virtual void ProcessVideoCleanFrameMsg() = 0; diff --git a/mediaPlayer/player_notifier.cpp b/mediaPlayer/player_notifier.cpp index e55b4be87..c81f11649 100644 --- a/mediaPlayer/player_notifier.cpp +++ b/mediaPlayer/player_notifier.cpp @@ -471,4 +471,13 @@ namespace Cicada { mEventQueue.pop_front(); } } + void PlayerNotifier::NotifyCurrentDownloadSpeed(float speed) + { + if (speed != mCurrentDownloadSpeed) { + mCurrentDownloadSpeed = speed; + + auto *event = new player_event((int64_t) speed, mListener.CurrentDownLoadSpeed); + pushEvent(event); + } + } } diff --git a/mediaPlayer/player_notifier.h b/mediaPlayer/player_notifier.h index dae597b08..f229c73b9 100644 --- a/mediaPlayer/player_notifier.h +++ b/mediaPlayer/player_notifier.h @@ -41,6 +41,8 @@ namespace Cicada { void NotifyPosition(int64_t pos); + void NotifyCurrentDownloadSpeed(float speed); + void NotifyBufferPosition(int64_t pos); void NotifyVideoSizeChanged(int64_t width, int64_t height); @@ -96,6 +98,7 @@ namespace Cicada { std::condition_variable mCondition; bool mEnable = true; std::atomic_bool mRunning{true}; + float mCurrentDownloadSpeed{0}; }; } diff --git a/mediaPlayer/player_types.cpp b/mediaPlayer/player_types.cpp index 872b34e5f..02dadc20a 100644 --- a/mediaPlayer/player_types.cpp +++ b/mediaPlayer/player_types.cpp @@ -38,7 +38,6 @@ namespace Cicada { bDisableAudio = false; bDisableVideo = false; bMute = false; - bDisableBufferManager = false; bLowLatency = false; mView = nullptr; mAutoSwitchTime = INT64_MIN; @@ -65,6 +64,9 @@ namespace Cicada { maxVideoRecoverSize = 300; mFastStart = true; pixelBufferOutputFormat = 0; + drmMagicKey = ""; + sessionId = ""; + netWorkRetryCount = 0; } } diff --git a/mediaPlayer/player_types.h b/mediaPlayer/player_types.h index 3c04a6608..2f2f53b91 100644 --- a/mediaPlayer/player_types.h +++ b/mediaPlayer/player_types.h @@ -38,7 +38,6 @@ namespace Cicada { bool bDisableAudio{false}; bool bDisableVideo{false}; bool bMute = false; - bool bDisableBufferManager = false; bool bLowLatency = false; std::atomic mView{nullptr}; @@ -49,7 +48,7 @@ namespace Cicada { atomic mVolume{1.0}; playerListener mPlayerListener; atomic rate {1.0}; - std::string http_proxy = ""; + std::string http_proxy{}; std::vector customHeaders; bool clearShowWhenStop = false; bool bEnableTunnelRender = true; @@ -64,6 +63,9 @@ namespace Cicada { int maxVideoRecoverSize; bool mFastStart{true}; uint32_t pixelBufferOutputFormat; + string drmMagicKey; + string sessionId{}; + int netWorkRetryCount{0}; }; } diff --git a/mediaPlayer/subTitle/subTitlePlayer.cpp b/mediaPlayer/subTitle/subTitlePlayer.cpp index 1ec80e08b..961911c83 100644 --- a/mediaPlayer/subTitle/subTitlePlayer.cpp +++ b/mediaPlayer/subTitle/subTitlePlayer.cpp @@ -2,9 +2,11 @@ // Created by moqi on 2019/10/31. // +#define LOG_TAG "subTitlePlayer" #include "subTitlePlayer.h" -#include #include +#include +#include using namespace std; namespace Cicada { @@ -27,6 +29,13 @@ namespace Cicada { int subTitlePlayer::add(const std::string &uri) { + for (auto item = mSources.begin(); item != mSources.end();) { + if ((*item)->mSource->getUri() == uri) { + mListener.onAdded(uri, (*item)->mSource->getID()); + return 0; + } + item++; + } auto *adding = new Adding(); adding->mSource = unique_ptr(new subTitleSource(uri)); subTitleSource *pSource = adding->mSource.get(); @@ -85,16 +94,7 @@ namespace Cicada { ++mSelectNum; } else { --mSelectNum; - - if ((*item)->mPacket) { - IAFPacket *packet = (*item)->mPacket.get(); - - if (packet->getDiscard()) { - mListener.onRender(false, (*item)->mPacket.release()); - } - - (*item)->mPacket = nullptr; - } + (*item)->mNeedFlush++; } } @@ -114,13 +114,37 @@ namespace Cicada { } } + int subTitlePlayer::setDelayTime(int index, int64_t time) + { + bool found = false; + for (auto item = mSources.begin(); item != mSources.end();) { + if ((*item)->mSource->getID() == index) { + (*item)->mDelay = time; + found = true; + break; + } + ++item; + } + if (!found) { + AF_LOGE("setDelayTime no such stream\n"); + } + return 0; + } + bool subTitlePlayer::isActive() { - return mSelectNum > 0 && mEnable; + /* + * need flush async + */ + return true; + // return mSelectNum > 0 && mEnable; } void subTitlePlayer::enable(bool bEnable) { + /* + * TODO: flush when disable + */ mEnable = bEnable; } @@ -128,17 +152,8 @@ namespace Cicada { { for (auto item = mSources.begin(); item != mSources.end();) { if ((*item)->mSelected) { - (*item)->mSource->seek(pts); - - if ((*item)->mPacket) { - IAFPacket *packet = (*item)->mPacket.get(); - - if (packet->getDiscard()) { - mListener.onRender(false, (*item)->mPacket.release()); - } - - (*item)->mPacket = nullptr; - } + (*item)->mSource->seek(std::max(pts + (*item)->mDelay, (int64_t) 0)); + (*item)->mNeedFlush++; } ++item; @@ -149,41 +164,43 @@ namespace Cicada { void subTitlePlayer::render(subTitlePlayer::SourceInfo &info, int64_t pts) { - IAFPacket *packet = nullptr; + auto iter = info.mSubtitleShowedQueue.begin(); + while (iter != info.mSubtitleShowedQueue.end()) { + if ((*iter) && ((*iter)->getInfo().pts + info.mDelay + (*iter)->getInfo().duration) <= pts) { + mListener.onRender(false, (*iter).release()); + iter = info.mSubtitleShowedQueue.erase(iter); + continue; + } + iter++; + } - do { - int ret = info.getPacket(&packet); + while (info.mSelected) { + IAFPacket *packet = nullptr; + info.getPacket(&packet); - if (packet == nullptr) { - break; - } + if (packet && packet->getInfo().pts + info.mDelay <= pts) { - if (packet->getInfo().pts + packet->getInfo().duration <= pts) { - if (packet->getDiscard()) { - mListener.onRender(false, info.mPacket.release()); + if (packet->getInfo().pts + info.mDelay + packet->getInfo().duration >= pts) { + mListener.onRender(true, packet); + info.mSubtitleShowedQueue.push_back(move(info.mPacket)); + } else { + AF_LOGD("drop the late subtitle %lld", packet->getInfo().pts); + info.mPacket = nullptr; } - - info.mPacket = nullptr; } else { break; } - } while (true); - - if (packet == nullptr) { - return; - } - - if (packet->getInfo().pts <= pts) { - if (!packet->getDiscard()) { - mListener.onRender(true, packet); - packet->setDiscard(true); - } } } void subTitlePlayer::update(int64_t pts) { for (auto item = mSources.begin(); item != mSources.end();) { + assert((*item)->mNeedFlush >= 0); + if ((*item)->mNeedFlush > 0) { + flushSource((*item).get()); + (*item)->mNeedFlush--; + } if ((*item)->mSelected) { render(*(*item), pts); } @@ -191,4 +208,30 @@ namespace Cicada { ++item; } } -} + + + void subTitlePlayer::flushSource(SourceInfo *source) + { + if (source == nullptr) { + return; + } + while (!source->mSubtitleShowedQueue.empty()) { + if (source->mSubtitleShowedQueue.front()) { + mListener.onRender(false, source->mSubtitleShowedQueue.front().release()); + } + source->mSubtitleShowedQueue.pop_front(); + } + source->mSubtitleShowedQueue.clear(); + source->mPacket = nullptr; + } + + void subTitlePlayer::flush() + { + for (auto item = mSources.begin(); item != mSources.end();) { + flushSource((*item).get()); + //mNeedFlush is used for async flush + // (*item)->mNeedFlush--; + ++item; + } + } +}// namespace Cicada diff --git a/mediaPlayer/subTitle/subTitlePlayer.h b/mediaPlayer/subTitle/subTitlePlayer.h index 32146c80b..4f352a4b3 100644 --- a/mediaPlayer/subTitle/subTitlePlayer.h +++ b/mediaPlayer/subTitle/subTitlePlayer.h @@ -5,10 +5,11 @@ #ifndef CICADAPLAYERSDK_SUBTITLEPLAYER_H #define CICADAPLAYERSDK_SUBTITLEPLAYER_H +#include +#include +#include #include #include -#include -#include #include "subTitleSource.h" #include @@ -53,8 +54,10 @@ namespace Cicada { std::unique_ptr mSource; bool mSelected{false}; + std::atomic_int mNeedFlush{0}; std::unique_ptr mPacket; - + int64_t mDelay{0}; + std::deque> mSubtitleShowedQueue{}; }; @@ -82,14 +85,19 @@ namespace Cicada { int select(int index, bool bSelect); + int setDelayTime(int index, int64_t time); + bool isActive(); void enable(bool bEnable); void onNoop(); + void flush(); + private: void render(subTitlePlayer::SourceInfo &info, int64_t pts); + void flushSource(SourceInfo *source); private: Listener &mListener; diff --git a/mediaPlayer/tests/apiTest/CMakeLists.txt b/mediaPlayer/tests/apiTest/CMakeLists.txt index e198608b1..241add89d 100644 --- a/mediaPlayer/tests/apiTest/CMakeLists.txt +++ b/mediaPlayer/tests/apiTest/CMakeLists.txt @@ -35,6 +35,7 @@ target_link_libraries(mediaPlayerApiTest PRIVATE videodec framework_filter framework_utils + framework_drm avfilter avformat avcodec diff --git a/mediaPlayer/tests/apiTest/mediaPlayerApiTest.cpp b/mediaPlayer/tests/apiTest/mediaPlayerApiTest.cpp index 8e3097a60..8bce1c8f3 100644 --- a/mediaPlayer/tests/apiTest/mediaPlayerApiTest.cpp +++ b/mediaPlayer/tests/apiTest/mediaPlayerApiTest.cpp @@ -7,6 +7,7 @@ #include "gtest/gtest.h" #include #include +#include #include #include #include @@ -126,7 +127,7 @@ TEST(cmd, autoPlay) listener.AutoPlayStart = onAutoPlayStart; test_simple("http://player.alicdn.com/video/aliyunmedia.mp4", nullptr, simple_loop, nullptr, &listener); - assert(g_autoPlay); + ASSERT_TRUE(g_autoPlay); } static void onCompletion(void *userData) diff --git a/mediaPlayer/tests/cache/CMakeLists.txt b/mediaPlayer/tests/cache/CMakeLists.txt index a88a75729..4d716df7e 100644 --- a/mediaPlayer/tests/cache/CMakeLists.txt +++ b/mediaPlayer/tests/cache/CMakeLists.txt @@ -36,6 +36,7 @@ target_link_libraries(mediaPlayerCacheTest PRIVATE videodec framework_filter framework_utils + framework_drm avfilter avformat avcodec diff --git a/mediaPlayer/tests/cache/mediaPlayerCacheTest.cpp b/mediaPlayer/tests/cache/mediaPlayerCacheTest.cpp index 4fe100d0f..ac95a00d7 100644 --- a/mediaPlayer/tests/cache/mediaPlayerCacheTest.cpp +++ b/mediaPlayer/tests/cache/mediaPlayerCacheTest.cpp @@ -2,15 +2,16 @@ // Created by moqi on 2020/1/14. // -#include "gtest/gtest.h" -#include #include "tests/mediaPlayerTest.h" -#include #include "tests/player_command.h" -#include -#include +#include "gtest/gtest.h" #include +#include +#include #include +#include +#include +#include using namespace std; diff --git a/mediaPlayer/tests/formatTest/CMakeLists.txt b/mediaPlayer/tests/formatTest/CMakeLists.txt index ef5a30597..a9574f0ac 100644 --- a/mediaPlayer/tests/formatTest/CMakeLists.txt +++ b/mediaPlayer/tests/formatTest/CMakeLists.txt @@ -35,6 +35,7 @@ target_link_libraries(mediaPlayerFormatTest PRIVATE videodec framework_filter framework_utils + framework_drm avfilter avformat avcodec diff --git a/mediaPlayer/tests/seekTest/CMakeLists.txt b/mediaPlayer/tests/seekTest/CMakeLists.txt index 35caee310..75bdf8025 100644 --- a/mediaPlayer/tests/seekTest/CMakeLists.txt +++ b/mediaPlayer/tests/seekTest/CMakeLists.txt @@ -36,6 +36,7 @@ target_link_libraries(mediaPlayerSeekTest PRIVATE videodec framework_filter framework_utils + framework_drm avfilter avformat avcodec diff --git a/mediaPlayer/tests/switch_stream/CMakeLists.txt b/mediaPlayer/tests/switch_stream/CMakeLists.txt index 243951c60..8f6cdbb04 100644 --- a/mediaPlayer/tests/switch_stream/CMakeLists.txt +++ b/mediaPlayer/tests/switch_stream/CMakeLists.txt @@ -36,6 +36,7 @@ target_link_libraries(mediaPlayerSwitchStreamTest PRIVATE videodec framework_filter framework_utils + framework_drm avfilter avformat avcodec diff --git a/mediaPlayer/tests/switch_stream/mediaPlayerSwitchStream.cpp b/mediaPlayer/tests/switch_stream/mediaPlayerSwitchStream.cpp index 6d903ef27..377969c7a 100644 --- a/mediaPlayer/tests/switch_stream/mediaPlayerSwitchStream.cpp +++ b/mediaPlayer/tests/switch_stream/mediaPlayerSwitchStream.cpp @@ -2,13 +2,14 @@ // Created by pingkai on 2020/1/14. // +#include "tests/mediaPlayerTest.h" +#include "tests/player_command.h" #include "gtest/gtest.h" #include -#include "tests/mediaPlayerTest.h" +#include +#include #include -#include "tests/player_command.h" #include -#include using namespace std; diff --git a/platform/Android/package.sh b/platform/Android/package.sh index f2a8da155..e4b955839 100755 --- a/platform/Android/package.sh +++ b/platform/Android/package.sh @@ -7,9 +7,6 @@ mkdir -p release/CicadaPlayerDemo file="source/releaseLibs/*.aar" cp $file release/saas_release_aar/ -file="source/premierlibrary/libs/*.jar" -cp $file release/saas_release_aar/ - cd release zip -r saas_release_aar-$MUPP_BUILD_ID.zip saas_release_aar cd .. diff --git a/platform/Android/source/ExternalPlayerExoLibrary/.gitignore b/platform/Android/source/ExternalPlayerExoLibrary/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/platform/Android/source/ExternalPlayerExoLibrary/build.gradle b/platform/Android/source/ExternalPlayerExoLibrary/build.gradle new file mode 100644 index 000000000..5d987e230 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.0" + + defaultConfig { + minSdkVersion 18 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + + implementation project(':premierlibrary') + + //ExoPlayer + implementation 'com.google.android.exoplayer:exoplayer:2.9.6' + implementation 'com.android.support:support-annotations:28.0.0' +} \ No newline at end of file diff --git a/platform/Android/source/ExternalPlayerExoLibrary/consumer-rules.pro b/platform/Android/source/ExternalPlayerExoLibrary/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/platform/Android/source/ExternalPlayerExoLibrary/proguard-rules.pro b/platform/Android/source/ExternalPlayerExoLibrary/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/platform/Android/source/ExternalPlayerExoLibrary/src/main/AndroidManifest.xml b/platform/Android/source/ExternalPlayerExoLibrary/src/main/AndroidManifest.xml new file mode 100644 index 000000000..14df19e32 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + / + \ No newline at end of file diff --git a/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternExoSurface.java b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternExoSurface.java new file mode 100644 index 000000000..e61d25f64 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternExoSurface.java @@ -0,0 +1,22 @@ +package com.aliyun.externalplayer.exo; + +import android.graphics.SurfaceTexture; +import android.view.Surface; +import android.view.TextureView; + +public class ExternExoSurface extends Surface { + + private TextureView mTextureView; + + public ExternExoSurface(SurfaceTexture from) { + super(from); + } + + public void setTextureView(TextureView textureView){ + this.mTextureView = textureView; + } + + public TextureView getTextureView(){ + return mTextureView; + } +} diff --git a/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternExoTextureView.java b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternExoTextureView.java new file mode 100644 index 000000000..700a5ad10 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternExoTextureView.java @@ -0,0 +1,84 @@ +package com.aliyun.externalplayer.exo; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.view.TextureView; + +import com.cicada.player.CicadaPlayer; + +public class ExternExoTextureView extends TextureView { + + private int mVideoWidth; + private int mVideoHeight; + + private CicadaPlayer.ScaleMode mCurrentAspectRatio = CicadaPlayer.ScaleMode.SCALE_ASPECT_FIT; + + public ExternExoTextureView(@NonNull Context context) { + super(context); + } + + public ExternExoTextureView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public ExternExoTextureView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setVideoSize(int width,int height){ + this.mVideoWidth = width; + this.mVideoHeight = height; + } + + public void setScaleType(CicadaPlayer.ScaleMode scaleType){ + this.mCurrentAspectRatio = scaleType; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mVideoHeight == 0 || mVideoWidth == 0) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; + } + + if (getRotation() == 90 || getRotation() == 270) { + int tempSpec = widthMeasureSpec; + widthMeasureSpec = heightMeasureSpec; + heightMeasureSpec = tempSpec; + } + + int width; + int height; + + int measuredWith = MeasureSpec.getSize(widthMeasureSpec); + int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); + + if (mCurrentAspectRatio == CicadaPlayer.ScaleMode.SCALE_TO_FILL) { + width = measuredWith; + height = measuredHeight; + }else if(mCurrentAspectRatio == CicadaPlayer.ScaleMode.SCALE_ASPECT_FILL){ + + if(mVideoWidth > mVideoHeight){ + height = Math.max(heightMeasureSpec,mVideoHeight); + width = height * mVideoWidth / mVideoHeight; + }else{ + width = Math.max(measuredWith,mVideoWidth); + height = width * mVideoHeight / mVideoWidth; + } + }else{ + width = measuredWith; + height = measuredHeight; + + if (mVideoWidth * height < width * mVideoHeight) { + width = height * mVideoWidth / mVideoHeight; + } else if (mVideoWidth * height > width * mVideoHeight) { + height = width * mVideoHeight / mVideoWidth; + } + } + + setMeasuredDimension(width, height); + } + +} diff --git a/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternHttpDataSourceFactory.java b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternHttpDataSourceFactory.java new file mode 100644 index 000000000..8d245fab9 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternHttpDataSourceFactory.java @@ -0,0 +1,145 @@ +package com.aliyun.externalplayer.exo; + +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; +import com.google.android.exoplayer2.upstream.HttpDataSource; +import com.google.android.exoplayer2.upstream.TransferListener; + +import java.util.ArrayList; +import java.util.List; + +public class ExternHttpDataSourceFactory extends HttpDataSource.BaseFactory { + + private final @Nullable + TransferListener listener; + private final boolean allowCrossProtocolRedirects; + List mHttpHeaders = new ArrayList<>(); + private String userAgent; + private int connectTimeoutMillis; + private int readTimeoutMillis; + + public ExternHttpDataSourceFactory() { + this(ExternHttpDataSourceFactory.class.getSimpleName(), null); + } + + /** + * Constructs a DefaultHttpDataSourceFactory. Sets {@link + * DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS} as the connection timeout, {@link + * DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read timeout and disables + * cross-protocol redirects. + * + * @param userAgent The User-Agent string that should be used. + */ + public ExternHttpDataSourceFactory(String userAgent) { + this(userAgent, null); + } + + /** + * Constructs a DefaultHttpDataSourceFactory. Sets {@link + * DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS} as the connection timeout, {@link + * DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read timeout and disables + * cross-protocol redirects. + * + * @param userAgent The User-Agent string that should be used. + * @param listener An optional listener. + * @see #ExternHttpDataSourceFactory(String, TransferListener, int, int, boolean) + */ + public ExternHttpDataSourceFactory(String userAgent, @Nullable TransferListener listener) { + this(userAgent, listener, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, + DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param connectTimeoutMillis The connection timeout that should be used when requesting remote + * data, in milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param readTimeoutMillis The read timeout that should be used when requesting remote data, in + * milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP + * to HTTPS and vice versa) are enabled. + */ + public ExternHttpDataSourceFactory( + String userAgent, + int connectTimeoutMillis, + int readTimeoutMillis, + boolean allowCrossProtocolRedirects) { + this( + userAgent, + /* listener= */ null, + connectTimeoutMillis, + readTimeoutMillis, + allowCrossProtocolRedirects); + } + + /** + * @param userAgent The User-Agent string that should be used. + * @param listener An optional listener. + * @param connectTimeoutMillis The connection timeout that should be used when requesting remote + * data, in milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param readTimeoutMillis The read timeout that should be used when requesting remote data, in + * milliseconds. A timeout of zero is interpreted as an infinite timeout. + * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP + * to HTTPS and vice versa) are enabled. + */ + public ExternHttpDataSourceFactory( + String userAgent, + @Nullable TransferListener listener, + int connectTimeoutMillis, + int readTimeoutMillis, + boolean allowCrossProtocolRedirects) { + this.userAgent = userAgent; + this.listener = listener; + this.connectTimeoutMillis = connectTimeoutMillis; + this.readTimeoutMillis = readTimeoutMillis; + this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; + } + + public void addHttpHeaders(String httpHeader) { + if (!TextUtils.isEmpty(httpHeader)) { + mHttpHeaders.add(httpHeader); + } + } + + public void clearHttpHeaders() { + mHttpHeaders.clear(); + } + + public void setConnectTimeoutMillis(int timeoutMillis) { + connectTimeoutMillis = timeoutMillis; + } + + public void setReadTimeoutMillis(int timeoutMillis) { + readTimeoutMillis = timeoutMillis; + } + + @Override + protected DefaultHttpDataSource createDataSourceInternal( + HttpDataSource.RequestProperties defaultRequestProperties) { + DefaultHttpDataSource dataSource = + new DefaultHttpDataSource( + TextUtils.isEmpty(userAgent) ? ExternHttpDataSourceFactory.class.getSimpleName() : userAgent, + /* contentTypePredicate= */ null, + connectTimeoutMillis, + readTimeoutMillis, + allowCrossProtocolRedirects, + defaultRequestProperties); + for (String header : mHttpHeaders) { + String[] keyValue = header.split(":"); + if (keyValue.length != 2) { + continue; + } + dataSource.setRequestProperty(keyValue[0], keyValue[1]); + } + + if (listener != null) { + dataSource.addTransferListener(listener); + } + return dataSource; + } + + public void setUserAgent(String userAgent) { + this.userAgent = userAgent; + } +} diff --git a/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternPlayerExo.java b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternPlayerExo.java new file mode 100644 index 000000000..ea8961518 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/ExternPlayerExo.java @@ -0,0 +1,1288 @@ +package com.aliyun.externalplayer.exo; + +import android.content.Context; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.text.TextUtils; +import android.view.Surface; +import android.view.TextureView; + +import com.cicada.player.CicadaExternalPlayer; +import com.cicada.player.CicadaPlayer; +import com.cicada.player.bean.ErrorCode; +import com.cicada.player.nativeclass.MediaInfo; +import com.cicada.player.nativeclass.Options; +import com.cicada.player.nativeclass.TrackInfo; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.DefaultRenderersFactory; +import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayerFactory; +import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.PlaybackParameters; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; +import com.google.android.exoplayer2.drm.DrmSessionManager; +import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; +import com.google.android.exoplayer2.drm.FrameworkMediaDrm; +import com.google.android.exoplayer2.drm.MediaDrmCallback; +import com.google.android.exoplayer2.drm.UnsupportedDrmException; +import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.MediaSource; +import com.google.android.exoplayer2.source.MergingMediaSource; +import com.google.android.exoplayer2.source.SingleSampleMediaSource; +import com.google.android.exoplayer2.source.TrackGroup; +import com.google.android.exoplayer2.source.TrackGroupArray; +import com.google.android.exoplayer2.source.dash.DashMediaSource; +import com.google.android.exoplayer2.source.hls.HlsMediaSource; +import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; +import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.text.TextOutput; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.trackselection.MappingTrackSelector; +import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; +import com.google.android.exoplayer2.util.MimeTypes; +import com.google.android.exoplayer2.util.Util; +import com.google.android.exoplayer2.video.VideoListener; + +import java.util.ArrayList; +import java.util.List; + +public class ExternPlayerExo extends CicadaExternalPlayer { + private static final String PLAYER_NAME = "ExoPlayer"; + private static final String TAG = PLAYER_NAME; + + PlayerStatus mLastPlayState = PlayerStatus.PLAYER_IDLE; + private Context mContext = null; + private SimpleExoPlayer mExoPlayer = null; + private String mPlayUrl; + private OnPreparedListener mOutOnPreparedListener; + private OnLoopingStartListener mOutOnLoopingStartListener; + private OnCompletionListener mOutOnCompletionListener; + private OnFirstFrameRenderListener mOutOnFirstFrameRenderListener; + private OnLoadStatusListener mOutOnLoadStatusListener; + private OnAutoPlayStartListener mOutOnAutoPlayStartListener; + private OnSeekStatusListener mOutOnSeekStatusListener; + private OnPositionUpdateListener mOutOnPositionUpdateListener; + private OnBufferPositionUpdateListener mOutOnBufferPositionUpdateListener; + private OnVideoSizeChangedListener mOutOnVideoSizeChangedListener; + private OnStatusChangedListener mOutOnStatusChangedListener; + private OnVideoRenderedListener mOutOnVideoRenderedListener; + private OnErrorListener mOutOnErrorListener; + private OnEventListener mOutOnEventListener; + private OnStreamInfoGetListener mOutOnStreamInfoGetListener; + private OnStreamSwitchSucListener mOutOnStreamSwitchSucListener; + private OnCaptureScreenListener mOutOnCaptureScreenListener; + private OnSubtitleListener mOutOnSubtitleListener; + private OnDRMCallback mOutOnDRMCallback; + + private List mExoTrackInfoList = new ArrayList<>(); + private DefaultTrackSelector mTrackSelector; + private Handler timer = null; + private int TIMER_WHAT_HALF_SECOND = 1000; + private ExoTrackInfo mLastSwitchTrackInfo = null; + private boolean mMute = false; + private float mLastVolume = 1f; + private String mUserAgent = null; + private ExternHttpDataSourceFactory mDataSourceFactory = null; + private Surface mSurface = null; + private int mTimeOut = 5 * 1000; + private List mHttpHeader = new ArrayList<>(); + private List mExtSubtitleUrls = new ArrayList<>(); + private FrameworkMediaDrm mediaDrm; + private CicadaPlayer.MirrorMode mMirrorMode = CicadaPlayer.MirrorMode.MIRROR_MODE_NONE; + private CicadaPlayer.RotateMode mRotateMode = CicadaPlayer.RotateMode.ROTATE_0; + private CicadaPlayer.ScaleMode mScaleMode = CicadaPlayer.ScaleMode.SCALE_ASPECT_FIT; + private TextureView mTextureView; + private int mVideoHeight; + private int mVideoWith; + + private ExternPlayerExo(Context context, Options options) { + mContext = context; + Looper looper = Looper.myLooper(); + if (looper == null) { + looper = Looper.getMainLooper(); + } + + timer = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + if (msg.what == TIMER_WHAT_HALF_SECOND) { + + if (mLastPlayState.getValue() >= PlayerStatus.PLAYER_PREPARED.getValue() + && mLastPlayState.getValue() <= PlayerStatus.PLAYER_COMPLETION.getValue()) { + if (mOutOnBufferPositionUpdateListener != null) { + mOutOnBufferPositionUpdateListener.onBufferPositionUpdate(getBufferPosition()); + } + if (mOutOnPositionUpdateListener != null) { + mOutOnPositionUpdateListener.onPositionUpdate(getPlayingPosition()); + } + } + + sendHalfSecondTimer(); + } + + super.handleMessage(msg); + + } + }; + + } + + public ExternPlayerExo() { + + } + + private static boolean isSupportRender(MappingTrackSelector.MappedTrackInfo mappedTrackInfo, int rendererIndex) { + TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex); + if (trackGroupArray.length == 0) { + return false; + } + int trackType = mappedTrackInfo.getRendererType(rendererIndex); + switch (trackType) { + case C.TRACK_TYPE_VIDEO: + case C.TRACK_TYPE_AUDIO: + case C.TRACK_TYPE_TEXT: + return true; + default: + return false; + } + } + + private static int inferPrimaryTrackType(Format format) { + int trackType = MimeTypes.getTrackType(format.sampleMimeType); + if (trackType != C.TRACK_TYPE_UNKNOWN) { + return trackType; + } + if (MimeTypes.getVideoMediaMimeType(format.codecs) != null) { + return C.TRACK_TYPE_VIDEO; + } + if (MimeTypes.getAudioMediaMimeType(format.codecs) != null) { + return C.TRACK_TYPE_AUDIO; + } + if (format.width != Format.NO_VALUE || format.height != Format.NO_VALUE) { + return C.TRACK_TYPE_VIDEO; + } + if (format.channelCount != Format.NO_VALUE || format.sampleRate != Format.NO_VALUE) { + return C.TRACK_TYPE_AUDIO; + } + return C.TRACK_TYPE_UNKNOWN; + } + + private void addSubtitleListener() { + mExoPlayer.addTextOutput(new TextOutput() { + @Override + public void onCues(List cues) { + int cicadaIndex = -1; + for (ExoTrackInfo info : mExoTrackInfoList) { + if (info.type == C.TRACK_TYPE_TEXT && info.selected) { + cicadaIndex = info.cicadaTrack.index; + break; + } + } + if (cicadaIndex < 0) { + return; + } + //字幕相关的接口 + if (cues == null || cues.size() == 0) { + //隐藏 + if (mOutOnSubtitleListener != null) { + mOutOnSubtitleListener.onSubtitleHide(cicadaIndex, 0); + } + } else { + String text = String.valueOf(cues.get(0).text); + if (mOutOnSubtitleListener != null) { + mOutOnSubtitleListener.onSubtitleShow(cicadaIndex, 0, text); + } + } + } + }); + } + + private void updateSelectedTrack(TrackSelectionArray trackSelections) { + for (ExoTrackInfo trackInfo : mExoTrackInfoList) { + trackInfo.selected = false; + } + + int selectionCount = trackSelections.length; + for (int selectIndex = 0; selectIndex < selectionCount; selectIndex++) { + TrackSelection trackSelection = trackSelections.get(selectIndex); + if (trackSelection == null) { + continue; + } + Format selectedFormat = trackSelection.getSelectedFormat(); + for (ExoTrackInfo trackInfo : mExoTrackInfoList) { + if (trackInfo.exoFormat == selectedFormat) { + trackInfo.selected = true; + } + } + } + } + + private void sendHalfSecondTimer() { + timer.removeMessages(TIMER_WHAT_HALF_SECOND); + if (mLastPlayState.getValue() >= PlayerStatus.PLAYER_PREPARED.getValue() + && mLastPlayState.getValue() <= PlayerStatus.PLAYER_COMPLETION.getValue()) { + timer.sendEmptyMessageDelayed(TIMER_WHAT_HALF_SECOND, 500); + } + } + + private void notifyGetTrackInfo() { + if (mOutOnStreamInfoGetListener != null) { + int trackSize = mExoTrackInfoList.size(); + TrackInfo[] trackInfos = new TrackInfo[trackSize]; + for (int i = 0; i < trackSize; i++) { + trackInfos[i] = mExoTrackInfoList.get(i).cicadaTrack; + } + MediaInfo info = new MediaInfo(); + info.setTrackInfos(trackInfos); + mOutOnStreamInfoGetListener.OnStreamInfoGet(info); + } + } + + private void fillExoTrackInfoList() { + mExoTrackInfoList.clear(); + + MappingTrackSelector.MappedTrackInfo mappedTrackInfo = mTrackSelector.getCurrentMappedTrackInfo(); + if (mappedTrackInfo == null) { + return; + } + + int renderCount = mappedTrackInfo.getRendererCount(); + int cicadaTrackIndex = 0; + for (int renderIndex = 0; renderIndex < renderCount; renderIndex++) { + if (!isSupportRender(mappedTrackInfo, renderIndex)) { + continue; + } + + TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(renderIndex); + int length = trackGroupArray.length; + for (int groupIndex = 0; groupIndex < length; groupIndex++) { + TrackGroup trackGroup = trackGroupArray.get(groupIndex); + int formatLength = trackGroup.length; + for (int trackIndex = 0; trackIndex < formatLength; trackIndex++) { + Format format = trackGroup.getFormat(trackIndex); + int trackType = inferPrimaryTrackType(format); + TrackInfo cicadaTrack = null; + if (trackType == C.TRACK_TYPE_VIDEO) { + //视频流 + cicadaTrack = new TrackInfo(); + cicadaTrack.index = cicadaTrackIndex; + cicadaTrack.mType = TrackInfo.Type.TYPE_VIDEO; + cicadaTrack.videoBitrate = format.bitrate; + cicadaTrack.videoWidth = format.width; + cicadaTrack.videoHeight = format.height; + } else if (trackType == C.TRACK_TYPE_AUDIO) { + //音频流 + cicadaTrack = new TrackInfo(); + cicadaTrack.index = cicadaTrackIndex; + cicadaTrack.mType = TrackInfo.Type.TYPE_AUDIO; + cicadaTrack.audioLang = format.language; + cicadaTrack.audioChannels = format.channelCount; + cicadaTrack.audioSampleRate = format.sampleRate; + //TODO audioSampleFormat 这个值设置 +// cicadaTrack.audioSampleFormat = format.; + } else if (trackType == C.TRACK_TYPE_TEXT) { + //字幕流 + cicadaTrack = new TrackInfo(); + cicadaTrack.index = cicadaTrackIndex; + cicadaTrack.mType = TrackInfo.Type.TYPE_SUBTITLE; + cicadaTrack.subtitleLang = format.language; + } + + if (cicadaTrack != null) { + cicadaTrackIndex++; + ExoTrackInfo exoTrackInfo = new ExoTrackInfo(); + exoTrackInfo.exoFormat = format; + exoTrackInfo.groupIndex = groupIndex; + exoTrackInfo.renderIndex = renderIndex; + exoTrackInfo.trackIndex = trackIndex; + exoTrackInfo.cicadaTrack = cicadaTrack; + exoTrackInfo.selected = false; + exoTrackInfo.type = trackType; + mExoTrackInfoList.add(exoTrackInfo); + } + } + } + } + + } + + private void changePlayState(PlayerStatus newState) { + PlayerStatus oldState = mLastPlayState; + mLastPlayState = newState; + if (oldState != newState) { + sendHalfSecondTimer(); + if (mOutOnStatusChangedListener != null) { + mOutOnStatusChangedListener.onStatusChanged(oldState.getValue(), newState.getValue()); + } + } + + } + + @Override + public boolean isSupport(Options options) { + if (options == null) { + return false; + } + + String name = options.get("name"); + if (PLAYER_NAME.equals(name)) { + return true; + } + + return false; + } + + @Override + public CicadaExternalPlayer create(Context context, Options options) { + return new ExternPlayerExo(context, options); + } + + @Override + public void setDataSource(String url) { + mPlayUrl = url; + changePlayState(PlayerStatus.PLAYER_INITIALZED); + } + + private MediaSource buildMediaSource() { + Uri uri = Uri.parse(mPlayUrl); + int type = Util.inferContentType(uri); + mDataSourceFactory = new ExternHttpDataSourceFactory(); + mDataSourceFactory.setReadTimeoutMillis(mTimeOut); + mDataSourceFactory.setConnectTimeoutMillis(mTimeOut); + mDataSourceFactory.setUserAgent(mUserAgent); + for (String header : mHttpHeader) { + mDataSourceFactory.addHttpHeaders(header); + } + + MediaSource mediaSource = null; + switch (type) { + case C.TYPE_DASH: + mediaSource = new DashMediaSource.Factory(mDataSourceFactory).createMediaSource(uri); + break; + case C.TYPE_SS: + mediaSource = new SsMediaSource.Factory(mDataSourceFactory).createMediaSource(uri); + break; + case C.TYPE_HLS: + mediaSource = new HlsMediaSource.Factory(mDataSourceFactory).createMediaSource(uri); + break; + case C.TYPE_OTHER: + mediaSource = new ExtractorMediaSource.Factory(mDataSourceFactory).createMediaSource(uri); + break; + } + + MergingMediaSource mergedSource = null; + + //测试外部字幕 +// mExtSubtitleUrls.add("https://alivc-demo-vod.aliyuncs.com/b4da45beb07b4d5b81b54b1ac50fb502/subtitles/cn/6b4949a8c3950f8aa76f1fed6730e525.vtt"); + if (!mExtSubtitleUrls.isEmpty()) { + + int size = mExtSubtitleUrls.size(); + for (int i = 0; i < size; i++) { + Format textFormat = Format.createTextSampleFormat(null, MimeTypes.TEXT_VTT, null, + Format.NO_VALUE, 0, "Subtitle:" + i, null, 0); + Uri subtitleUri = Uri.parse(mExtSubtitleUrls.get(i)); + MediaSource subtitleSource = new SingleSampleMediaSource(subtitleUri, mDataSourceFactory, textFormat, C.TIME_UNSET); + mergedSource = new MergingMediaSource(mediaSource, subtitleSource); + } + } + + if (mergedSource != null) { + return mergedSource; + } + return mediaSource; + } + + @Override + public void setSurface(Surface surface) { + if (mExoPlayer == null) { + mSurface = surface; + } else { + mExoPlayer.setVideoSurface(surface); + } + if (surface instanceof ExternExoSurface) { + mTextureView = ((ExternExoSurface) surface).getTextureView(); + } + } + + @Override + public void prepare() { + MediaSource mMediaSource = buildMediaSource(); + if (mMediaSource == null) { + return; + } + + initPlayer(); + + if (mExoPlayer == null) { + return; + } + + changePlayState(PlayerStatus.PLAYER_PREPARING); + mExoPlayer.prepare(mMediaSource, false, true); + } + + private void initPlayer() { + //TODO 判断是否是WideVine的流 + boolean isWideVine = true; + DrmSessionManager drmSessionManager = null; + + if (isWideVine) { + drmSessionManager = buildDrmSessionManagerV18(); + } + + mTrackSelector = new DefaultTrackSelector(); + mExoPlayer = ExoPlayerFactory.newSimpleInstance( + mContext, new DefaultRenderersFactory(mContext), mTrackSelector, drmSessionManager); + + mExoPlayer.setPlayWhenReady(false); + mExoPlayer.setVideoSurface(mSurface); + + addSubtitleListener(); + Player.VideoComponent videoComponent = mExoPlayer.getVideoComponent(); + if (videoComponent != null) { + videoComponent.addVideoListener(new VideoListener() { + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { + mVideoWith = width; + mVideoHeight = height; + if (mTextureView instanceof ExternExoTextureView) { + ((ExternExoTextureView) mTextureView).setVideoSize(mVideoWith, mVideoHeight); + mTextureView.requestLayout(); + } + if (mOutOnVideoSizeChangedListener != null) { + mOutOnVideoSizeChangedListener.onVideoSizeChanged(width, height); + } + } + + @Override + public void onRenderedFirstFrame() { + if (mExoPlayer.getPlayWhenReady()) { + if (mOutOnAutoPlayStartListener != null) { + mOutOnAutoPlayStartListener.onAutoPlayStart(); + } + } + + if (mOutOnFirstFrameRenderListener != null) { + mOutOnFirstFrameRenderListener.onFirstFrameRender(); + } + } + }); + } + + mExoPlayer.addListener(new Player.EventListener() { + @Override + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + if (playbackState == Player.STATE_ENDED) { + changePlayState(PlayerStatus.PLAYER_COMPLETION); + if (mOutOnCompletionListener != null) { + mOutOnCompletionListener.onCompletion(); + } + + } else if (playbackState == Player.STATE_BUFFERING) { + + } + + } + + @Override + public void onLoadingChanged(boolean isLoading) { + + if (mOutOnLoadStatusListener != null) { + if (isLoading) { + mOutOnLoadStatusListener.onLoadingStart(); + } else { + mOutOnLoadStatusListener.onLoadingEnd(); + } + } + } + + @Override + public void onPlayerError(ExoPlaybackException error) { + changePlayState(PlayerStatus.PLAYER_ERROR); + + if (mOutOnErrorListener != null) { + if (error.type == ExoPlaybackException.TYPE_SOURCE) { + mOutOnErrorListener.onError(ErrorCode.ERROR_DEMUXER_OPENURL.getValue(), error.getMessage()); + } else if (error.type == ExoPlaybackException.TYPE_RENDERER) { + mOutOnErrorListener.onError(ErrorCode.ERROR_CODEC_UNKNOWN.getValue(), error.getMessage()); + } else if (error.type == ExoPlaybackException.TYPE_UNEXPECTED) { + mOutOnErrorListener.onError(ErrorCode.ERROR_UNKNOWN.getValue(), error.getMessage()); + } + } + } + + @Override + public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { + if (mExoTrackInfoList.isEmpty()) { + fillExoTrackInfoList(); + notifyGetTrackInfo(); + } + + if (mLastPlayState == PlayerStatus.PLAYER_PREPARING) { + //播放多码率时,不会回调onPlayerStateChanged的回调 + changePlayState(PlayerStatus.PLAYER_PREPARED); + //准备完成 + if (mOutOnPreparedListener != null) { + mOutOnPreparedListener.onPrepared(); + } + } + + updateSelectedTrack(trackSelections); + + if (mOutOnStreamSwitchSucListener != null && mLastSwitchTrackInfo != null) { + mOutOnStreamSwitchSucListener.onStreamSwitchSuc(convertType(mLastSwitchTrackInfo.cicadaTrack.mType), mLastSwitchTrackInfo.cicadaTrack); + } + } + + @Override + public void onSeekProcessed() { + if (mOutOnSeekStatusListener != null) { + mOutOnSeekStatusListener.onSeekEnd(false); + } + } + + @Override + public void onPositionDiscontinuity(int reason) { + if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) { + //循环播放 + if (mOutOnLoopingStartListener != null) { + mOutOnLoopingStartListener.onLoopingStart(); + } + } + } + }); + + } + + @Override + public void start() { + if (mExoPlayer == null) { + return; + } + + mExoPlayer.setPlayWhenReady(true); + + changePlayState(PlayerStatus.PLAYER_PLAYING); + } + + @Override + public void pause() { + if (mExoPlayer == null) { + return; + } + + mExoPlayer.setPlayWhenReady(false); + changePlayState(PlayerStatus.PLAYER_PAUSED); + } + + @Override + public void stop() { + if (mExoPlayer == null) { + return; + } + mExoPlayer.stop(true); + changePlayState(PlayerStatus.PLAYER_STOPPED); + } + + @Override + public void release() { + if (mExoPlayer == null) { + return; + } + changePlayState(PlayerStatus.PLAYER_IDLE); + releaseMediaDrm(); + mExoPlayer.release(); + + } + + @Override + public StreamType switchStream(int index) { + ExoTrackInfo targetInfo = null; + for (ExoTrackInfo info : mExoTrackInfoList) { + if (info.cicadaTrack.index == index) { + targetInfo = info; + break; + } + } + + if (targetInfo == null) { + return StreamType.ST_TYPE_UNKNOWN; + } + + StreamType streamType = convertType(targetInfo.cicadaTrack.mType); + boolean success = selectTrack(targetInfo); + return streamType; + } + + private boolean selectTrack(ExoTrackInfo exoTrackInfo) { + if (mTrackSelector == null) { + return false; + } + MappingTrackSelector.MappedTrackInfo mappedTrackInfo = mTrackSelector.getCurrentMappedTrackInfo(); + if (mappedTrackInfo == null) { + return false; + } + DefaultTrackSelector.Parameters parameters = mTrackSelector.getParameters(); + if (parameters == null) { + return false; + } + + mLastSwitchTrackInfo = exoTrackInfo; + if (exoTrackInfo.cicadaTrack.mType == TrackInfo.Type.TYPE_SUBTITLE) { + changeSubtitleSelection(true, exoTrackInfo); + } + + DefaultTrackSelector.ParametersBuilder builder = parameters.buildUpon(); + builder.setSelectionOverride( + exoTrackInfo.renderIndex, + mappedTrackInfo.getTrackGroups(exoTrackInfo.renderIndex), + new DefaultTrackSelector.SelectionOverride(exoTrackInfo.groupIndex, exoTrackInfo.trackIndex)); + + mTrackSelector.setParameters(builder); + + return true; + } + + private StreamType convertType(TrackInfo.Type type) { + if (type == TrackInfo.Type.TYPE_AUDIO) { + return StreamType.ST_TYPE_AUDIO; + } else if (type == TrackInfo.Type.TYPE_VIDEO) { + return StreamType.ST_TYPE_VIDEO; + } else if (type == TrackInfo.Type.TYPE_SUBTITLE) { + return StreamType.ST_TYPE_SUB; + } + return StreamType.ST_TYPE_UNKNOWN; + } + + @Override + public void seekTo(long seekPos, boolean accurate) { + if (mExoPlayer == null) { + return; + } + + if (mOutOnSeekStatusListener != null) { + mOutOnSeekStatusListener.onSeekStart(false); + } + + mExoPlayer.seekTo(seekPos); + } + + @Override + public PlayerStatus getPlayerStatus() { + return mLastPlayState; + } + + @Override + public long getDuration() { + if (mExoPlayer == null) { + return 0; + } + long duration = mExoPlayer.getDuration(); + return duration; + } + + @Override + public long getPlayingPosition() { + if (mExoPlayer == null) { + return 0; + } + return mExoPlayer.getCurrentPosition(); + } + + @Override + public long getBufferPosition() { + if (mExoPlayer == null) { + return 0; + } + return mExoPlayer.getBufferedPosition(); + } + + @Override + public void mute(boolean mute) { + if (mExoPlayer == null) { + return; + } + + mMute = mute; + if (mute) { + mLastVolume = getVolume(); + setVolume(0f); + } else { + setVolume(mLastVolume); + } + } + + @Override + public boolean isMute() { + return mMute; + } + + @Override + public float getVideoRenderFps() { + //TODO + return 0; + } + + @Override + public void enterBackGround(boolean back) { + //TODO + } + + @Override + public CicadaPlayer.ScaleMode getScaleMode() { + if (mExoPlayer == null) { + return CicadaPlayer.ScaleMode.SCALE_ASPECT_FIT; + } + + int mode = mExoPlayer.getVideoScalingMode(); + CicadaPlayer.ScaleMode scaleMode = CicadaPlayer.ScaleMode.SCALE_ASPECT_FIT; + if (mode == C.VIDEO_SCALING_MODE_SCALE_TO_FIT) { + scaleMode = CicadaPlayer.ScaleMode.SCALE_ASPECT_FIT; + } else if (mode == C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING) { + scaleMode = CicadaPlayer.ScaleMode.SCALE_ASPECT_FILL; + } else if (mode == C.VIDEO_SCALING_MODE_DEFAULT) { + scaleMode = CicadaPlayer.ScaleMode.SCALE_TO_FILL; + } + + return scaleMode; + } + + @Override + public void setScaleMode(CicadaPlayer.ScaleMode scaleMode) { + if (mExoPlayer == null) { + return; + } + + if (mTextureView == null) { + return; + } + + ((ExternExoTextureView) mTextureView).setScaleType(scaleMode); + + this.mScaleMode = scaleMode; + + mTextureView.requestLayout(); + } + + @Override + public CicadaPlayer.RotateMode getRotateMode() { + if (mExoPlayer == null) { + return CicadaPlayer.RotateMode.ROTATE_0; + } + return this.mRotateMode; + } + + @Override + public void setRotateMode(CicadaPlayer.RotateMode rotateMode) { + if (mExoPlayer == null) { + return; + } + + if (mTextureView == null) { + return; + } + + if (rotateMode == CicadaPlayer.RotateMode.ROTATE_0) { + mTextureView.setRotation(0f); + } else if (rotateMode == CicadaPlayer.RotateMode.ROTATE_90) { + mTextureView.setRotation(90f); + } else if (rotateMode == CicadaPlayer.RotateMode.ROTATE_180) { + mTextureView.setRotation(180f); + } else { + mTextureView.setRotation(270f); + } + mTextureView.requestLayout(); + + this.mRotateMode = rotateMode; + } + + @Override + public CicadaPlayer.MirrorMode getMirrorMode() { + if (mExoPlayer == null) { + return CicadaPlayer.MirrorMode.MIRROR_MODE_NONE; + } + return mMirrorMode; + } + + @Override + public void setMirrorMode(CicadaPlayer.MirrorMode mirrorMode) { + if (mExoPlayer == null) { + return; + } + if (mTextureView == null) { + return; + } + if (mirrorMode == CicadaPlayer.MirrorMode.MIRROR_MODE_NONE) { + mTextureView.setScaleX(1f); + mTextureView.setScaleY(1f); + } else if (mirrorMode == CicadaPlayer.MirrorMode.MIRROR_MODE_VERTICAL) { + mTextureView.setScaleX(1f); + mTextureView.setScaleY(-1f); + } else { + mTextureView.setScaleX(-1f); + mTextureView.setScaleY(1f); + } + mTextureView.invalidate(); + this.mMirrorMode = mirrorMode; + } + + @Override + public void setVideoBackgroundColor(long color) { + if (mExoPlayer == null) { + return; + } + //TODO + } + + @Override + public int getCurrentStreamIndex(StreamType streamType) { + TrackInfo cicadaTrackInfo = getCurrentStreamInfo(streamType); + if (cicadaTrackInfo != null) { + return cicadaTrackInfo.index; + } + return -1; + } + + @Override + public TrackInfo getCurrentStreamInfo(StreamType streamType) { + for (ExoTrackInfo info : mExoTrackInfoList) { + if (info.selected) { + if (streamType == StreamType.ST_TYPE_VIDEO && info.type == C.TRACK_TYPE_VIDEO) { + return info.cicadaTrack; + } else if (streamType == StreamType.ST_TYPE_AUDIO && info.type == C.TRACK_TYPE_AUDIO) { + return info.cicadaTrack; + } else if (streamType == StreamType.ST_TYPE_SUB && info.type == C.TRACK_TYPE_TEXT) { + return info.cicadaTrack; + } + } + } + return null; + } + + @Override + public long getMasterClockPts() { + return 0; + } + + @Override + public void setTimeout(int timeOut) { + mTimeOut = timeOut; + if (mDataSourceFactory != null) { + mDataSourceFactory.setConnectTimeoutMillis(timeOut); + mDataSourceFactory.setReadTimeoutMillis(timeOut); + } + } + + @Override + public void setDropBufferThreshold(int dropValue) { + //TODO + } + + @Override + public DecoderType getDecoderType() { + return null; + } + + @Override + public void setDecoderType(DecoderType type) { + + } + + @Override + public float getVolume() { + if (mExoPlayer == null) { + return 0; + } + return mExoPlayer.getVolume(); + } + + @Override + public void setVolume(float volume) { + if (mExoPlayer == null) { + return; + } + + mLastVolume = volume; + + if (!mMute) { + mExoPlayer.setVolume(volume); + } + } + + @Override + public void setRefer(String refer) { + + } + + @Override + public void setUserAgent(String userAgent) { + mUserAgent = userAgent; + if (mDataSourceFactory != null) { + mDataSourceFactory.setUserAgent(userAgent); + } + } + + @Override + public boolean isLooping() { + if (mExoPlayer == null) { + return false; + } + + return mExoPlayer.getRepeatMode() == Player.REPEAT_MODE_ALL; + } + + @Override + public void setLooping(boolean bCirclePlay) { + if (mExoPlayer == null) { + return; + } + mExoPlayer.setRepeatMode(bCirclePlay ? Player.REPEAT_MODE_ALL : Player.REPEAT_MODE_ONE); + } + + @Override + public void captureScreen() { + if (mExoPlayer == null) { + return; + } + + //TODO + } + + @Override + public int getVideoWidth() { + if (mExoPlayer == null || mExoPlayer.getVideoFormat() == null) { + return 0; + } + return mExoPlayer.getVideoFormat().width; + } + + @Override + public int getVideoHeight() { + if (mExoPlayer == null || mExoPlayer.getVideoFormat() == null) { + return 0; + } + return mExoPlayer.getVideoFormat().height; + } + + @Override + public int getVideoRotation() { + if (mExoPlayer == null || mExoPlayer.getVideoFormat() == null) { + return 0; + } + return mExoPlayer.getVideoFormat().rotationDegrees; + } + + @Override + public String getPropertyString(PropertyKey key) { + return null; + } + + @Override + public long getPropertyInt(PropertyKey key) { + return 0; + } + + @Override + public long getPropertyLong(PropertyKey key) { + return 0; + } + + @Override + public float getVideoDecodeFps() { + return 0; + } + + @Override + public int setOption(String key, String value) { + return 0; + } + + @Override + public String getOption(String key) { + return null; + } + + @Override + public float getSpeed() { + if (mExoPlayer == null) { + return 1f; + } + PlaybackParameters playbackParameters = mExoPlayer.getPlaybackParameters(); + if (playbackParameters == null) { + return 1f; + } + return playbackParameters.speed; + } + + @Override + public void setSpeed(float speed) { + if (mExoPlayer == null) { + return; + } + PlaybackParameters playbackParameters = new PlaybackParameters(speed, 1); + mExoPlayer.setPlaybackParameters(playbackParameters); + } + + @Override + public void addCustomHttpHeader(String httpHeader) { + if (!TextUtils.isEmpty(httpHeader)) { + mHttpHeader.add(httpHeader); + } + + if (mDataSourceFactory != null) { + mDataSourceFactory.addHttpHeaders(httpHeader); + } + } + + @Override + public void removeAllCustomHttpHeader() { + mHttpHeader.clear(); + + if (mDataSourceFactory != null) { + mDataSourceFactory.clearHttpHeaders(); + } + } + + @Override + public void addExtSubtitle(String uri) { + if (!TextUtils.isEmpty(uri)) { + mExtSubtitleUrls.add(uri); + } + } + + @Override + public int selectExtSubtitle(int index, boolean bSelect) { + ExoTrackInfo targetInfo = null; + for (ExoTrackInfo info : mExoTrackInfoList) { + if (info.cicadaTrack.index == index) { + targetInfo = info; + } + } + + if (targetInfo == null) { + return -1; + } + + if (!bSelect) { + int cicadaIndex = -1; + for (ExoTrackInfo info : mExoTrackInfoList) { + if (info.type == C.TRACK_TYPE_TEXT && info.selected) { + cicadaIndex = info.cicadaTrack.index; + break; + } + } + + if (cicadaIndex == targetInfo.cicadaTrack.index) { + if (mOutOnSubtitleListener != null) { + mOutOnSubtitleListener.onSubtitleHide(targetInfo.cicadaTrack.index, 0); + } + } + } + + boolean b = changeSubtitleSelection(bSelect, targetInfo); + return b ? 0 : -1; + } + + private boolean changeSubtitleSelection(boolean bSelect, ExoTrackInfo targetInfo) { + TrackSelectionArray trackSelections = mExoPlayer.getCurrentTrackSelections(); + int selectionCount = trackSelections.length; + for (int selectIndex = 0; selectIndex < selectionCount; selectIndex++) { + TrackSelection trackSelection = trackSelections.get(selectIndex); + if (trackSelection == null) { + continue; + } + Format selectedFormat = trackSelection.getSelectedFormat(); + if (targetInfo.exoFormat == selectedFormat) { + if (bSelect) { + trackSelection.enable(); + targetInfo.selected = true; + } else { + trackSelection.disable(); + targetInfo.selected = false; + } + return true; + } + } + return false; + } + + @Override + public void reLoad() { + if (mExoPlayer == null) { + return; + } + + mExoPlayer.retry(); + } + + @Override + public boolean isAutoPlay() { + if (mExoPlayer == null) { + return false; + } + return mExoPlayer.getPlayWhenReady(); + } + + @Override + public void setAutoPlay(boolean bAutoPlay) { + if (mExoPlayer == null) { + return; + } + + mExoPlayer.setPlayWhenReady(bAutoPlay); + } + + @Override + public int invokeComponent(String content) { + return 0; + } + + @Override + public void setOnPreparedListener(OnPreparedListener onPreparedListener) { + mOutOnPreparedListener = onPreparedListener; + } + + @Override + public void setOnLoopingStartListener(OnLoopingStartListener onLoopingStartListener) { + mOutOnLoopingStartListener = onLoopingStartListener; + } + + @Override + public void setOnCompletionListener(OnCompletionListener onCompletionListener) { + mOutOnCompletionListener = onCompletionListener; + } + + @Override + public void setOnFirstFrameRenderListener(OnFirstFrameRenderListener onFirstFrameRenderListener) { + mOutOnFirstFrameRenderListener = onFirstFrameRenderListener; + } + + @Override + public void setOnLoadStatusListener(OnLoadStatusListener onLoadStatusListener) { + mOutOnLoadStatusListener = onLoadStatusListener; + } + + @Override + public void setOnAutoPlayStartListener(OnAutoPlayStartListener onAutoPlayStartListener) { + mOutOnAutoPlayStartListener = onAutoPlayStartListener; + } + + @Override + public void setOnSeekStatusListener(OnSeekStatusListener onSeekStatusListener) { + mOutOnSeekStatusListener = onSeekStatusListener; + } + + @Override + public void setOnPositionUpdateListener(OnPositionUpdateListener onPositionUpdateListener) { + mOutOnPositionUpdateListener = onPositionUpdateListener; + } + + @Override + public void setOnBufferPositionUpdateListener(OnBufferPositionUpdateListener onBufferPositionUpdateListener) { + mOutOnBufferPositionUpdateListener = onBufferPositionUpdateListener; + } + + @Override + public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener onVideoSizeChangedListener) { + mOutOnVideoSizeChangedListener = onVideoSizeChangedListener; + } + + @Override + public void setOnStatusChangedListener(OnStatusChangedListener onStatusChangedListener) { + mOutOnStatusChangedListener = onStatusChangedListener; + } + + @Override + public void setOnVideoRenderedListener(OnVideoRenderedListener onVideoRenderedListener) { + mOutOnVideoRenderedListener = onVideoRenderedListener; + } + + @Override + public void setOnErrorListener(OnErrorListener onErrorListener) { + mOutOnErrorListener = onErrorListener; + } + + @Override + public void setOnEventListener(OnEventListener onEventListener) { + mOutOnEventListener = onEventListener; + } + + @Override + public void setOnStreamInfoGetListener(OnStreamInfoGetListener onStreamInfoGetListener) { + mOutOnStreamInfoGetListener = onStreamInfoGetListener; + } + + @Override + public void setOnStreamSwitchSucListener(OnStreamSwitchSucListener onStreamSwitchSucListener) { + mOutOnStreamSwitchSucListener = onStreamSwitchSucListener; + } + + @Override + public void setOnCaptureScreenListener(OnCaptureScreenListener onCaptureScreenListener) { + mOutOnCaptureScreenListener = onCaptureScreenListener; + } + + @Override + public void setOnSubtitleListener(OnSubtitleListener onSubtitleListener) { + mOutOnSubtitleListener = onSubtitleListener; + } + + @Override + public void setOnDrmCallback(OnDRMCallback onDRMCallback) { + mOutOnDRMCallback = onDRMCallback; + } + + private DefaultDrmSessionManager buildDrmSessionManagerV18() { + MediaDrmCallback drmCallback = + new WideVineDrmCallback(new OnDRMCallback() { + + @Override + public byte[] onRequestProvision(String provisionUrl, byte[] data) { + if (mOutOnDRMCallback != null) { + return mOutOnDRMCallback.onRequestProvision(provisionUrl, data); + } + return null; + } + + @Override + public byte[] onRequestKey(String licenseUrl, byte[] data) { + if (mOutOnDRMCallback != null) { + return mOutOnDRMCallback.onRequestKey(licenseUrl, data); + } + return null; + } + }); + + releaseMediaDrm(); + try { + mediaDrm = FrameworkMediaDrm.newInstance(C.WIDEVINE_UUID); + } catch (UnsupportedDrmException e) { + e.printStackTrace(); + return null; + } + return new DefaultDrmSessionManager<>(C.WIDEVINE_UUID, mediaDrm, drmCallback, null, false); + } + + private void releaseMediaDrm() { + if (mediaDrm != null) { + mediaDrm.release(); + mediaDrm = null; + } + } + + private static class ExoTrackInfo { + Format exoFormat; + int renderIndex; + int groupIndex; + int trackIndex; + TrackInfo cicadaTrack; + boolean selected = false; + int type; + } + +} diff --git a/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/WideVineDrmCallback.java b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/WideVineDrmCallback.java new file mode 100644 index 000000000..fa1d9d8c6 --- /dev/null +++ b/platform/Android/source/ExternalPlayerExoLibrary/src/main/java/com/aliyun/externalplayer/exo/WideVineDrmCallback.java @@ -0,0 +1,32 @@ +package com.aliyun.externalplayer.exo; + +import com.cicada.player.CicadaExternalPlayer; +import com.google.android.exoplayer2.drm.ExoMediaDrm; +import com.google.android.exoplayer2.drm.MediaDrmCallback; + +import java.util.UUID; + +public class WideVineDrmCallback implements MediaDrmCallback { + + private final CicadaExternalPlayer.OnDRMCallback mDrmCallback; + + public WideVineDrmCallback(CicadaExternalPlayer.OnDRMCallback drmCallback) { + mDrmCallback = drmCallback; + } + + @Override + public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request) throws Exception { + if (mDrmCallback != null) { + return mDrmCallback.onRequestProvision(request.getDefaultUrl(), request.getData()); + } + return null; + } + + @Override + public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request) throws Exception { + if (mDrmCallback != null) { + return mDrmCallback.onRequestKey(request.getLicenseServerUrl(), request.getData()); + } + return null; + } +} diff --git a/platform/Android/source/build.gradle b/platform/Android/source/build.gradle index f786cd804..55b1ec638 100644 --- a/platform/Android/source/build.gradle +++ b/platform/Android/source/build.gradle @@ -9,7 +9,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.0' + classpath 'com.android.tools.build:gradle:4.0.0' // NOTE: Do not place your application dependencies here; they belong diff --git a/platform/Android/source/gradle/wrapper/gradle-wrapper.properties b/platform/Android/source/gradle/wrapper/gradle-wrapper.properties index 14a89a742..bc07ac192 100644 --- a/platform/Android/source/gradle/wrapper/gradle-wrapper.properties +++ b/platform/Android/source/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Sep 24 17:49:42 CST 2019 +#Wed Aug 05 11:32:59 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/platform/Android/source/paasApp/build.gradle b/platform/Android/source/paasApp/build.gradle index 7e774ddcc..a650046ac 100644 --- a/platform/Android/source/paasApp/build.gradle +++ b/platform/Android/source/paasApp/build.gradle @@ -4,7 +4,7 @@ android { compileSdkVersion 27 defaultConfig { applicationId "com.cicada.player.demo" - minSdkVersion 14 + minSdkVersion 18 targetSdkVersion 27 versionCode 1 versionName "1.0" @@ -79,6 +79,7 @@ dependencies { implementation project(':premierlibrary') implementation project(':zxing') + implementation project(':ExternalPlayerExoLibrary') //ExoPlayer implementation 'com.google.android.exoplayer:exoplayer:2.9.6' diff --git a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/CicadaPlayerActivity.java b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/CicadaPlayerActivity.java index 8ca8ba81c..504d5a6be 100644 --- a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/CicadaPlayerActivity.java +++ b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/CicadaPlayerActivity.java @@ -544,6 +544,11 @@ private void updatePlayerViewMode() { @Override protected void onDestroy() { + releasePlayer(); + super.onDestroy(); + } + + private void releasePlayer() { if (mCicadaVodPlayerView != null) { mCicadaVodPlayerView.onDestroy(); mCicadaVodPlayerView = null; @@ -553,8 +558,6 @@ protected void onDestroy() { toFragment = null; mCurrentFragment = null; mSubtitleMap.clear(); - - super.onDestroy(); } @Override @@ -564,6 +567,7 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { //handler == false 不要处理 if (handler) { if(keyCode == KeyEvent.KEYCODE_BACK){ + releasePlayer(); finish(); } } diff --git a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/PlayerApplication.java b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/PlayerApplication.java index 4496706a4..c42ea0ef1 100644 --- a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/PlayerApplication.java +++ b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/PlayerApplication.java @@ -2,6 +2,8 @@ import android.support.multidex.MultiDexApplication; +import com.aliyun.externalplayer.exo.ExternPlayerExo; +import com.cicada.player.CicadaExternalPlayer; import com.cicada.player.demo.util.SharedPreferenceUtils; @@ -11,5 +13,7 @@ public class PlayerApplication extends MultiDexApplication{ public void onCreate() { super.onCreate(); SharedPreferenceUtils.init(getApplicationContext()); + + CicadaExternalPlayer.registerExternalPlayer(new ExternPlayerExo()); } } diff --git a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/SettingActivity.java b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/SettingActivity.java index e82907aa1..a97a6e46d 100644 --- a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/SettingActivity.java +++ b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/SettingActivity.java @@ -7,6 +7,7 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.RadioGroup; import android.widget.TextView; import android.widget.Toast; @@ -35,6 +36,7 @@ public class SettingActivity extends BaseActivity { * phone Model info */ private TextView mModelTextView; + private RadioGroup mPlayerSelectRadioGroup; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -46,7 +48,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { initListener(); } - private void initView(){ + private void initView() { TextView tvTitle = findViewById(R.id.tv_title); tvTitle.setText(R.string.title_setting); @@ -54,23 +56,35 @@ private void initView(){ mBlackListButton = findViewById(R.id.blacklit); mVersionTextView = findViewById(R.id.tv_version); mHardwareDecoderCheckBox = findViewById(R.id.hardwaredecoder); + mPlayerSelectRadioGroup = findViewById(R.id.radio_group_player); } - private void initData(){ + private void initData() { mVersionTextView.setText(CicadaPlayerFactory.getSdkVersion()); mHardwareDecoderCheckBox.setChecked(SharedPreferenceUtils.getBooleanExtra(SharedPreferenceUtils.CICADA_PLAYER_HARDWARE_DECODER)); + String playerName = SharedPreferenceUtils.getStringExtra(SharedPreferenceUtils.SELECTED_PLAYER_NAME); + if ("CicadaPlayer".equals(playerName)) { + mPlayerSelectRadioGroup.check(R.id.radio_btn_cicada); + } else if ("ExoPlayer".equals(playerName)) { + mPlayerSelectRadioGroup.check(R.id.radio_btn_exo); + } else if ("MediaPlayer".equals(playerName)) { + mPlayerSelectRadioGroup.check(R.id.radio_btn_media); + } else { + boolean selectedCicadaPlayer = SharedPreferenceUtils.getBooleanExtra(SharedPreferenceUtils.SELECTED_CICADA_PLAYER); + mPlayerSelectRadioGroup.check(selectedCicadaPlayer ? R.id.radio_btn_cicada : R.id.radio_btn_exo); + } mModelTextView.setText(Build.MODEL); } - private void initListener(){ + private void initListener() { mHardwareDecoderCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - if(Common.HAS_ADD_BLACKLIST && b){ + if (Common.HAS_ADD_BLACKLIST && b) { Toast.makeText(SettingActivity.this, getString(R.string.cicada_unable_start_hardware_decoder), Toast.LENGTH_SHORT).show(); mHardwareDecoderCheckBox.setChecked(false); - }else{ - SharedPreferenceUtils.putBooleanExtra(SharedPreferenceUtils.CICADA_PLAYER_HARDWARE_DECODER,b); + } else { + SharedPreferenceUtils.putBooleanExtra(SharedPreferenceUtils.CICADA_PLAYER_HARDWARE_DECODER, b); } } @@ -79,14 +93,27 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean b) { mBlackListButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if(mHardwareDecoderCheckBox != null){ + if (mHardwareDecoderCheckBox != null) { mHardwareDecoderCheckBox.setChecked(false); } Toast.makeText(SettingActivity.this, getString(R.string.cicada_success), Toast.LENGTH_SHORT).show(); Common.HAS_ADD_BLACKLIST = true; CicadaPlayerFactory.DeviceInfo deviceInfo = new CicadaPlayerFactory.DeviceInfo(); deviceInfo.model = Build.MODEL; - CicadaPlayerFactory.addBlackDevice(CicadaPlayerFactory.BlackType.HW_Decode_H264,deviceInfo); + CicadaPlayerFactory.addBlackDevice(CicadaPlayerFactory.BlackType.HW_Decode_H264, deviceInfo); + } + }); + + mPlayerSelectRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup radioGroup, int checkedId) { + if (checkedId == R.id.radio_btn_cicada) { + SharedPreferenceUtils.putStringExtra(SharedPreferenceUtils.SELECTED_PLAYER_NAME, "CicadaPlayer"); + } else if (checkedId == R.id.radio_btn_exo) { + SharedPreferenceUtils.putStringExtra(SharedPreferenceUtils.SELECTED_PLAYER_NAME, "ExoPlayer"); + } else if (checkedId == R.id.radio_btn_media) { + SharedPreferenceUtils.putStringExtra(SharedPreferenceUtils.SELECTED_PLAYER_NAME, "MediaPlayer"); + } } }); } diff --git a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/HttpClientHelper.java b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/HttpClientHelper.java new file mode 100644 index 000000000..43704a440 --- /dev/null +++ b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/HttpClientHelper.java @@ -0,0 +1,71 @@ +package com.cicada.player.demo.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +public class HttpClientHelper { + private static final int CONNECTION_TIMEOUT = 10000; + + public static byte[] post(String requestUrl, byte[] body) { + URLConnection urlConnection = null; + InputStream in = null; + ByteArrayOutputStream outputStream = null; + try { + URL url = new URL(requestUrl); + urlConnection = url.openConnection(); + HttpURLConnection connection = (HttpURLConnection) urlConnection; + connection.setRequestMethod("POST"); + connection.setDoOutput(body != null); + connection.setDoInput(true); + connection.setConnectTimeout(CONNECTION_TIMEOUT); + connection.setReadTimeout(CONNECTION_TIMEOUT); + if (body != null) { + connection.setFixedLengthStreamingMode(body.length); + connection.connect(); + OutputStream os = connection.getOutputStream(); + os.write(body); + os.close(); + } else { + connection.connect(); + } + + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + in = connection.getInputStream(); + byte[] buffer = new byte[1024 * 4]; + outputStream = new ByteArrayOutputStream(); + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + byte[] bytes = outputStream.toByteArray(); + return bytes; + } + + } catch (Exception e) { +// e.printStackTrace(); + } finally { + try { + if (in != null) { + in.close(); + } + + if (outputStream != null) { + outputStream.close(); + } + + } catch (IOException e) { + + } + if (urlConnection != null) { + ((HttpURLConnection) urlConnection).disconnect(); + } + } + return null; + } + +} diff --git a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/ScreenStatusController.java b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/ScreenStatusController.java index 2943263ef..33bcc9b07 100644 --- a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/ScreenStatusController.java +++ b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/ScreenStatusController.java @@ -16,6 +16,7 @@ public class ScreenStatusController { private String TAG = ScreenStatusController.class.getSimpleName(); + private boolean mReceiverTag = false; private Context mContext; private IntentFilter mScreenStatusFilter = null; private ScreenStatusListener mScreenStatusListener = null; @@ -61,14 +62,16 @@ public void setScreenStatusListener(ScreenStatusListener l) { //开始监听 public void startListen() { - if (mContext != null) { + if (mContext != null && !mReceiverTag) { + mReceiverTag = true; mContext.registerReceiver(mScreenStatusReceiver, mScreenStatusFilter); } } //结束监听 public void stopListen() { - if (mContext != null) { + if (mContext != null && mReceiverTag) { + mReceiverTag = false; mContext.unregisterReceiver(mScreenStatusReceiver); } } diff --git a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/SharedPreferenceUtils.java b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/SharedPreferenceUtils.java index dc6f971d4..1f45adfdf 100644 --- a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/SharedPreferenceUtils.java +++ b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/util/SharedPreferenceUtils.java @@ -6,6 +6,8 @@ public class SharedPreferenceUtils { public static final String CICADA_PLAYER_HARDWARE_DECODER = "cicada_player_hardware_decoder"; + public static final String SELECTED_CICADA_PLAYER = "selected_cicada_player"; + public static final String SELECTED_PLAYER_NAME = "selected_player_type"; private static SharedPreferences mInstance = null; @@ -42,4 +44,20 @@ public static boolean getBooleanExtra(String key){ } return mInstance.getBoolean(key, true); } + + public static void putStringExtra(String key,String value){ + if(mInstance == null){ + return ; + } + SharedPreferences.Editor edit = mInstance.edit(); + edit.putString(key,value); + edit.commit(); + } + + public static String getStringExtra(String key){ + if(mInstance == null){ + return ""; + } + return mInstance.getString(key, ""); + } } diff --git a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/view/CicadaVodPlayerView.java b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/view/CicadaVodPlayerView.java index 01701b280..d887e3d31 100644 --- a/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/view/CicadaVodPlayerView.java +++ b/platform/Android/source/paasApp/src/main/java/com/cicada/player/demo/view/CicadaVodPlayerView.java @@ -8,13 +8,14 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; +import android.view.Gravity; import android.view.KeyEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.RelativeLayout; import android.widget.Toast; import com.cicada.player.CicadaPlayer; @@ -28,9 +29,12 @@ import com.cicada.player.demo.listener.ViewAction; import com.cicada.player.demo.util.BrightnessUtil; import com.cicada.player.demo.util.DensityUtils; +import com.cicada.player.demo.util.HttpClientHelper; +import com.cicada.player.demo.util.NetWatchdog; import com.cicada.player.demo.util.OrientationWatchDog; import com.cicada.player.demo.util.ScreenUtils; import com.cicada.player.demo.util.SharedPreferenceUtils; +import com.cicada.player.demo.util.VcPlayerLog; import com.cicada.player.demo.view.control.ControlView; import com.cicada.player.demo.view.gesture.GestureView; import com.cicada.player.demo.view.guide.GuideView; @@ -42,12 +46,15 @@ import com.cicada.player.nativeclass.MediaInfo; import com.cicada.player.nativeclass.PlayerConfig; import com.cicada.player.nativeclass.TrackInfo; -import com.cicada.player.demo.util.NetWatchdog; -import com.cicada.player.demo.util.VcPlayerLog; import com.cicada.player.utils.Logger; +import com.cicada.player.utils.media.DrmCallback; + +import org.json.JSONException; +import org.json.JSONObject; import java.io.File; import java.lang.ref.WeakReference; +import java.nio.charset.Charset; import static com.cicada.player.demo.view.subtitle.LocationStyle.Location_CenterH; import static com.cicada.player.demo.view.subtitle.LocationStyle.Location_Top; @@ -66,14 +73,14 @@ * view 的初始化是在{@link #initVideoView}方法中实现的。 * 然后是对各个view添加监听方法,处理对应的操作,从而实现与播放器的共同操作 */ -public class CicadaVodPlayerView extends RelativeLayout { +public class CicadaVodPlayerView extends FrameLayout { private static final String TAG = CicadaVodPlayerView.class.getSimpleName(); /** * 视频画面 */ - private SurfaceView mSurfaceView; + private SurfaceView mTextureView; /** * 手势操作view */ @@ -218,8 +225,8 @@ public CicadaVodPlayerView(Context context, AttributeSet attrs, int defStyleAttr * 初始化view */ private void initVideoView() { - //初始化播放用的surfaceView - initSurfaceView(); + //初始化播放用的TextureView + initTextureView(); //初始化播放器 initCicadaPlayer(); //初始化封面 @@ -1047,43 +1054,39 @@ public void onDoubleTap() { /** * 初始化播放器显示view */ - private void initSurfaceView() { - mSurfaceView = new SurfaceView(getContext().getApplicationContext()); - addSubView(mSurfaceView); - - SurfaceHolder holder = mSurfaceView.getHolder(); - //增加surfaceView的监听 - holder.addCallback(new SurfaceHolder.Callback() { + private void initTextureView() { + boolean selectedCicadaPlayer = SharedPreferenceUtils.getBooleanExtra(SharedPreferenceUtils.SELECTED_CICADA_PLAYER); + mTextureView = new SurfaceView(getContext().getApplicationContext()); + addTextureView(mTextureView); + mTextureView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { - VcPlayerLog.d(TAG, " surfaceCreated = surfaceHolder = " + surfaceHolder); if (mCicadaVodPlayer != null) { - mCicadaVodPlayer.setDisplay(surfaceHolder); +// ExternExoSurface surface = new ExternExoSurface(surfaceTexture); +// if(!selectedCicadaPlayer){ +// surface.setTextureView(mTextureView); +// } + mCicadaVodPlayer.setSurface(surfaceHolder.getSurface()); + //防止黑屏 mCicadaVodPlayer.redraw(); } } @Override - public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, - int height) { - VcPlayerLog.d(TAG, " surfaceChanged surfaceHolder = " + surfaceHolder + " , width = " + width + " , height = " + height); - if (mCicadaVodPlayer != null) { - mCicadaVodPlayer.redraw(); - } + public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { + } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { - VcPlayerLog.d(TAG, " surfaceDestroyed = surfaceHolder = " + surfaceHolder); if (mCicadaVodPlayer != null) { - mCicadaVodPlayer.setDisplay(null); + mCicadaVodPlayer.setSurface(null); } } }); } - private boolean isPlaying() { return currentPlayState == CicadaPlayer.started || currentPlayState == CicadaPlayer.paused; @@ -1095,8 +1098,42 @@ private boolean isPlaying() { * 初始化播放器 */ private void initCicadaPlayer() { - Logger.enableConsoleLog(true); - mCicadaVodPlayer = CicadaPlayerFactory.createCicadaPlayer(getContext().getApplicationContext()); + Logger.getInstance(getContext()).enableConsoleLog(true); + Logger.getInstance(getContext()).setLogLevel(Logger.LogLevel.AF_LOG_LEVEL_DEBUG); + + String playerName = SharedPreferenceUtils.getStringExtra(SharedPreferenceUtils.SELECTED_PLAYER_NAME); + if (TextUtils.isEmpty(playerName)) { + boolean selectedCicadaPlayer = SharedPreferenceUtils.getBooleanExtra(SharedPreferenceUtils.SELECTED_CICADA_PLAYER); + if (selectedCicadaPlayer) { + mCicadaVodPlayer = CicadaPlayerFactory.createCicadaPlayer(getContext().getApplicationContext()); + } else { + mCicadaVodPlayer = CicadaPlayerFactory.createCicadaPlayer(getContext().getApplicationContext(), "ExoPlayer"); + } + } else { + mCicadaVodPlayer = CicadaPlayerFactory.createCicadaPlayer(getContext().getApplicationContext(), playerName); + } + + //设置drm的callback + mCicadaVodPlayer.setDrmCallback(new DrmCallback() { + @Override + public byte[] requestProvision(String defaultUrl, byte[] data) { + String requestUrl = defaultUrl + "&signedRequest=" + new String(data, Charset.forName("UTF-8")); + byte[] provisionData = HttpClientHelper.post(requestUrl, null); + return provisionData; + } + + @Override + public byte[] requestKey(String defaultUrl, byte[] data) { + if (TextUtils.isEmpty(defaultUrl)) { + defaultUrl = "https://proxy.uat.widevine.com/proxy?provider=widevine_test"; + } + + byte[] requestData = HttpClientHelper.post(defaultUrl, data); + Log.d(TAG , "requestKey data = " + requestData); + return requestData; + } + }); + mCicadaVodPlayer.enableHardwareDecoder(SharedPreferenceUtils.getBooleanExtra(SharedPreferenceUtils.CICADA_PLAYER_HARDWARE_DECODER)); //设置准备回调 @@ -1237,6 +1274,21 @@ public void onInfo(InfoBean infoBean) { mRetryTime = 3; Log.e(TAG, "NetworkRetrySuccess"); Toast.makeText(getContext(), R.string.cicada_tip_network_connect_success, Toast.LENGTH_SHORT).show(); + } else if (infoBean.getCode() == InfoCode.DirectComponentMSG) { + String msg = infoBean.getExtraMsg(); + try { + JSONObject msgJson = new JSONObject(msg); + if (msgJson.has("content")) { + if ("hello".equals(msgJson.getString("content"))) { + msgJson.remove("content"); + msgJson.put("content", "hi"); + msgJson.put("cmd", 0); + mCicadaVodPlayer.invokeComponent(msgJson.toString()); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } } if (mOutInfoListener != null) { mOutInfoListener.onInfo(infoBean); @@ -1251,13 +1303,9 @@ public void onRenderingStart() { inSeek = false; boolean changed = generateMediaInfo(); -// if (changed && mAliyunMediaInfo != null) { -// TrackInfo trackInfo = mCicadaVodPlayer.currentTrack(TrackInfo.Type.TYPE_VOD.ordinal()); -// if (trackInfo != null) { -// mControlView.setMediaInfo(mAliyunMediaInfo, trackInfo.getVodDefinition()); -// } -// -// } + if (changed && mAliyunMediaInfo != null) { + mControlView.setMediaInfo(mAliyunMediaInfo, "OD"); + } mCoverView.setVisibility(GONE); mTipsView.hideAll(); updateViewState(ControlView.PlayState.Playing); @@ -1355,7 +1403,6 @@ public void onSnapShot(Bitmap bitmap, int i, int i1) { }); mCicadaVodPlayer.setScaleMode(CicadaPlayer.ScaleMode.SCALE_ASPECT_FIT); - mCicadaVodPlayer.setDisplay(mSurfaceView.getHolder()); } private int currentPlayState = CicadaPlayer.idle; @@ -1517,12 +1564,25 @@ private void addSubView(View view) { addView(view, params); } + /** + * addSubView + * 添加子view到布局中 + * + * @param view 子view + */ + private void addTextureView(View view) { + LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + params.gravity = Gravity.CENTER; + //添加到布局中 + addView(view, params); + } + /** * 添加子View到布局中央 */ private void addSubViewByCenter(View view) { LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - params.addRule(RelativeLayout.CENTER_IN_PARENT); + params.gravity = Gravity.CENTER; addView(view, params); } @@ -1822,7 +1882,7 @@ public void onDestroy() { mCicadaVodPlayer.setSurface(null); mCicadaVodPlayer.release(); } - mSurfaceView = null; + mTextureView = null; if (mNetWatchdog != null) { mNetWatchdog.stopWatch(); @@ -1928,7 +1988,7 @@ public void setControlBarCanShow(boolean show) { * 开启底层日志 */ public void enableNativeLog() { - Logger.enableConsoleLog(true); + Logger.getInstance(getContext()).enableConsoleLog(true); } @@ -1936,7 +1996,7 @@ public void enableNativeLog() { * 关闭底层日志 */ public void disableNativeLog() { - Logger.enableConsoleLog(false); + Logger.getInstance(getContext()).enableConsoleLog(false); } /** @@ -1953,9 +2013,9 @@ public String getSDKVersion() { * * @return 播放surfaceView */ - public SurfaceView getPlayerView() { - return mSurfaceView; - } +// public TextureView getPlayerView() { +// return mTextureView; +// } /** * 设置锁定竖屏监听 diff --git a/platform/Android/source/paasApp/src/main/res/layout/activity_setting.xml b/platform/Android/source/paasApp/src/main/res/layout/activity_setting.xml index be7c8eccd..69e810a2f 100644 --- a/platform/Android/source/paasApp/src/main/res/layout/activity_setting.xml +++ b/platform/Android/source/paasApp/src/main/res/layout/activity_setting.xml @@ -53,6 +53,52 @@ android:text="@string/hardware_decoder" /> + + + + + + + + + + + + + + + + - + + + + @@ -104,7 +122,7 @@ - + @@ -238,6 +256,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.h b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.h index e2069a4ef..5eb50deae 100644 --- a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.h +++ b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.h @@ -25,6 +25,15 @@ */ @property (nonatomic,strong)NSDictionary * subtitleDictionary; +/** +fairPlay标识,如果使用了FairPlay,avResourceLoaderDelegate也要同步设置 +*/ +@property (nonatomic, assign) BOOL useFairPlay; +/** +ResourceLoaderDelegate +*/ +@property (nonatomic,strong) NSObject* avResourceLoaderDelegate; + @end diff --git a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.m b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.m index ed8423a25..24311e3eb 100644 --- a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.m +++ b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/CicadaPlayerViewController.m @@ -104,7 +104,13 @@ - (void)viewDidLoad { [self.view addSubview:self.settingAndConfigView]; [CicadaPlayer setAudioSessionDelegate:self]; - self.player = [[CicadaPlayer alloc] init]; + if (self.useFairPlay) { + self.player = [[CicadaPlayer alloc] init:nil opt:@{@"name":@"AppleAVPlayer"}]; + [self.player performSelector:@selector(setAVResourceLoaderDelegate:) withObject:self.avResourceLoaderDelegate]; + } else { + self.player = [[CicadaPlayer alloc] init]; + } + self.player.enableHardwareDecoder = [CicadaTool isHardware]; self.player.playerView = self.CicadaView.playerView; self.player.delegate = self; @@ -114,12 +120,20 @@ - (void)viewDidLoad { [self.settingAndConfigView setVolume:self.player.volume/2]; [self setConfig]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChangeFunc) name:UIDeviceOrientationDidChangeNotification object:nil]; // 添加检测app进入后台的观察者 - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationEnterBackground) name: UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationEnterBackground) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; // app从后台进入前台都会调用这个方法 - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name: UIApplicationDidBecomeActiveNotification object:nil]; - + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationDidBecomeActive) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + WEAK_SELF [[AFNetworkReachabilityManager sharedManager] startMonitoring]; [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { @@ -612,6 +626,17 @@ -(void)onPlayerEvent:(CicadaPlayer*)player eventWithString:(CicadaEventWithStrin } }else if (eventWithString == CICADA_EVENT_PLAYER_NETWORK_RETRY_SUCCESS) { self.retryCount = 3; + } else if (eventWithString == CICADA_EVENT_PLAYER_DIRECT_COMPONENT_MSG) { + NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:[description dataUsingEncoding:NSUTF8StringEncoding] + options:0 + error:nil]; + NSString *value = dic[@"content"]; + if ([value isEqualToString:@"hello"]) { + NSMutableDictionary *mutableDic = [dic mutableCopy]; + mutableDic[@"content"] = @"hi"; + NSData *data = [NSJSONSerialization dataWithJSONObject:mutableDic options:0 error:nil]; + [self.player invokeComponent:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]; + } } [CicadaTool hudWithText:description view:self.view]; } diff --git a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/FairplayInputViewController.h b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/FairplayInputViewController.h new file mode 100644 index 000000000..f2b903acb --- /dev/null +++ b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/FairplayInputViewController.h @@ -0,0 +1,17 @@ +// +// FairplayInputViewController.h +// CicadaDemo +// +// Created by zhou on 2020/7/30. +// Copyright © 2020 com.alibaba. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FairplayInputViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/FairplayInputViewController.m b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/FairplayInputViewController.m new file mode 100644 index 000000000..bb34c63a3 --- /dev/null +++ b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/FairplayInputViewController.m @@ -0,0 +1,67 @@ +// +// FairplayInputViewController.m +// CicadaDemo +// +// Created by zhou on 2020/7/30. +// Copyright © 2020 com.alibaba. All rights reserved. +// + +#import "FairplayInputViewController.h" +#import "CicadaPlayerViewController.h" +#import "CicadaScanViewController.h" +#import "AssetLoaderDelegate.h" + +@interface FairplayInputViewController () + +@property (nonatomic, strong) IBOutlet UITextField *mediaUrlTextField; +@property (nonatomic, strong) IBOutlet UITextField *certificateUrlTextField; +@property (nonatomic, strong) IBOutlet UITextField *licenseUrlTextField; +@property (nonatomic, assign) BOOL isClickedFlag; + +@end + +@implementation FairplayInputViewController + +- (void)viewDidLoad { + [super viewDidLoad]; +} + +/** + 进入URL播放 + @param sender 调用者 + */ +- (IBAction)gotoURLPlay:(id)sender { + if (self.isClickedFlag == NO) { + self.isClickedFlag = YES; + NSString *urlStr = self.mediaUrlTextField.text; + CicadaUrlSource *source = [[CicadaUrlSource alloc] urlWithString:urlStr]; + CicadaPlayerViewController *vc = [[CicadaPlayerViewController alloc]init]; + vc.urlSource = source; + NSString *certificateUrl = self.certificateUrlTextField.text; + NSString *licenseUrl = self.licenseUrlTextField.text; + AssetLoaderDelegate *delegate = [[AssetLoaderDelegate alloc] initWithCertificateUrl:certificateUrl licenseUrl:licenseUrl]; + vc.useFairPlay = YES; + vc.avResourceLoaderDelegate = delegate; + [self.navigationController pushViewController:vc animated:YES]; + } +} + +- (IBAction)pushToScan:(id)sender { + if (self.isClickedFlag == NO) { + self.isClickedFlag = YES; + CicadaScanViewController *vc = [[CicadaScanViewController alloc]init]; + vc.scanTextCallBack = ^(NSString *text) { + self.mediaUrlTextField.text = text; + }; + [self.navigationController pushViewController:vc animated:YES]; + } +} + +#pragma mark textDelegate + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [textField resignFirstResponder]; + return YES; +} + +@end diff --git a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/SourceInputViewController.m b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/SourceInputViewController.m index 891959282..85e0e5e78 100644 --- a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/SourceInputViewController.m +++ b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/controller/SourceInputViewController.m @@ -21,8 +21,14 @@ @implementation SourceInputViewController - (void)viewDidLoad { [super viewDidLoad]; - //开发测试用 + // 开发测试用 self.URLTextField.text = @"http://player.alicdn.com/video/aliyunmedia.mp4"; + +// // 苹果官方测试视频 +// self.URLTextField.text = @"https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"; +// +// // 有多视频轨,没有音频轨、字幕轨 +// self.URLTextField.text = @"https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"; } /** diff --git a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/other/AssetLoaderDelegate.h b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/other/AssetLoaderDelegate.h new file mode 100644 index 000000000..ba0f672b5 --- /dev/null +++ b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/other/AssetLoaderDelegate.h @@ -0,0 +1,18 @@ +// +// AssetLoaderDelegate.h +// iOS FPS Client AxinomDRM +// +// Created by Axinom. +// Copyright (c) Axinom. All rights reserved. +// + +#import + +@interface AssetLoaderDelegate : NSObject + +@property (nonatomic, strong) NSString *certificateUrl; +@property (nonatomic, strong) NSString *licenseUrl; + +- (instancetype)initWithCertificateUrl:(NSString *)certificateUrl licenseUrl:(NSString *)licenseUrl; + +@end diff --git a/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/other/AssetLoaderDelegate.m b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/other/AssetLoaderDelegate.m new file mode 100644 index 000000000..b215aebcb --- /dev/null +++ b/platform/Apple/demo/iOS/CicadaDemo/CicadaDemo/CicadaCode/other/AssetLoaderDelegate.m @@ -0,0 +1,100 @@ +// +// AssetLoaderDelegate.m +// iOS FPS Client AxinomDRM +// +// Created by Axinom. +// Copyright (c) Axinom. All rights reserved. +// + +#import +#import "AssetLoaderDelegate.h" + +typedef void(^AppCertificateRequestCompletion)(NSData *certificate); +typedef void(^ContentKeyAndLeaseExpiryRequestCompletion)(NSData *response, NSError *error); + +@interface AssetLoaderDelegate () + +@property (nonatomic, strong) NSData *certificateData; + +@end + +@implementation AssetLoaderDelegate + +- (instancetype)initWithCertificateUrl:(NSString *)certificateUrl licenseUrl:(NSString *)licenseUrl { + self = [super init]; + if (self) { + self.certificateUrl = certificateUrl; + self.licenseUrl = licenseUrl; + [self requestApplicationCertificateWithCompletion:^(NSData *certificate) { + self.certificateData = certificate; + }]; + } + return self; +} + +- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForRenewalOfRequestedResource:(AVAssetResourceRenewalRequest *)renewalRequest { + return [self resourceLoader:resourceLoader shouldWaitForLoadingOfRequestedResource:renewalRequest]; +} + +- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest { + NSURL *url = loadingRequest.request.URL; + if (![[url scheme] isEqual:@"skd"]) { + return NO; + } + AVAssetResourceLoadingDataRequest *dataRequest = loadingRequest.dataRequest; + NSString *assetStr = [url.absoluteString stringByReplacingOccurrencesOfString:@"skd://" withString:@""]; + NSData *assetId = [NSData dataWithBytes: [assetStr cStringUsingEncoding:NSUTF8StringEncoding] length:[assetStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]]; + NSError *error = nil; + NSData *requestBytes = [loadingRequest streamingContentKeyRequestDataForApp:self.certificateData contentIdentifier:assetId options:nil error:&error]; + [self requestContentKeyAndLeaseExpiryfromKeyServerModuleWithRequestBytes:requestBytes + assetId:assetStr + completion:^(NSData *response, NSError *error) { + if (response) { + [dataRequest respondWithData:response]; + if (@available(iOS 9.0, *)) { + loadingRequest.contentInformationRequest.contentType = AVStreamingKeyDeliveryContentKeyType; + } else { + + } + [loadingRequest finishLoading]; + } + else { + [loadingRequest finishLoadingWithError:error]; + } + }]; + return YES; +} + +- (void)requestApplicationCertificateWithCompletion:(AppCertificateRequestCompletion)completion { + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; + NSURL *url = [NSURL URLWithString:self.certificateUrl]; + NSURLSessionDataTask *requestTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + completion(data); + }]; + [requestTask resume]; +} + +- (void)requestContentKeyAndLeaseExpiryfromKeyServerModuleWithRequestBytes:(NSData *)requestBytes assetId:(NSString *)assetId completion:(ContentKeyAndLeaseExpiryRequestCompletion)completion { + NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration]; + NSURL *url = [NSURL URLWithString:self.licenseUrl]; + NSMutableURLRequest *ksmRequest = [NSMutableURLRequest requestWithURL:url]; + [ksmRequest setHTTPMethod:@"POST"]; + + NSString *spc = [requestBytes base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; + NSString *postStr = [NSString stringWithFormat:@"spc=%@&assetId=%@", spc, assetId]; + NSData *postData = [postStr dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; + [ksmRequest setHTTPBody:postData]; + + NSURLSessionDataTask *requestTask = [session dataTaskWithRequest:ksmRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + NSString *responseStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + responseStr = [responseStr stringByReplacingOccurrencesOfString:@"" withString:@""]; + responseStr = [responseStr stringByReplacingOccurrencesOfString:@"" withString:@""]; + NSData* decodeData = [[NSData alloc] initWithBase64EncodedString:responseStr options:0]; + completion(decodeData, error); + }]; + [requestTask resume]; +} + +@end diff --git a/platform/Apple/source/CMakeLists.txt b/platform/Apple/source/CMakeLists.txt index 187a3134b..e233fd084 100644 --- a/platform/Apple/source/CMakeLists.txt +++ b/platform/Apple/source/CMakeLists.txt @@ -133,6 +133,7 @@ set_target_properties(CicadaPlayerSDK PROPERTIES XCODE_ATTRIBUTE_LD_DYLIB_INSTALL_NAME "@rpath/CicadaPlayerSDK.framework/CicadaPlayerSDK" # MACOSX_FRAMEWORK_INFO_PLIST Info.plist #XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer" + XCODE_ATTRIBUTE_EXCLUDED_ARCHS[sdk=iphonesimulator*] arm64 XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=MinSizeRel] YES XCODE_ATTRIBUTE_ENABLE_BITCODE ${BITCODE_VALUE} XCODE_ATTRIBUTE_DEFINES_MODULE "YES" diff --git a/platform/Apple/source/CicadaConfig.h b/platform/Apple/source/CicadaConfig.h index bdfde7913..2ccb6be40 100644 --- a/platform/Apple/source/CicadaConfig.h +++ b/platform/Apple/source/CicadaConfig.h @@ -104,6 +104,30 @@ OBJC_EXPORT @property(nonatomic, assign) int liveStartIndex; +/** +@brief 进度跟新的频率。包括当前位置和缓冲位置。 + */ +/**** +@brief Set the frequencies of Progress. Includes the current position and the buffer position. + */ +@property(nonatomic, assign) int positionTimerIntervalMs; + +/** + @brief 禁用Audio. + */ +/**** + @brief Disable audio track. + */ +@property (nonatomic, assign) BOOL disableAudio; + +/** + @brief 禁用Video + */ +/**** + @brief Disable video track. + */ +@property (nonatomic, assign) BOOL disableVideo; + @end #endif /* CicadaConfig_h */ diff --git a/platform/Apple/source/CicadaConfig.mm b/platform/Apple/source/CicadaConfig.mm index 96632f849..2071b5299 100644 --- a/platform/Apple/source/CicadaConfig.mm +++ b/platform/Apple/source/CicadaConfig.mm @@ -13,6 +13,7 @@ @implementation CicadaConfig @synthesize maxDelayTime; @synthesize highBufferDuration; @synthesize startBufferDuration; +@synthesize positionTimerIntervalMs; @synthesize maxBufferDuration; @synthesize networkTimeout; @synthesize referer; @@ -22,6 +23,8 @@ @implementation CicadaConfig @synthesize networkRetryCount; @synthesize httpHeaders; @synthesize liveStartIndex; +@synthesize disableAudio; +@synthesize disableVideo; - (instancetype)init{ if (self = [super init]) { @@ -37,6 +40,9 @@ - (instancetype)init{ networkRetryCount = 2; httpHeaders = [[NSMutableArray alloc] init]; liveStartIndex = -3; + disableAudio = NO; + disableVideo = NO; + positionTimerIntervalMs = 500; } return self; } diff --git a/platform/Apple/source/CicadaDelegate.h b/platform/Apple/source/CicadaDelegate.h index 2124f9bec..b9fa9f298 100644 --- a/platform/Apple/source/CicadaDelegate.h +++ b/platform/Apple/source/CicadaDelegate.h @@ -114,6 +114,19 @@ */ - (void)onLoadingProgress:(CicadaPlayer*)player progress:(float)progress; + +/** + @brief 下载速度回调 + @param player 播放器player指针 + @param speed 当前下载速度单位 bps + */ +/**** + @brief current download speed callback. + @param player Player pointer. + @param speed bps + */ +- (void)onCurrentDownLoadSpeed:(CicadaPlayer *)player progress:(float)speed; + /** @brief 获取track信息回调 @param player 播放器player指针 diff --git a/platform/Apple/source/CicadaMediaInfo.h b/platform/Apple/source/CicadaMediaInfo.h index adf41394b..ad7f5a037 100644 --- a/platform/Apple/source/CicadaMediaInfo.h +++ b/platform/Apple/source/CicadaMediaInfo.h @@ -26,6 +26,11 @@ typedef enum CicadaTrackType: NSUInteger { CICADA_TRACK_SUBTITLE, } CicadaTrackType; +typedef enum CicadaVideoHDRType : NSUInteger { + CICADA_VideoHDRType_SDR, + CICADA_VideoHDRType_HDR10, +} CicadaVideoHDRType; + OBJC_EXPORT @interface CicadaTrackInfo : NSObject @@ -34,6 +39,8 @@ OBJC_EXPORT */ @property (nonatomic, assign) CicadaTrackType trackType; +@property(nonatomic, assign) CicadaVideoHDRType HDRType; + /** @brief vod format */ diff --git a/platform/Apple/source/CicadaOCHelper.h b/platform/Apple/source/CicadaOCHelper.h index 5d01617de..f11e760dd 100644 --- a/platform/Apple/source/CicadaOCHelper.h +++ b/platform/Apple/source/CicadaOCHelper.h @@ -47,6 +47,8 @@ class CicadaOCHelper { static void onLoadingProgress(int64_t percent, void *userData); + static void onCurrentDownLoadSpeed(int64_t speed, void *userData); + static void onLoadingEnd(void *userData); static void onSeekEnd(int64_t seekInCache, void *userData); diff --git a/platform/Apple/source/CicadaOCHelper.mm b/platform/Apple/source/CicadaOCHelper.mm index 74bc483a9..eb1dce8f7 100644 --- a/platform/Apple/source/CicadaOCHelper.mm +++ b/platform/Apple/source/CicadaOCHelper.mm @@ -95,21 +95,31 @@ trackInfo.trackType = static_cast(info->type); trackInfo.trackIndex = info->streamIndex; - trackInfo.videoWidth = info->videoWidth; - trackInfo.videoHeight = info->videoHeight; - trackInfo.trackBitrate = info->videoBandwidth; - trackInfo.audioChannels = info->nChannels; - trackInfo.audioSamplerate = info->sampleRate; - trackInfo.audioSampleFormat = info->sampleFormat; - if (info->description) { trackInfo.description = [NSString stringWithUTF8String:info->description]; } - if (info->audioLang) { - trackInfo.audioLanguage = [NSString stringWithUTF8String:info->audioLang]; - } - if (info->subtitleLang) { - trackInfo.subtitleLanguage = [NSString stringWithUTF8String:info->subtitleLang]; + switch (trackInfo.trackType) { + case CICADA_TRACK_VIDEO: + trackInfo.videoWidth = info->videoWidth; + trackInfo.videoHeight = info->videoHeight; + trackInfo.trackBitrate = info->videoBandwidth; + trackInfo.HDRType = static_cast(info->HDRType); + break; + case CICADA_TRACK_AUDIO: + trackInfo.audioChannels = info->nChannels; + trackInfo.audioSamplerate = info->sampleRate; + trackInfo.audioSampleFormat = info->sampleFormat; + if (info->audioLang) { + trackInfo.audioLanguage = [NSString stringWithUTF8String:info->audioLang]; + } + break; + case CICADA_TRACK_SUBTITLE: + if (info->subtitleLang) { + trackInfo.subtitleLanguage = [NSString stringWithUTF8String:info->subtitleLang]; + } + break; + default: + break; } return trackInfo; @@ -148,6 +158,7 @@ listener.BufferPositionUpdate = onBufferPositionUpdate; listener.LoadingStart = onLoadingStart; listener.LoadingProgress = onLoadingProgress; + listener.CurrentDownLoadSpeed = onCurrentDownLoadSpeed; listener.LoadingEnd = onLoadingEnd; listener.SeekEnd = onSeekEnd; listener.StreamInfoGet = onStreamInfoGet; @@ -302,6 +313,17 @@ } } +void CicadaOCHelper::onCurrentDownLoadSpeed(int64_t speed, void *userData) +{ + __weak CicadaPlayer *player = getOCPlayer(userData); + if (player.delegate && [player.delegate respondsToSelector:@selector(onCurrentDownLoadSpeed:speed:)]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [player.delegate onCurrentDownLoadSpeed:player progress:speed]; + }); + } +} + + void CicadaOCHelper::onShowSubtitle(int64_t index, int64_t size, const void *data, void *userData) { __weak CicadaPlayer * player = getOCPlayer(userData); diff --git a/platform/Apple/source/CicadaPlayer.h b/platform/Apple/source/CicadaPlayer.h index fd6a55428..ee0424804 100644 --- a/platform/Apple/source/CicadaPlayer.h +++ b/platform/Apple/source/CicadaPlayer.h @@ -37,6 +37,13 @@ OBJC_EXPORT */ - (instancetype)init:(NSString*)traceID; +/**** + @brief Initialize the player. + @param traceID A trace ID for debugging. + @param opt user defined settings + */ +- (instancetype)init:(NSString*)traceID opt:(NSDictionary *)opt; + /** @brief 使用url方式来播放视频 @param source CicadaUrlSource的输入类型 @@ -257,6 +264,20 @@ OBJC_EXPORT */ -(void) selectExtSubtitle:(int)trackIndex enable:(BOOL)enable; + +/** + * 设置某路流相对于主时钟的延时时间,默认是0, 目前只支持外挂字幕 + * @param index 流的索引 + * @param time 延时,单位毫秒 + */ +/**** + * set the delay time of the stream + * @param index steam index + * @param time ms + */ + +- (void)setStreamDelayTime:(int)index time:(int)time; + /** @brief 重新加载。比如网络超时时,可以重新加载。 */ @@ -332,6 +353,25 @@ OBJC_EXPORT */ -(NSString *) getOption:(CicadaOption)key; +/** + @brief 向播放器的组件发送命令。 + @param content 命令内容。 + @return 命令执行结果, < 0 失败。 + */ +/**** + @brief Send command to component + @param content command content + @return < 0 on Error + */ + +- (int)invokeComponent:(NSString *)content; + +/**** +@brief 支持业务方设置resourceLoaderDelegate,可支持fairPlay +@param avResourceLoaderDelegate Log output callback block, which can be nil. +*/ +-(void)setAVResourceLoaderDelegate:(id *)avResourceLoaderDelegate; + /** @brief 获取SDK版本号信息 */ diff --git a/platform/Apple/source/CicadaPlayer.mm b/platform/Apple/source/CicadaPlayer.mm index 0102f82bc..990a37c2b 100644 --- a/platform/Apple/source/CicadaPlayer.mm +++ b/platform/Apple/source/CicadaPlayer.mm @@ -108,9 +108,22 @@ +(void) setLogCallbackInfo:(CicadaLogLevel)logLevel callbackBlock:(void (^)(Cica - (instancetype)init:(NSString*)traceID { + self = [self init:traceID opt:nil]; + if (self) { + + } + return self; +} + +- (instancetype)init:(NSString*)traceID opt:(NSDictionary *)opt { if (self = [super init]) { self.traceId = traceID; - self.player = new MediaPlayer(); + NSString *json = nullptr; + if (opt != nullptr) { + json = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:opt options:0 error:nil] + encoding:NSUTF8StringEncoding]; + } + self.player = new MediaPlayer([json UTF8String]); [self resetProperty]; playerListener listener = {0}; mHelper = new CicadaOCHelper(self); @@ -127,18 +140,18 @@ - (instancetype)init:(NSString*)traceID #if TARGET_OS_IPHONE [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(becomeActive) - name:UIApplicationDidBecomeActiveNotification + name:UIApplicationWillEnterForegroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resignActive) - name:UIApplicationWillResignActiveNotification + name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willTerminate) name:UIApplicationWillTerminateNotification object:nil]; - if (UIApplicationStateActive != [[UIApplication sharedApplication] applicationState]) { + if (UIApplicationStateBackground == [[UIApplication sharedApplication] applicationState]) { self.player->EnterBackGround(true); } #endif // TARGET_OS_IPHONE @@ -151,8 +164,8 @@ -(void)dealloc [self destroy]; #if TARGET_OS_IPHONE - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil]; #endif // TARGET_OS_IPHONE } @@ -168,6 +181,9 @@ -(void) setConfig:(CicadaConfig*)config alivcConfig.startBufferDuration = config.startBufferDuration; alivcConfig.networkRetryCount = config.networkRetryCount; alivcConfig.liveStartIndex = config.liveStartIndex; + alivcConfig.mDisableVideo = config.disableVideo; + alivcConfig.mDisableAudio = config.disableAudio; + alivcConfig.mPositionTimerIntervalMs = config.positionTimerIntervalMs; if (nil != config.httpProxy) { alivcConfig.httpProxy = [config.httpProxy UTF8String]; @@ -222,6 +238,9 @@ -(CicadaConfig*) getConfig mConfig.clearShowWhenStop = config->bClearShowWhenStop; mConfig.networkRetryCount = config->networkRetryCount; mConfig.liveStartIndex = config->liveStartIndex; + mConfig.disableAudio = config->mDisableAudio; + mConfig.disableVideo = config->mDisableVideo; + mConfig.positionTimerIntervalMs = config->mPositionTimerIntervalMs; [mConfig.httpHeaders removeAllObjects]; for (int i=0;icustomHeaders.size();i++) { @@ -833,6 +852,13 @@ -(void) selectExtSubtitle:(int)trackIndex enable:(BOOL)enable } } +- (void)setStreamDelayTime:(int)index time:(int)time +{ + if (self.player) { + self.player->SetStreamDelayTime(index, time); + } +} + -(void) reload { if (self.player) { @@ -856,6 +882,14 @@ -(void) setDefaultBandWidth:(int)bandWidth } } +- (int)invokeComponent:(NSString *)content +{ + if (self.player) { + return self.player->InvokeComponent([content UTF8String]); + } + return -1; +} + - (void) setDelegate:(id)theDelegate { _delegate = theDelegate; @@ -897,7 +931,7 @@ int64_t CicadaClockRefer(void *arg) if (player.referClock) { return player.referClock(); } - return -1; + return 0; } -(void) SetClockRefer:(int64_t (^)(void))referClock @@ -927,6 +961,12 @@ -(NSString *) getOption:(CicadaOption)key return [NSString stringWithUTF8String:value]; } +-(void)setAVResourceLoaderDelegate:(id *)avResourceLoaderDelegate { + NSInteger objAddress = (NSInteger)avResourceLoaderDelegate; + NSString *addressStr = [NSString stringWithFormat:@"%ld", (long)objAddress]; + self.player->SetOption("AVResourceLoaderDelegate", addressStr.UTF8String); +} + + (NSString *) getSDKVersion { string version = MediaPlayer::GetSdkVersion(); diff --git a/platform/Apple/source/config.cmake b/platform/Apple/source/config.cmake index dbd770889..fedf74403 100644 --- a/platform/Apple/source/config.cmake +++ b/platform/Apple/source/config.cmake @@ -9,6 +9,8 @@ find_library(COREGRAPHICS CoreGraphics) find_library(OPEN_AL OpenAl) find_library(CORE_IMAGE CoreImage) find_library(QUARTZ_CORE QuartzCore) +find_library(AV_FOUNDATION AVFoundation) + message("xxxxxxxxxxxxxxxxxxxxxxxx TOP_DIR is ${TOP_DIR}") @@ -17,7 +19,6 @@ if ("${TARGET_PLATFORM}" STREQUAL "iOS") ${TOP_DIR}/apsaraPlayer/external/install/ffmpeg/iOS/Xcode/OS/_builds/ NO_DEFAULT_PATH) set(ALIVCFFMPEG ${TOP_DIR}/apsaraPlayer/external/install/ffmpeg/iOS/Xcode/OS/_builds/alivcffmpeg.framework) message("CONAN is ${CONAN}") - find_library(AV_FOUNDATION AVFoundation) find_library(UIKIT UIKit) find_library(OPENGLES OpenGLES) else () @@ -34,6 +35,7 @@ set(ALI_SRC_LIBRARIES data_source framework_filter framework_utils + framework_drm cacheModule videodec muxer @@ -43,28 +45,12 @@ set(ALI_SRC_LIBRARIES iconv resolv bz2 - ${VIDEO_TOOL_BOX} - ${COREMEDIA} - ${COREVIDEO} - ${COREFOUNDATION} - ${COREGRAPHICS} - ${AUDIO_TOOL_BOX} - ${CORE_AUDIO} - ${OPEN_AL} - ${CORE_IMAGE} c++ ) + +set(ALI_SRC_LIBRARIES ${ALI_SRC_LIBRARIES} "-framework CoreMedia -framework CoreVideo -framework VideoToolbox -framework CoreFoundation -framework CoreGraphics -framework AudioToolbox -framework CoreAudio -framework OpenAl -framework CoreImage -framework AVFoundation -framework QuartzCore") if (IOS) - set(ALI_SRC_LIBRARIES ${ALI_SRC_LIBRARIES} - ${AV_FOUNDATION} - ${UIKIT} - ${QUARTZ_CORE} - ${OPENGLES} - ) + set(ALI_SRC_LIBRARIES ${ALI_SRC_LIBRARIES} "-framework UIKit -framework OpenGLES") else () - set(ALI_SRC_LIBRARIES ${ALI_SRC_LIBRARIES} -# ${CONAN} - ${APP_KIT} - ${OPEN_GL} - ) + set(ALI_SRC_LIBRARIES ${ALI_SRC_LIBRARIES} "-framework AppKit -framework OpenGl") endif ()