「音影片直播技術」Android下H264解碼
- 2020 年 4 月 2 日
- 筆記

前言
上一篇文章中我介紹了如何使用MediaCodec編碼,今天我們再來分析一下如何通過 MediaCodec 進行解碼。
為了講解的方便,我們引入了 MediaExtractor 類。它用於打開MP4等媒體文件,並從中抽取出音影片數據。
打開媒體文件
MediaExtractor,音影片數據分離器。每種媒體文件如MP4, FLV, MOOV等都是一種容器,裡邊存放了音頻數據和影片數據。MediaExtractor的作用就是根據容器協議打開容器,並讀取其中的音頻或影片數據。
在容器文件(MP4)中,音頻數據與影片數據是以軌道(�track)的概念存放的。取的是兩條軌道永遠不相交的意思,也就指明音頻數據與影片數據是分別存儲的。
我們使用MediaExtractor類打開媒體文件,它的使用非常簡單,步驟如下:
1. 創建一個MediaExtractor對象。 2. 將媒體文件設置給MediaExtractor對象。 3. 選定要處理的軌道。
我們先來看一下示例程式碼吧
...... MediaExtractor extractor = null; try { extractor = new MediaExtractor(); extractor.setDataSource(sourceFile.toString()); int trackIndex = selectTrack(extractor); //根據關鍵字獲取影片Track if (trackIndex < 0) { throw new RuntimeException("No video track found in " + mSourceFile); } extractor.selectTrack(trackIndex); //選定影片軌 ...... } finally { if (extractor != null) { extractor.release(); } } ......
通過上面的步驟我們就選好了要處理的影片軌。下面我們來創建解碼器。
創建解碼器
在創建解碼器之前,需要先通過 MediaExtractor 獲取到要處理的影片軌的媒體格式(因為媒體格式中包括了 CSD-0/CSD-1 資訊,這個資訊對於解碼非常重要)。然後通過媒體格式的 mime 資訊創建解碼器。
CSD-0/CSD-1 指的就是 H264中的 PPS 和 SPS。
另外,在配置解碼器時,可以給它傳入一個 Surface,這樣解碼器解碼後,就可以直接將影像幀渲染到 Surface里了。程式碼如下:
...... MediaFormat format = extractor.getTrackFormat(trackIndex); // Create a MediaCodec decoder, and configure it with the MediaFormat from the // extractor. It's very important to use the format from the extractor because // it contains a copy of the CSD-0/CSD-1 codec-specific data chunks. String mime = format.getString(MediaFormat.KEY_MIME); decoder = MediaCodec.createDecoderByType(mime); decoder.configure(format, mOutputSurface, null, 0); decoder.start(); ......
解碼
解碼按如下步驟進行:
1. 從InputBuffer隊列中取出一個空閑的InputBuffer。 2. 通過 MediaExtractor 對象從影片軌道中取出H264數據存到InputBuffer中。 3. 將InputBuffer放到InputBuffer隊列中。此時需要解碼的數據已經送入了解碼器。 4. 從OutputBuffer隊列中取OutputBuffer,如果能取到說明已經有解碼好的數據了。 5. 最後調用releaseOutputBuffer釋放OutputBuffer。此時OutputBuffer中的數據將被轉成紋理進行渲染。
示例程式碼如下:
...... while (!outputDone){ ...... // Feed more data to the decoder. if (!inputDone) { int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); if (inputBufIndex >= 0) { ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex]; int chunkSize = extractor.readSampleData(inputBuf, 0); ...... long presentationTimeUs = extractor.getSampleTime(); decoder.queueInputBuffer(inputBufIndex, 0, chunkSize, presentationTimeUs, 0 /*flags*/); extractor.advance(); //處理下一幀 ...... } } if (!outputDone) { int decoderStatus = decoder.dequeueOutputBuffer( mBufferInfo, TIMEOUT_USEC); ...... if(decoderStatus > 0) { // As soon as we call releaseOutputBuffer, the buffer will be forwarded // to SurfaceTexture to convert to a texture. decoder.releaseOutputBuffer(decoderStatus, doRender); //解碼數據 } ...... } } ......
小結
通過上面的介紹我們知道通過MediaCodec進行解碼也非常的簡單,主要是三大步:
- 創建影片解碼器。
- 獲取數據。今天我們是通過 MediaExtrator從文件中獲取的。如果是直播系統,則是直接從網上獲取數據。
- 在循環中不停的向解碼器喂數據,並從解碼器中取出解碼後的數據。