實習工作記錄
- Android下ffmpeg+x264+fdk-aac軟解碼demo
功能描述:Android下獲取攝像頭1080P的YV12格式視頻編碼爲h264格式視頻保存,同時獲取PCM格式音頻數據編碼爲AAC格式音頻保存
重點:控制編碼時間不超過33ms
編碼時間優化過程:
首先在native層使用ffmpeg的編碼器接口調用x264庫
關鍵的參數設置爲
pCodecCtx->thread_count = 4 ;//設置開啓線程數(測試手機的cpu核數)
av_dict_set(¶m, "preset", "ultrafast", 0); //有ultrafast、superfast、veryfast、fast、、medium、slow、slower、veryslow的選項,影響編碼速度和質量
av_dict_set(¶m, "tune", "zerolatency", 0); //有film 、animation、grain、stillimage、psnr、ssim、fastdecode、zerolatency的選項,從左到右編碼複雜度依次遞減,質量依次遞減,實時編碼必須設置zerolatency,表示零延遲輸出當前編碼幀
但是在這個參數設置下,編碼時間爲80ms一幀,並會逐漸增長,達不到要求
分析編碼處理時間包括:
- 將YV21數據轉換成YUV420P的時間
- ffmpeg與X264編碼器之間的參數傳遞和函數調用
- x264編碼器編碼的時間
數據格式在1080P下的轉換時間在10毫秒左右,爲了排除ffmpeg的影響,我對x264庫相同設置下的編碼時間進行了測試,單獨編譯了x264庫,配置了AS,在native層重寫了一個調用264庫的類,封裝好給java層調用,編碼速度得到提升,但還是逐漸變慢,分析應該是由於編碼對於CPU的需求比較高,但編碼線程並沒有得到很好的調度,所以在java層採用android.os.Process類中設置優先級的函數來對編碼線程進行調度優化,結果就是,直接使用x264工程加上調度優化可以滿足需求,也就是說x264編碼器在設置正確的情況下的速度是可以滿足要求的。
接下來研究爲什麼在使用ffmpeg調用x264庫時同樣的設置起不了作用,對log和源碼進行分析之後發現,雖然設置了幀並行編碼線程數,但是並沒有起作用,也就是說並行編碼沒起作用,這是由於ffmpeg的參數設置並不是與x264的參數完全一一對應,要加上以下設置(表明並行類型是幀並行),結果就與直接應用x264無差異了
pCodecCtx->thread_type = FF_THREAD_FRAME;
需要注意的問題:
1 編譯x264和fdk-aac庫的時候可能會出現找不到庫的問題,這是由於路徑設置可能存在問題
2 fdk-aac庫在使用ffmpeg調用時,可能會報錯,這是使用了根據id尋找編碼器的函數造成的,由於fdk-aac的id與ffmpeg內置aac格式的音頻編碼器的id相同,所以應使用編碼器name尋找(avcodec_find_encoder_by_name(“libfdk_aac”))
- 研究如何給ffmpeg添加自定義的編碼器
- 編寫Android下MediaCodec硬解碼類
描述:實現三個接口,init、encode、release,encode實現YUV420P到h264同步編碼,爲了熟悉Android硬解碼功能 - 優化首幀時間
描述:線上代碼存在首幀硬編碼時間過長問題(從開始獲取圖像至返回編碼後的首幀時間400-500ms)
首幀過慢原因:
線上的硬編碼代碼邏輯是,每次獲取到相機返回的圖像後調用encodevideofrombuffer()
進行此幀的編碼並返回編碼後的幀
核心函數encodevideofrombuffer()
中的邏輯是獲取編碼器的的可用buffer將YUV數據寫入,並調用dequeueOutputBuffer( bufferInfo, TIMEOUT_USEC)
取出編碼後的幀
有兩部分原因導致首幀時間長:
1 初始化硬解碼器的時機不對,應該儘量將初始化代碼提前
2 TIMEOUT_USEC設置過小,編碼未完成導致超時返回,只能放棄此次取數據,等到下次相機返回圖像時再取出此幀,也由於android的硬編碼器規定要先輸出一個配置幀,所以一般要等到第二、三幀時才能獲取到首幀解碼後的數據,並且幀率越小,幀間間隔越大,拿到首幀的時間越長
優化過程:
1、修改了init函數,將初始化時間提前到執行oncreate()
時
2、修改了TIMEOUT_USEC與邏輯,使得可在第一次調用encodevideofrombuffer()
時就返回解碼後的首幀
優化結果:
從獲取到相機圖像到獲取到解碼後的首幀的時間縮短至40ms左右