編解碼流程

目錄

1 整體步驟

2 MediaCodec 使用的基本流程
3 設計
4 編解碼實例分析
5 Android FFmpeg視頻解碼播放
6 從零實現一個H.264碼流解析器
7 音視頻同步
8 OpenSL ES音頻解碼播放

正文

1 整體步驟

image.png

模塊之間數據(buffer)流程圖。
1.1 編碼:dequeueinputBuffer(從input緩沖隊列申請empty buffer.左邊的上虛線)---inputbuffer(拷貝mp4文件的一幀到empty buffer)
--queueInputBuffe(將inputbuffer放回codec)---dequeueOutputBuffer(從output緩沖區隊列申請編解碼后的buffer)--編碼后的數據渲染--releaseOutputBuffer(放回到output緩沖區隊列)
1.2 解碼:dequeueinputBuffer(從input緩沖隊列申請empty buffer)---inputbuffer(拷貝mp4文件的數據到empty buffer)---mediacodec(從input buffer 取一幀)
--dequeueOutputBuffer(從output緩沖區隊列申請編解碼后的buffer)--解碼后的數據播放--releaseOutputBuffer(放回到output緩沖區隊列)

2 MediaCodec 使用的基本流程

MediaCodec 的生命周期有三種狀態:停止態-Stopped、執行態-Executing、釋放態-Released
停止狀態(Stopped)包括了三種子狀態:未初始化(Uninitialized)、配置(Configured)、錯誤(Error)。
執行狀態(Executing)會經歷三種子狀態:刷新(Flushed)、運行(Running)、流結束(End-of-Stream

image

理解了queueInputBuffe作用,就理解了編解碼流程.
1) mediaExtractor.setDataSource(path);
//extractor讀取sampleData
int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
2 ) decodec.queueInputBuffer(index, 0, sampleSize, mediaExtractor.getSampleTime(), 0);
//一直沒搞懂queueInputBuffer的作用,主要是因為inputBuffer沒搞懂。codec抽取了文件不就是解碼了嗎?都已經放入inputbuffer中了,還需要queueInputBuffer干什么?
實際這是兩件事,兩個主體,mediaExtractor and decodec 操作同一個inputBuffer;
mediaExtractor僅僅負責抽取sample到 input buffer,比如aac是1024. 沒有編解碼操作.僅僅是拷貝部分文件。
decodec是真正的編解碼,將一幀中的aac,去header,變成pcm.并且將pcm放入queueoutputbuffer。
queueInputBuffer將input buffer放回codec,實現兩件事,1) codec 開始編解碼,結果放入outputbuffer 2)將inputbuffer重新入隊
mediaextractor& queueInputBuffer
mediaextractor:input-source file,output--inputbuffer
queueInputBuffer:inputbuffer--outputbuffer (!!抽取sample的不是codec.也不是放入codec的inputbuffer就自動開始編解碼,而是需要queueInputBuffer通知codec開始編解碼)
notes: dequeueInputBuffer是典型的dequeue 隊列的用法,從header取一個buffer.
queue & dequeue 是入隊出隊.兩者都是codec做的.

3 設計

流程圖

image

偽代碼

- createByCodeName/createEncoderByType/createDecoderByType: (靜態工廠構造MediaCodec對象)--生成MediaCodec,Uninitialized狀態
- configure:(配置) -- configure狀態
- start        (啟動)--處于Excuting狀態 Flushed子狀態,然后進入Running狀態
- while(1) {
    try{
       - dequeueInputBuffer    (從編解碼器獲取輸入緩沖區buffer)
       - queueInputBuffer      (buffer被生成方client填滿之后提交給編解碼器)
       - dequeueOutputBuffer   (從編解碼器獲取輸出緩沖區buffer)
       - releaseOutputBuffer   (消費方client消費之后釋放給編解器)
    } catch(Error e){
       - error                   (出現異常 進入error狀態)
    }

}
- stop                          (編解碼完成后,釋放codec)
- release

MediaCodec 有已下解碼相關的 API:
1)dequeueInputBuffer:若大于 0,則是返回填充編碼數據的緩沖區的索引,該操作為同步操作;
返回一個input index
2)getInputBuffer:填充編碼數據的 ByteBuffer 數組,結合 dequeueInputBuffer 返回值,可獲取一個可填充編碼數據的 ByteBuffer;
根據index 返回一個input ByteBuffer
3)queueInputBuffer :應用將編碼數據拷貝到 ByteBuffer 后,通過該方法告知 MediaCodec 已經填寫的編碼數據的緩沖區索引;
拷貝操作
4)dequeueOutputBuffer:若大于 0,則是返回填充解碼數據的緩沖區的索引,該操作為同步操作;
返回一個output index
5)getOutputBuffer:填充解碼數據的 ByteBuffer 數組,結合 dequeueOutputBuffer 返回值,可獲取一個可填充解碼數據的 ByteBuffer;
根據index,返回一個output ByteBuffer
6)releaseOutputBuffer:告訴編碼器數據處理完成,釋放 ByteBuffer 數據。
release output buffer。
補充:
a) release queueoutputbuffer
沒有release queue inputbuffer,從上圖可知,release inputbuffer是codec的工作.
b) //讀取下一幀
mediaExtractor.advance();

在實踐當中發現,發送端發送的視頻寬高需要 16 字節對齊,因為在某些 Android 手機上解碼器需要 16 字節對齊。
other:若非16字節對齊,dequeueOutputBuffer會有一次MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED.
而不是一上來就能成功解碼一幀.

經測試發現:幀寬高非 16 字節對齊會比 16 字節對齊的慢 100 ms 左右

4 編解碼實例分析

編解碼分成同步和異步兩種實現方法。

image.png

流程圖比較給力.屬于詳細設計
//【解碼步驟:1. 初始化,并啟動解碼器】
//【解碼步驟:2. 將數據壓入解碼器輸入緩沖】
//【解碼步驟:3. 將解碼好的數據從緩沖區拉取出來】
//【解碼步驟:4. 渲染】
//【解碼步驟:5. 釋放輸出緩沖】
//【解碼步驟:6. 判斷解碼是否完成】
音視頻硬解碼流程:封裝基礎解碼框架: http://www.lxweimin.com/p/ff65ef5207ce 解碼器
MediaCodec進行編解碼AAC(文件格式轉換) https://hejunlin.blog.csdn.net/article/details/103771289.
Android 音視頻學習:使用 MediaCodec API 完成音頻 AAC 硬編、硬解 https://my.oschina.net/u/4582396/blog/4384448
音視頻開發之旅(六)MediaCodec硬編解流程與實踐 https://zhuanlan.zhihu.com/p/268441151

5 Android FFmpeg視頻編解碼播放

image
image
image

FFmpeg 開發(02):FFmpeg + ANativeWindow 實現視頻解碼播放 http://www.lxweimin.com/p/1efd030e8cf7

Android FFmpeg視頻解碼播放:http://www.lxweimin.com/p/d7c8f49d9ea4

6 從零實現一個H.264碼流解析器

【從零實現一個H.264碼流解析器】(一):從碼流中找到NALU https://mp.weixin.qq.com/s/7E0z-mRi4uURICm0RVXuxw
【從零實現一個H.264碼流解析器】(二):導入指數哥倫布解碼實現并初步解析NALU https://mp.weixin.qq.com/s?__biz=MzI5Njc3OTk5NA==&mid=2247483958&idx=1&sn=7eeb49fa874115abd9237db2d4175fcd&chksm=ecbe6b83dbc9e295c058dea5c5915792377118b8d670fd6059a8b62c5ee70da69554e9f49edb&scene=21#wechat_redirect
【從零實現一個H.264碼流解析器】(三):解析序列參數集SPS的句法元素 https://mp.weixin.qq.com/s?__biz=MzI5Njc3OTk5NA==&mid=2247483961&idx=1&sn=175dcc1ce65659b24f4e09e2dfa11c2d&chksm=ecbe6b8cdbc9e29a57831664a9b89074a4e9829556601196c05b370858787519c807416f3cc3&scene=21#wechat_redirect
【從零實現一個H.264碼流解析器】(四):生成句法元素跟蹤trace文件 https://mp.weixin.qq.com/s/ZPUg4wWU0CWlN7pp0DbspQ
【從零實現一個H.264碼流解析器】(五):解析圖像參數集PPS的句法元素 https://mp.weixin.qq.com/s/GXkq88XrHW8Pug-376T5BA
【從零實現一個H.264碼流解析器】(六):解析片頭部Slice_Header的句法元素 https://mp.weixin.qq.com/s/01-JTlph0mduW76N-zubaA

7 音視頻同步

http://www.lxweimin.com/p/ba8db84f8fe8
時間同步的原理如下:
codec 出來的pts 播放時間戳和system current time保存一致
進入解碼前,獲取當前系統時間,存放在mStartTimeForSync,一幀數據解碼出來以后,計算當前系統時間和mStartTimeForSync的距離,也就是已經播放的時間,如果當前幀的PTS大于流失的時間,進入sleep,否則直接渲染

8 OpenSL ES音頻解碼播放

OpenSL 是音頻編解碼,ffmpeg是視頻編解碼. ffmpeg也可以音頻編解碼.
Android FFmpeg+OpenSL ES音頻解碼播放 http://www.lxweimin.com/p/28fc978721b4
用ffmpeg解碼視頻,用opensl es解碼音頻。用opensl 渲染音頻,eg:混音(增加音效?)。小結opensl的使用steps good

other:常用格式YUV 4:2:0 下面link說的清楚: https://www.cnblogs.com/leisure_chn/p/10290575.html

QA:

codec 注意碼率設置不成功,可能是幀率設置有問題
每當切換到前置攝像頭的時候,界面會有部分卡在上一個界面.每當切換到前置攝像頭的時候,界面會有部分卡在上一個界面

附錄:

old:用MediaCodec解碼->取出ByteBuffer->用OpenGLES處理->處理完畢后readPixels->得到圖像數據->將圖像數據推入MediaCodec編碼.(readPixels非常耗時。480*840的視頻,一幀耗時基本是40ms+)
new:MediaCodec解碼視頻直接解碼到Surface上->通過OpenGLES處理->又通過Surface進行編碼
(無需關注解碼出來的數據的格式了,而且應用層也不必自己去將原始數據導入GPU以及將處理后的數據導出GPU了,這些工作可以都丟給Android SDK去做)

5 Android音視頻硬編碼:生成一個MP4:

http://www.lxweimin.com/p/bfdeac7da147
code實現完全符合標準設計流程:先變量,接口。然后基類核心函數。然后實現av子類. 然后render獨立線程。最后activity調用線程池運行子類。
實現:
1 基類實現
1). 定義編碼器變量
2) 初始化編碼器 (定義接口)
3). 開啟編碼循環 :encode實現的核心,使用codec實現。這樣子類負責配置就可以了.
4). 拉取數據 :將codec出來的數據,writeData到mp4文件
2、音頻/視頻編碼器
configEncoder ,addTrack,writeVideoData
3、整合
1 ) 主線程
從系統獲取surface

override fun surfaceCreated(holder: SurfaceHolder) {
        mSurface = holder.surface
        mThread.onSurfaceCreate()
    }

decodeOneFrame --notifySwap通知渲染線程render --渲染之后encodeOneFrame
2 ) 渲染線程:initEGL,createEGLSurface

 mDrawers.forEach { it.draw() }
                mEGLSurface?.setTimestamp(mCurTimestamp)
                mEGLSurface?.swapBuffers()

3 ) activity:
threadPool.execute(videoEncoder) //線程池運行子類

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容