Android MediaCodec硬解碼囤热、ffmpeg軟解碼猎提,兼顧機(jī)型一致性和性能

MediaCodec硬解

首先考慮使用MediaCodec硬解碼,硬解碼的代碼谷歌的文檔很詳細(xì)旁蔼,主要分為異步模式锨苏、同步模式疙教。至于解碼的輸出,如果是解碼到文件中伞租,可以提取outputBuffer后寫入文件松逊;如果是用于顯示,推薦初始化MediaCodec的時(shí)候傳入Surface:

decoder.configure(mediaFormat, surface, null, 0);

mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

decoder.releaseOutputBuffer(outputBufferIndex, true);

這樣Surface會(huì)與codec綁定起來肯夏,解碼后的buffer直接在底層用于顯示到Surface上经宏,無需業(yè)務(wù)層數(shù)組拷貝,效率最高驯击,同時(shí)這種情況下outputBuffer中獲取到的buffer也為null烁兰。decoder.releaseOutputBuffer是解碼器真正解碼渲染的時(shí)候。

不過有遇到某些h264流在某些手機(jī)上徊都,硬解碼丟幀的情況沪斟,調(diào)試發(fā)現(xiàn)很多幀在decoder.dequeueOutputBuffer(bufferInfo, 0);會(huì)返回-2,也就是format changed暇矫,導(dǎo)致這些幀解碼失敗主之。故思考mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);這邊將COLOR_FormatSurface換成COLOR_QCOM_FormatYUV420SemiPlanar,再將解碼出來的yuv李根,通過OpenGLES2.0或者EGL槽奕,顯示在Surface上,是否能規(guī)避這個(gè)問題房轿。經(jīng)過一番嘗試粤攒,上述出現(xiàn)問題的攝像頭和手機(jī)的配合,不管KEY_COLOR_FORMAT換成哪個(gè)囱持,依然format changed造成丟幀夯接。為了一致性的體驗(yàn),決定轉(zhuǎn)向軟解纷妆。

ffmpeg軟解

使用ffmpeg軟解不需要我們在業(yè)務(wù)代碼里拆幀盔几,只要將流一股腦的給ffmpeg就行,ffmpeg自己有av_read_frame函數(shù)可以拆幀掩幢,我們主要做好隊(duì)列工作逊拍。網(wǎng)上大部分ffmpeg軟解的代碼都是從文件讀流,直接將文件的path傳給ffmpeg最簡單粒蜈,但是要使用攝像頭這樣的buffer輸入的話顺献,需要用到avio_alloc_context、av_probe_input_buffer枯怖。

顯示部分注整,可以將Surface傳給ffmpeg,轉(zhuǎn)為ANativeWindow,解碼后的yuv通過sws_scale轉(zhuǎn)為rgb肿轨,再逐行復(fù)制到ANativeWindow里就可以顯示寿冕。

然而遇到了喜聞樂見的軟解性能問題。攝像頭碼率提到10M后椒袍,解碼成為瓶頸驼唱,每一幀的解碼需要將近30ms,加上渲染時(shí)長驹暑,導(dǎo)致幀率低于攝像頭的原始幀率30玫恳,出現(xiàn)畫面延遲,幀率不足优俘,很快解碼buffer隊(duì)列就滿了京办,隊(duì)列滿了就要丟棄老數(shù)據(jù),于是畫面就會(huì)出現(xiàn)馬賽克帆焕。這里使用三星S8加上幀率30惭婿,10M碼率的攝像頭做測試:解碼后不渲染,幀率34-38叶雹;解碼后同一個(gè)線程渲染财饥,幀率22,CPU占用始終125%左右

可見光解碼的話折晦,還是可以保證幀率的钥星,但是加上渲染就不行了。

ffmpeg軟解筋遭,解碼和渲染異步

于是嘗試將解碼線程和渲染線程獨(dú)立出來打颤,盡量榨干CPU。需要注意的是avcodec_send_packet和avcodec_receive_frame必須同步調(diào)用漓滔,就是send后必須馬上receive,等receive返回不為0后乖篷,才能繼續(xù)send响驴,否則send會(huì)失敗。

這兩個(gè)函數(shù)名設(shè)計(jì)的讓人容易產(chǎn)生誤解撕蔼,以為ffmpeg自己維護(hù)了一個(gè)幀隊(duì)列豁鲤,然后可以在兩個(gè)線程中分別send和receive,其實(shí)是錯(cuò)的鲸沮,ffmpeg應(yīng)該只維護(hù)了一個(gè)數(shù)組琳骡,數(shù)組為空取完后才可以再次send。

所以需要send和receive在同一個(gè)線程同步執(zhí)行讼溺,receive后將AVFrame放到隊(duì)列里楣号;另一個(gè)線程從隊(duì)列里取幀,進(jìn)行sws_scale后,繪制rgb到NativeWindow上炫狱。解碼+渲染藻懒,CPU占用上升到180%,性能提升到幀率29视译,但是一會(huì)兒CPU會(huì)發(fā)熱降頻嬉荆,解碼耗時(shí)增大,幀率掉到20酷含,這性能還是沒達(dá)到要求鄙早。

ffmpeg硬解

于是嘗試使用ffmpeg硬解。雖然測試過某些攝像頭在某些手機(jī)上調(diào)用MediaCodec硬解會(huì)出現(xiàn)format changed導(dǎo)致丟幀的現(xiàn)象椅亚,并且ffmpeg實(shí)際上也是使用MediaCodec實(shí)現(xiàn)的硬解蝶锋,但是本著不試一試怎么知道的精神,決定嘗試ffmpeg硬解什往。

configure需要做如下配置:

--enable-jni

--enable-mediacodec

--enable-decoder=h264_mediacodec

--enable-hwaccel=h264_mediacodec

--target-os=android(這條如果沒有扳缕,會(huì)報(bào)錯(cuò)jni not found)

由于我之前編譯過ijkplayer,有一個(gè)中間步驟是編譯ffmpeg别威,于是圖方便使用ijk的工程來編譯躯舔,發(fā)現(xiàn)加上上述configure后總是報(bào)jni not found,后來發(fā)現(xiàn)需要ijkplayer/android/contrib/tools/do-compile-ffmpeg.sh中將FF_CFG_FLAGS="$FF_CFG_FLAGS --target-os=linux"改為--target-os=android編譯好新的ffmpeg后嘗試省古,創(chuàng)建解碼器的時(shí)候粥庄,需要使用AVCodec *pCodec = avcodec_find_decoder_by_name("h264_mediacodec");代替掉AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

硬解確實(shí)解碼很快,每一幀的解碼時(shí)間縮短為1ms左右豺妓,但是發(fā)現(xiàn)畫面卡頓惜互,CPU占用依然很高,一看log琳拭,發(fā)現(xiàn)瓶頸變?yōu)閟ws_scale训堆,硬解后的yuv進(jìn)行sws_scale計(jì)算,效率非常低白嘁,使用SWS_BILINEAR算法坑鱼,一幀的scale需要68ms,很快渲染隊(duì)列就滿了絮缅。網(wǎng)上查到可以使用libyuv通過neon硬件加速替換掉sws_scale函數(shù)鲁沥,也有使用OpenGL硬件加速渲染yuv到Surface的方案。原因是軟解出來的yuv耕魄,是YUV420P画恰,sws_scale效率高,一幀只需要12ms吸奴,硬解出來的yuv是NV12允扇,sws_scale效率很低缠局。

ffmpeg多線程軟解

嘗試多線程軟解,繼續(xù)榨干CPU蔼两,解決解碼性能瓶頸甩鳄。配置多線程解碼:pCodecCtx->thread_count = 8;有個(gè)坑是設(shè)置pCodecCtx->thread_type = FF_THREAD_SLICE;后,反而多線程無效额划,注釋掉后多線程解碼生效妙啃,性能飆升,幀率直接取決于喂數(shù)據(jù)的速度

加快喂數(shù)據(jù)俊戳,能吃掉更多CPU資源

加快喂數(shù)據(jù)后的幀率直接上去了

總結(jié)

在取舍了機(jī)型一致性揖赴、性能后,最終方案:ffmpeg多線程軟解抑胎,通過sws_scale轉(zhuǎn)換yuv到rgb顯示在ANativeWindow上燥滑。

優(yōu)化空間:sws_scale替換為libyuv,或者OpenGL阿逃,進(jìn)一步降低能耗

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铭拧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恃锉,更是在濱河造成了極大的恐慌搀菩,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件破托,死亡現(xiàn)場離奇詭異肪跋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)土砂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門州既,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萝映,你說我怎么就攤上這事吴叶≌鸢梗” “怎么了赴恨?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長袍辞。 經(jīng)常有香客問我贸宏,道長,這世上最難降的妖魔是什么磕洪? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任吭练,我火速辦了婚禮,結(jié)果婚禮上析显,老公的妹妹穿的比我還像新娘鲫咽。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布分尸。 她就那樣靜靜地躺著锦聊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪箩绍。 梳的紋絲不亂的頭發(fā)上孔庭,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機(jī)與錄音材蛛,去河邊找鬼圆到。 笑死,一個(gè)胖子當(dāng)著我的面吹牛卑吭,可吹牛的內(nèi)容都是我干的芽淡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼豆赏,長吁一口氣:“原來是場噩夢啊……” “哼挣菲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掷邦,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤白胀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后耙饰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纹笼,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年苟跪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了廷痘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡件已,死狀恐怖笋额,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情篷扩,我是刑警寧澤兄猩,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站鉴未,受9級特大地震影響枢冤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铜秆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一淹真、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧连茧,春花似錦核蘸、人聲如沸巍糯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祟峦。三九已至,卻和暖如春徙鱼,著一層夾襖步出監(jiān)牢的瞬間宅楞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工疆偿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留咱筛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓杆故,卻偏偏與公主長得像迅箩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子处铛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容