日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

MediaCodec在Android视频硬解码组件的应用

發布時間:2025/5/22 Android 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MediaCodec在Android视频硬解码组件的应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://yq.aliyun.com/articles/632892

  • 云棲社區>
  • 博客列表>
  • 正文
  • MediaCodec在Android視頻硬解碼組件的應用

    cheenc?2018-09-03 11:21:35?瀏覽433?評論0
    • android ?
    • 函數 ?
    • 阿里技術協會 ?
    • OpenGL

    摘要:?本文大致介紹了一下Android MediaCodec 在解碼的接口調用流程和主流業務邏輯。

    背景:

    隨著多媒體產業的發展,手機端對視頻解碼性能要求越來越高。如果采用cpu進行解碼,則會占用很多cpu資源?,F在主流做法是利用手機gpu資源進行視頻解碼。Android系統在Android4.0(API 16)增加了 MediaCodec,可以支持app調用java接口,進而使用底層硬件的音視頻編解碼能力。Android ndk在 Android 5.0(API21) 提供了對應的Native方法。功能大體相同。

    MediaCodec 可以處理編碼,也可以處理解碼;可以處理音頻,也可以處理視頻,里面有軟解(cpu),也有硬解(gpu)。具體手機Android 系統一般會寫在 media_codecs.xml 上。不同手機位置不一樣。根據我的經驗,大多數手機上是/system/etc/目錄下。

    這里主要是講視頻解碼。

    Android MediaCodec內部大致結構

    如上圖所示,mediacodec 內部有兩種緩沖,一種是InputBuffer,另一種是OutputBuffer。兩種緩沖的大小一般是底層硬件的代碼決定。解碼過程中,Client需要不斷的查詢InputBuffer和OutputBuffer的情況,如果InputBuffer有空閑,則應放入相應碼流;如果OutputBuffer有輸出,則應該及時去消費視頻幀并且釋放。

    codec則內部自啟線程,也是不斷的查詢InputBuffer和OutputBuffer的情況,如果OutputBuffer有空閑并且有未處理的InputBuffer,則去解碼一幀;否則掛起。

    Android MediaCodec啟動流程

    1.判斷Android Runtime版本

    由于ndk接口只有在Android 5.0以上才有,我們先判斷Android版本,如果版本號在5.0以上則使用Ndk接口,否則,使用java反調的方式。

    2.創建解碼器

    mediaCodec提供創建解碼器的方式有兩種,一種比較簡單的方式是通過MIME直接創建解碼器。MIME是解碼器的類型。例如創建264解碼器的話只需要調用如下函數即可:

    ?

    AMediaCodec_createDecoderByType("video/avc")
    如果手機上264解碼器不止一個(通常手機上會有一個硬解碼器和一個Google的軟解碼器),那么MediaCodec會按照默認的順序選擇一個。當然這個順序可以更改。手機一般情況下,是默認選用硬解的。

    ?

    如果想精確的選擇創建的解碼器,可以通過名字創建:

    ?

    AMediaCodec_createCodecByName("OMX.video.decoder.avc")

    ?

    3.配置解碼器

    AMediaCodec_configure(handle,format,surface, crypto, flag)

    這個函數有兩個需要注意,一個是mediaFormat,另一個是surface.crypto是加密相關的,我們這里每太用到。flag是編碼應該注意的參數,解碼一般填0.

    mediaFormat 是Client需要提前告訴解碼器的一些參數格式,包括width,height, sps, pps等。例如:

    AMediaFormat* videoFormat = AMediaFormat_new(); AMediaFormat_setString(videoFormat, "mime", "video/avc"); AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_WIDTH, width); // 視頻寬度 AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_HEIGHT, height); // 視頻高度 AMediaFormat_setBuffer(videoFormat, "csd-0", sps, spsSize); // sps AMediaFormat_setBuffer(videoFormat, "csd-1", pps, ppsSize); // pps

    我發現如果直接將sps,pps放到第一個I幀之前,format不設置,也能解碼成功。如果提前設置的話,configure函數應該可以提前檢查參數,如果參數不支持,則提前返回失敗。

    surface參數直接決定了解碼器 的工作方式。我們如果傳入一個nativeWindow,則解碼器接完之后的AImage將會通過Release方法直接渲染到surface上,然后就有畫面了。這樣省去了圖像從GPU到CPU,CPU再到GPU的拷貝,效率較高;如果我們傳入nullptr,我們則需要通過接口將圖像地址獲取。這樣有個好處就是后面可以接一些CPU的圖像處理,達到我們的要求,然后再進行圖像渲染。

    4.啟動解碼器

    這個比較簡單。就是Start接口調用一下即可。如果沒有configure,則會失敗。

    AMediaCodec_start();

    數據流程

    啟動之后就開始送數據,取數據進行解碼了。根據之前的大致結構描述,數據流程也基本分為兩步,送數據主要圍繞InputBuffer展開,取數據主要圍繞OutputBuffer展開。為了達到最佳實踐,我們發現最好是用兩個線程分別處理這兩個過程,以免互相影響導致效率降低。

    1.送數據

    送數據分3步驟,第一步,獲取InputBufferIndex.這步的主要目的是看看InputBuffer是不是滿了。如果InputBuffer滿,則上游應該進行相應的數據緩存操作。

    ?

    MediaCodec_dequeueInputBuffer(handle, 2000);
    第二步,獲取InputBuffer地址,然后填數據:

    ?

    ?

    AMediaCodec_getInputBuffer(handle, idx, &size);
    第三步,告訴MediaCodec我們數據填好了:

    ?

    ?

    AMediaCodec_queueInputBuffer(handle, idx, 0, bufferSize, pts, 0);
    這里具體一些參數這里不講了,詳情Android Developer上有詳細解釋。我這里是有個疑問,為啥要獲取InputBuffer地址,然后填數據再告訴它填好了,這樣需要兩個函數getInputBuffer和queueInputBuffer。如果直接用一個函數SendDataToInputBuffer代替不更好么?

    ?

    這里要提一句的是Android 硬解碼只支持AnnexB 格式的碼流,也就是 00 00 00 01 開頭的碼流,如果是avcc 字節長度開頭的碼流,需要提前轉一下。

    2.取數據

    取數據相對送數據復雜一些,第一步獲取index,這是看看有沒有解碼好的幀:

    ?

    AMediaCodec_dequeueOutputBuffer(handle, &info, 2000);
    如果有,則取幀。若surface填nullptr,則可以通過接口獲取數據地址:

    ?

    ?

    AMediaCodec_getOutputBuffer(handle, idx, &outsize);
    如果surface之前填有值,我們可以通過release接口直接將圖像渲染到Surface上:

    ?

    ?

    AMediaCodec_releaseOutputBuffer(handle, idx, bRender);
    取數據需要注意一點的就是getOutputBuffer時候可能會獲取到一些負值。并且這些負值都是很有意義的。例如AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED就表示了輸出格式發生改變等等。我們需要關注這些信息,及時更新解碼器的輸出格式。

    ?

    硬解碼業務路線

    1.代替軟解的硬解碼

    最簡單的方式,就是configure時候Surface填null,然后將解碼后的數據拷貝出來。這樣做的有點很明顯,就是跟之前的軟解邏輯基本一樣,外面并不需要改變太多,之前的VideoProcess 也能接著用,也不需要渲染引擎的配合,封裝性好。缺點是多了一次解碼器內存到自己內存的拷貝。

    2.利用解碼器緩存

    如果我們針對業務一的拷貝做優化,減少拷貝,這就是第二種業務路線。我們可以利用解碼器的緩存進行輸出存儲。也就是說,我們調用ouputBuffer之后,獲得輸出緩存index,并不著急拷貝出圖像。而是等到渲染時候,調用GetOutputBuffer獲取圖像指針,然后調用Image2D,進行生成gpu紋理。

    3.利用GPU Image直接渲染

    如果我們configure傳surface,我們可以通過gpu傳遞的方法,直接進行渲染,這樣可以減少GPU <-> CPU之間的內存拷貝。首先configure時候傳surface,我們調用ouputBuffer之后,獲得輸出緩存index,得到渲染時候,直接調用releaseOutputBuffer(handle,idx,true),則解碼器的圖像直接渲染到surface圖像上了。

    這樣雖然效率高,但是弊端也很明顯,第一,那就是圖像后處理做不了。第二,這種方案依賴解碼器緩存,這會帶來一些問題。如果解碼器被提前析構,則緩存內容都沒有了。又或者一些播放業務邏輯對解碼器緩存要求較多(比如倒放),這也做不了。

    4.利用GPU Image,SurfaceTexture類渲染到OpenGL管線

    針對業務路線3,Android系統也考慮到這個問題,提供我們一種方案做折中。我們可以先建立自己的OpenGL環境,然后從建立Texture,通過Texture建立SurfaceTexture,然后取出surface,進行Configure。這樣,MediaCodec的Release就渲染到SurfaceTexture類了。然后我們調用Update方法,就同步到OpenGL的Texture上了。之后可以接各種后處理,然后swapbuffer進行顯示等等。

    這樣處理得話,基本所有的業務邏輯都可以滿足。但是有一個小問題就是流暢性不足。具體為:當輸出一個surface,并且OpenGL還沒消費這個surface時候,解碼輸出是被阻塞的。也就是說,outputBuffer和OpenGL cosume 這個surface必須串行執行。如果并行,則會有覆蓋的問題。

    因此我們可以采取一步小調整:將OpenGL得到的Texture 拷貝一份(是GPU->GPU復制,紋理復制)。這樣OpenGL就不會阻塞解碼輸出了。但是代價會帶來拷貝性能損耗。

    5.多路同步,增大流暢性

    Android 6.0 (API23)新增了一個接口 —— setOutputSurface。顧名思義,這個可以動態的設置輸出的Surface。這就完美解決了上面的問題。具體為,我們可以事先建立多個Texture,然后OutputBuffer時候循環輸出到任意一個空閑Texture并標記為帶數據,當OpenGL消費了圖像之后,將Texture回歸空閑。這樣相當于在OutputBuffer和OpenGL消費之間建立了一個紋理緩沖??梢酝瓿啥嗑€程并行的需求。

    缺點很明顯就是需要Android 6.0才能支持,不過現在通過Android統計面板https://developer.android.com/about/dashboards/

    能看到大部分手機都在Android 6.0之上。

    最后

    Google的官方文檔關于 Android MediaCodec 還是很詳細的。應該還有很多隱藏屬性待我們發覺。我們要多查查官方文檔手冊:

    java文檔:https://developer.android.com/reference/android/media/MediaCodec

    ndk文檔:https://developer.android.com/ndk/reference/group/media

    同時 Android Samples 有Sample Code,可供參考

    https://github.com/googlesamples/android-BasicMediaDecoder/

    ffmpeg上也有相關封裝,具體文件為:/libavcodec/mediacodecdec.c

    轉載于:https://www.cnblogs.com/jukan/p/9877430.html

    總結

    以上是生活随笔為你收集整理的MediaCodec在Android视频硬解码组件的应用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 日本丰满少妇裸体自慰 | 国产又粗又黄又猛 | 欧美日韩一二三区 | 一本色道久久加勒比精品 | 精品中出 | 亚洲激情视频一区 | 伊人影院综合在线 | 免费看爱爱视频 | 久久99九九 | 日韩精品av一区二区三区 | 亚洲色图欧美色 | 91精品999| 免费一级做a爰片久久毛片潮 | 你懂的av在线 | 亚洲 欧美 日韩 在线 | 色爽视频 | 五月婷婷中文 | 欧美精品在线一区 | 一区二区视频在线观看 | 亚洲人成电影网 | 麻豆精品久久 | 97精品视频在线观看 | 亚洲人午夜射精精品日韩 | 少妇又紧又深又湿又爽视频 | 国产美女www爽爽爽视频 | aaa黄色一级片 | 日韩三区视频 | 簧片av | 国产视频污 | 色撸撸av | jizz性欧美17| 欧洲-级毛片内射 | 一道本视频在线 | 国产精品77 | 性大片潘金莲裸体 | 中国美女黄色一级片 | 欧美伦理一区二区 | 天天干天天操天天拍 | 挪威xxxx性hd极品 | 欧美一区二区公司 | 日本一区二区在线观看视频 | 中文字幕日韩欧美一区二区三区 | 精品国产一区二区三区在线观看 | 成人午夜淫片100集 伊人久久国产 | 天天躁日日躁bbbbb | 亲子乱子伦xxxx| 成年人免费在线 | 亚洲丁香 | 欧美一卡二卡三卡 | 国产aⅴ无码片毛片一级一区2 | 日韩精品欧美精品 | 国产午夜伦鲁鲁 | 国产富婆一级全黄大片 | 国精产品一区一区三区mba下载 | 久久综合88| 免费看黄在线观看 | 激情欧美综合 | 老外黄色一级片 | 日本视频在线播放 | 青青草免费观看视频 | 青草视频免费看 | 在线观看免费视频一区二区 | 久久久久久国产精品三区 | 二区在线观看 | 一区二区的视频 | 男女在线视频 | 亚洲欧洲无码一区二区三区 | 九九热视频这里只有精品 | 亚洲男人的天堂在线 | 青草国产 | 男女日批视频 | av天天有 | 69视频网| 在线国产91| av鲁丝一区鲁丝二区鲁丝三区 | 色综合天 | 天天操天天干天天爽 | 涩涩视频免费在线观看 | 不卡视频在线播放 | 麻豆91在线播放 | 欧美性区| 国产精品成人3p一区二区三区 | 黄色网一级片 | 欧美午夜在线观看 | 在线天堂中文字幕 | 成人免费在线小视频 | 美乳人妻一区二区三区 | 日韩午夜剧场 | 四虎成人在线观看 | 一本色道久久综合精品婷婷 | 欲色av| 正在播放adn156松下纱荣子 | 一区二区三区小视频 | 免费视频污 | 在线波多野结衣 | 女人的天堂网站 | 精产国产伦理一二三区 | 日韩视频中文 | 久久精品大全 |