播放錄制是在觀眾端錄制視頻內容存至本地。觀眾對觀看內容非常感興趣想要將該視頻內容留存至本地時便可使用該功能。
一. 可行的方案
在Android端實現播放錄制的方法大約有下面三種:
1. 錄屏
在Android 5.0 (API Level 21)及以上版本提供了錄屏功能,使用系統提供的類MediaProjection
與VirtualDisplay
可實現在Android端的錄制屏幕內容的功能,此處不再贅述。
但此方法亦會錄制應用的UI及可能的消息通知等與視頻無關的內容,對Android的版本有一定要求。
2. 重封裝(Remux)
播放器在播放視頻時可獲取原始音視頻數據,當觀眾希望錄制視頻的時候,將原數數據進行一次Remux即成。
但是Remux的數據需要保證第一個視頻幀是關鍵幀。因此播放器內部內部需要緩存已經解碼并播放過的音視頻數據直到新的關鍵幀(IDR幀)到來。
但此方案存在若干問題:
- 額外的內存開銷。已經用于解碼的音視頻數據,其對應內存不可被釋放,以備觀眾錄制視頻內容、直到新的GOP數據到來,上一個GOP的數據方可被釋放。這樣就帶來了額外的內存開銷。
- 內容多余。此方案必須從關鍵幀開始做Remux,并不一定是從用戶想要錄制的內容開始,特別是對于GOP較大的視頻,此問題尤為明顯。
3. 重編碼&封裝
此方法是將解碼之后的YUV和PCM數據送入編碼器,將編碼后的數據重新封裝為目標視頻。
此方案需要重新編碼,會占用相當的CPU資源。
我們最終采用了方案3作為初始方案,采用Android手機提供的硬編功能將音視頻數據編碼,將編碼后的數據封裝為MP4文件。
二. 踩過的坑
在開發過程中踩過不少坑,下面和大家分享下:
2.1 MediaCodec
MediaCodec是Android提供的關于音視頻硬編/硬解功能的核心類,其接口及相應功能在此不在贅述。實現播放錄屏功能時使用MediaCodec編碼視頻,遇到的最坑的問題是:
- 顏色空間,一般情況下軟解視頻輸出
YUV420Planar
數據,而Android手機可能只支持YUV420Planar
或YUV420SemiPlanar
,其區別如下圖所示。因此需要根據每個手機的實際情況做出適配。
image.png
2.2 MediaMuxer
MediaMuxer
是Android于4.3版本引入的用于將編碼后的音視頻數據封裝為MP4的核心類。實現播放錄屏功能時最初是選用MediaMuxer
將編碼之后的音視頻數據復用為MP4,然則因為Android系統的碎片化,各廠商根據各自的需求對ROM做了相應修改,導致MediaMuxer
的穩定性在各手機表現不太一致,與預期相差較遠。
三. 最終的方案
最終的解決方案是使用MediaCodec+FFMpeg
,MediaCodec
將解碼后的音視頻數據編碼,FFMpeg
將編碼之后的音視頻數據封裝為MP4文件。其中視頻編碼默認使用H.264/AVC的Baseline,音頻編碼使用AAC-LC。其基本架構圖如下所示
- 播放器將解碼后的音視頻數據(YUV/PCM)交予錄制模塊,錄制模塊有兩個緩存隊列分別緩存解碼后的音視頻數據;
- 錄制模塊的音視頻各有一個編碼線程,每個線程持有一個
MediaCodec
的對象,分別從音視頻隊列取解碼后的數據,然后將編碼后的數據放入已編碼數據隊列。寫數據線程會不斷從已編碼數據隊列中拉取數據,然后調用封裝模塊的接口,將音視頻數據封裝為MP4; - 封裝模塊調用了
FFMpeg
提供的接口,將音視頻數據寫入本地磁盤。其中需要先寫入音視頻的關鍵信息例如: SPS/PPS、ADTS等。
四. 結語
按照以上步驟便可實現在Android端的播放錄制功能,但是依舊有改進的空間,例如使用MediaCodec
做視頻編碼在少許低端機型依舊存在問題,后續會根據推出更加穩定的版本。
轉載請注明:
作者金山視頻云,首發簡書 Jianshu.com
有關音視頻的更多精彩內容,請參考https://github.com/ksvc
金山云SDK相關的QQ交流群:
- 視頻云技術交流群:574179720
- 視頻云Android技術交流:620036233