*因爲工做須要,須要利用MediaCodec實現Playback及Transcode等功能,故在學習過程當中翻譯了Google官方的MediaCodec API文檔,因爲做者水平限制,文中不免有錯誤和不恰當之處,望批評指正。數組
*轉載請註明出處:http://www.cnblogs.com/roger-yu/微信
Android MediaCodec能夠訪問底層的media codecs,咱們很容易利用MediaCodec來構建encoder或decoder來實現音視頻編碼和音視頻解碼的功能。異步
簡單點兒理解,一個Codec(能夠認爲是一個MediaCodec的實例對象)就至關於一個「處理器」:處理輸入數據,併產生輸出數據。ide
以下圖所示,每個Codec都維護着一組 input buffers 和 output buffers。開始時Codec擁有全部buffers的全部權,Client(能夠暫且理解爲MediaCodec以外寫的程序)沒法向 input buffer 寫入數據,也沒法讀取 output buffer 中的數據。數據處理開始後,Client向Codec請求一個(同步模式)或者接收到(異步模式)一個空的 input buffer,將要處理的數據寫入到該buffer中,而後提交給Codec處理,Codec處理完數據後會將處理的結果寫入到一個空的 output buffer 中,以後Client就能夠請求或接收到這個存有結果的 output buffer,Client對結果使用完畢後就能夠release這個output buffer,Codec就能夠再次使用這個buffer,如此過程完成整個的處理。函數
Android MediaCodec主要有3種數據處理的方式:學習
1. 使用Buffers的異步處理方式(Asynchronous Processing using Buffers)ui
2. 使用Buffers的同步處理方式(Synchronous Processing using Buffers)編碼
3. 使用Buffer數組的同步處理方式(Synchronous Processing using Buffer Arrays (deprecated))spa
依據Android版本不一樣能夠採用不一樣的方式,以下圖:翻譯
目前最經常使用的是前兩種模式,故接下來重點講解。
基本處理流程:
注意:
1. 在調用configure配置MediaCodec以前須要爲MediaCodec設置callback,須要實現MediaCodec.Callback接口並重寫其中的方法:onInputBufferAvailable 、onOutputBufferAvailable、onOutputFormatChanged、onError,工做時MediaCodec會利用 這四個回調方法來自動的通知Client何時input buffer有效,何時output buffer有效,何時media format發生變化,何時運行出錯,也是在這些方法中Client向Codec送入數據並獲得處理的結果及獲取Codec的一些其餘信息。
2. 異步模式下MediaCodec的狀態轉換會有些許不一樣,在調用start方法後會直接進入Running狀態;
異步處理模式下,調用MediaCodec.start()後Codec 當即進入Running子狀態,經過設置的callback中的回調方法onInputBufferAvailable()會自動收到可用(empty)的input buffer,此時能夠根據input buffer id調用getInputBuffer(id)獲得這個buffer,並將須要的處理的數據寫入該buffer中,最後調用queueInputBuffer(id, ...)將該buffer提交給Codec處理;Codec每處理完一幀數據就會將處理結果寫入一個空的output buffer,並經過回調函數
onOutputBufferAvailable
來通知Client來讀取結果,Client能夠根據output bufffer id調用getOutputBuffer(id)獲取該buffer並讀取結果,完畢後能夠調用releaseOutputBuffer(id, ...)釋放該buffer給Codec再次使用。
典型的代碼設計:
1 MediaCodec codec = MediaCodec.createByCodecName(name); 2 MediaFormat mOutputFormat; // member variable 3 // 異步模式下須要在configure以前設置callback 4 codec.setCallback(new MediaCodec.Callback() { 5 6 /** 7 * 在onInputBufferAvailable回調方法中,MediaCodec會通知何時input 8 * buffer有效,根據buffer id,調用getInputBuffer(id)能夠得到這個buffer, 9 * 此時就能夠向這個buffer中寫入數據,最後調用queueInputBuffer(id, …)提交 10 * 給MediaCodec處理。 11 */ 12 @Override 13 void onInputBufferAvailable(MediaCodec mc, int inputBufferId) { 14 ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId); 15 // fill inputBuffer with valid data 16 … 17 codec.queueInputBuffer(inputBufferId, …); 18 } 19 20 /** 21 * 在onOutputBufferAvailable回調方法中,MediaCodec會通知何時output 22 * buffer有效,根據buffer id,調用getOutputBuffer(id)能夠得到這個buffer, 23 * 此時就能夠讀取這個buffer中的數據,最後調用releaseOutputBuffer(id, …)釋放 24 * 給MediaCodec再次使用。 25 */ 26 27 @Override 28 void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) { 29 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); 30 MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A 31 // bufferFormat is equivalent to mOutputFormat 32 // outputBuffer is ready to be processed or rendered. 33 … 34 codec.releaseOutputBuffer(outputBufferId, …); 35 } 36 /** 37 * 當MediaCodec的output format發生變化是會回調該方法,通常在start以後都會首先回調該方法 38 */ 39 @Override 40 void onOutputFormatChanged(MediaCodec mc, MediaFormat format) { 41 // Subsequent data will conform to new format. 42 // Can ignore if using getOutputFormat(outputBufferId) 43 mOutputFormat = format; // option B 44 } 45 /** 46 * MediaCodec運行發生錯誤時會回調該方法 47 */ 48 @Override 49 void onError(…) { 50 … 51 } 52 }); 53 codec.configure(format, …); 54 mOutputFormat = codec.getOutputFormat(); // option B 55 codec.start(); // start 以後MediaCodec當即進入Running子狀態,並會回調callback中的方法 56 // wait for processing to complete 57 codec.stop(); // stop後MediaCodec進入Uninitialized子狀態 58 codec.release(); //使用完畢要釋放掉MediaCdoec佔用的資源
基本處理流程:
同步模式下,MediaCodec調用start()方法後會進入Flushed子狀態,而後第一次調用dequeueInputBuffer()後纔會進入Running子狀態。
這種模式下,程序須要在一個無限循環中經過調用dequeueInputBuffer(...)和dequeueOutputBuffer(...)來不斷地請求Codec是否有可用的input buffer 或 output buffer:
> 若是有可用的input buffer:根據獲得的buffer id,調用getInputBuffer(id)獲取該buffer,並向其中寫入待處理的數據,而後調用queueInputBuffer(id,..)提交到Codec進行處理
> 若是有可用的output buffer: 根據獲得的buffer id,調用getOutputBuffer(id)獲取該buffer,讀取其中的處理結果,而後調用releaseOutputBuffer(id,..)釋放該buffer供Codec再次使用
> 處理過程當中還可能受到一些特殊標記的buffer id,好比MediaCodec.INFO_OUTPUT_FORMAT_CHANGED,要做出恰當處理
典型的代碼設計:
1 MediaCodec codec = MediaCodec.createByCodecName(name); 2 codec.configure(format, ...); 3 MediaFormat outputFormat = codec.getOutputFormat(); // option B 4 codec.start(); // start()方法後會進入Flushed子狀態 5 /** 6 * 在一個無限循環中不斷地請求Codec是否有可用的input buffer 或 output buffer 7 */ 8 for (;;) { 9 int inputBufferId = codec.dequeueInputBuffer(timeoutUs); // 請求是否有可用的input buffer 10 if (inputBufferId >= 0) { 11 ByteBuffer inputBuffer = codec.getInputBuffer(...); // 獲取input buffer 12 // fill inputBuffer with valid data 13 ... 14 codec.queueInputBuffer(inputBufferId, ...); // 提交數據給Codec 15 } 16 int outputBufferId = codec.dequeueOutputBuffer(...); // 請求是否有可用的output buffer 17 if (outputBufferId >= 0) { 18 ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId); // 獲取output buffer 19 MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A 20 // bufferFormat is identical to outputFormat 21 // outputBuffer is ready to be processed or rendered. 22 ... 23 codec.releaseOutputBuffer(outputBufferId, ...); // 釋放output buffer供Codec再次使用 24 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 25 // Subsequent data will conform to new format. 26 // Can ignore if using getOutputFormat(outputBufferId) 27 outputFormat = codec.getOutputFormat(); // option B 28 } 29 } 30 codec.stop(); 31 codec.release(); //釋放資源
》異步模式下經過回調函數來自動的傳遞可用的input buffer 或 output buffer
》同步模式下須要經過dequeueInputBuffer(...)或dequeueOutputBuffer(...)來請求獲取可用的input buffer 或 output buffer
微信掃一掃,關注玖零日記,獲取更多相關資訊及源碼 -- 雖無面朝大海,依舊春暖花開