Android音視頻開發(fā)之MediaCodec的使用教程
前言
獲取到音視頻軌道(編解碼格式),知道設(shè)備支持哪些編解碼器,下一步就是創(chuàng)建編解碼器去實(shí)現(xiàn)數(shù)據(jù)流的編解碼過(guò)程了。在Android
開發(fā)中提供了實(shí)現(xiàn)音視頻編解碼工具MediaCodec
,針對(duì)對(duì)應(yīng)音視頻解碼類型通過(guò)該類創(chuàng)建對(duì)應(yīng)解碼器就能實(shí)現(xiàn)對(duì)數(shù)據(jù)進(jìn)行解碼操作。
MediaCodec
MediaCodec
所支持的數(shù)據(jù)類型:壓縮的音視頻數(shù)據(jù)、原始音頻數(shù)據(jù)和原始視頻數(shù)據(jù)。 首先show代碼,緊接著之前MediaExtactor
提取資源,MediaCodecList
遍歷支持格式,確認(rèn)設(shè)置支持該資源格式后通過(guò)MediaCodec
創(chuàng)建解碼器(這里是做視頻解碼播放)。
// 加載資源 extractor.setDataSource(path); // 獲取視頻軌道 int trackIndex = getTrackIndex(extractor,"video/"); // 獲取視頻軌道參數(shù) MediaFormatInfo mediaFormatInfo = MediaFormatInfo.buildUpVideoMediaFormatInfo(extractor.getTrackFormat(trackIndex)); // 選取上述的視頻軌道 extractor.selectTrack(trackIndex); MediaCodecInfo mediaCodecInfo = CodecInfoInstance.getInstance().selectDeCodec(mediaFormatInfo.getMime()); // 判斷設(shè)備是否支持該視頻解碼,創(chuàng)建視頻編碼器 if(mediaCodecInfo != null){ mediaCodec = MediaCodec.createDecoderByType(mediaFormatInfo.getMime()); mediaCodec.configure(mediaFormatInfo.getMediaFormat(),surface,null,0); }
編解碼流程
MediaCodec
的功能其實(shí)很簡(jiǎn)單通過(guò)一個(gè)數(shù)據(jù)緩沖去,將數(shù)據(jù)填充到輸入緩沖區(qū)給到Codec
Codec
通過(guò)異步方式處理輸入緩沖區(qū)數(shù)據(jù)將處理好數(shù)據(jù)填充到輸出緩沖區(qū)- 客戶端從輸出緩沖區(qū)獲取到處理好的數(shù)據(jù)去消費(fèi),最后把緩沖區(qū)返還給
Codec
部分代碼實(shí)現(xiàn)
- dequeueInputBuffer:從輸入流隊(duì)列取數(shù)據(jù)進(jìn)行編碼操作
- getInputBuffers: 獲取需要編碼數(shù)據(jù)的輸入隊(duì)列 返回ByteBuffer數(shù)組
- queueInoutBuffer: 輸入流入隊(duì)列
- dequeueOutputBuffer: 從輸入隊(duì)列中取出編碼操作結(jié)果數(shù)據(jù)
- getOutPutBuffer: 獲取編解碼之后數(shù)據(jù)輸出隊(duì)列 返回一個(gè)ByterBuffer數(shù)組
- releaseOutPutBuffer: 處理完成釋放ByterBuffer數(shù)組
while (!isEnd && !isInterrupted()){ if (!mIsEOS) { mIsEOS = dequeueInputBuffers(); } isEnd = dequeueOutputBuffers(); } //輸入緩沖區(qū) private boolean dequeueInputBuffers() { boolean isMediaEOS = false; // 等待編碼器輸入緩沖區(qū)數(shù)據(jù)出隊(duì) int inputBufferId = mediaCodec.dequeueInputBuffer(TIMEOUT_US); if (inputBufferId >= 0) { // 獲取緩存區(qū)數(shù)據(jù) ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferId); // 讀取數(shù)據(jù) 返回值sampleSize大于0表示還有數(shù)據(jù),否則表示結(jié)束 int sampleSize = extractor.readSampleData(inputBuffer, 0); if (sampleSize < 0) { // 數(shù)據(jù)末尾 必須再次調(diào)用queueInputBuffer使用BUFFER_FLAG_END_OF_STREAM標(biāo)識(shí)符輸入到編碼器 mediaCodec.queueInputBuffer(inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); isMediaEOS = true; MediaLogUtils.printI(TAG + "end of stream"); } else { // 輸入緩沖區(qū)數(shù)據(jù)入隊(duì) mediaCodec.queueInputBuffer(inputBufferId, 0, sampleSize, extractor.getSampleTime(), 0); extractor.advance(); } } return isMediaEOS; } //輸出緩沖區(qū) private synchronized boolean dequeueOutputBuffers() { MediaCodec.BufferInfo outBufferInfo = new MediaCodec.BufferInfo(); //輸出緩沖區(qū) int outputBufferId = mediaCodec.dequeueOutputBuffer(outBufferInfo, TIMEOUT_US); switch (outputBufferId) { case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: MediaLogUtils.printI(TAG+"INFO_OUTPUT_FORMAT_CHANGED"); break; case MediaCodec.INFO_TRY_AGAIN_LATER: MediaLogUtils.printI(TAG+ "INFO_TRY_AGAIN_LATER"); break; case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: MediaLogUtils.printI(TAG+ "INFO_OUTPUT_BUFFERS_CHANGED"); break; default: // 延遲解碼 decodeDelay(outBufferInfo, mStartMs); // 釋放輸出緩沖區(qū)數(shù)據(jù) render為true渲染到surface上 mediaCodec.releaseOutputBuffer(outputBufferId, true); break; } // 結(jié)尾 if ((outBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { MediaLogUtils.printI(TAG+"buffer stream end"); return true; } return false; }
生命周期
MediaCodec
生命周期分為三種狀態(tài):Stopped
、Executing
、Released
- Stopped具有三種子狀態(tài):Uninitialized、Configured、Error
- Executing具有三種子狀態(tài):Flushed、Running、End-of-Stream
Stopped
Uninitialized: Uninitialized
是MediaCodec
創(chuàng)建后初始狀態(tài)??赏ㄟ^(guò)reset()
復(fù)位到Uninitialized
Configured: MediaCodec
通過(guò)configure
方法設(shè)置配置(編解碼器類型等)進(jìn)入到Configured
狀態(tài)。
Error: MediaCodec
發(fā)生異常情況下會(huì)進(jìn)入Error
狀態(tài)。
Executing
Flush:MediaCodec
調(diào)用start
方法后進(jìn)入Flushed
,MediaCodec
就具備所有緩存能力。若可以在Executing
,調(diào)用flush()
回到Flushed
狀態(tài)。
Running: 當(dāng)?shù)谝淮?code>InputBuffer輸入緩存被移除隊(duì)列,MediaCodec
就會(huì)進(jìn)入到Running
狀態(tài)。
End-of-Stream:將end-of-stream
標(biāo)記輸入InputBuffer
隊(duì)列,MediaCodec
就會(huì)進(jìn)入到 End-of-Stream
狀態(tài),MediaCodec
就不再接收InputBuffer
,但不影響輸出隊(duì)列OutBuffer
產(chǎn)出直到end-of-stream
標(biāo)記輸出為止(輸入和輸出中間是有一定處理時(shí)間)。
接口簡(jiǎn)介
createDecoderByType/createEncoderByType,創(chuàng)建解碼器/編碼器對(duì)象
createByCodecName,根據(jù)編解碼器名稱創(chuàng)建
configure,配置編解碼器配置
start,配置完成后需要執(zhí)行start完成配置
dequeueInputBuffer, 輸入隊(duì)列取數(shù)據(jù)編碼操作
queueInputBuffer,輸入入隊(duì)列
dequeueOutputBuffer,從輸出隊(duì)列取出編碼操作后的數(shù)據(jù)
releaseOutputBuffer,釋放輸出隊(duì)列釋放ByteBuffer數(shù)據(jù)
getInputBuffers,獲取需要編碼數(shù)據(jù)輸入流隊(duì)列,返回ByteBuffer數(shù)組
getOutputBuffers,獲取編解碼后數(shù)據(jù)輸出流隊(duì)列,返回ByteBuffer數(shù)組
flush,清空輸入和輸出端口
stop,終止decode/encode會(huì)話
release,釋放編解碼器資源
到此這篇關(guān)于Android音視頻開發(fā)之MediaCodec的使用教程的文章就介紹到這了,更多相關(guān)Android MediaCodec內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義View實(shí)現(xiàn)分段選擇按鈕的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android自定義View實(shí)現(xiàn)分段選擇按鈕的實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Android評(píng)分控件RatingBar使用實(shí)例解析
這篇文章主要為大家詳細(xì)介紹了Android評(píng)分控件RatingBar使用實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Android中使用Expandablelistview實(shí)現(xiàn)微信通訊錄界面
本文主要介紹了Android中使用Expandablelistview實(shí)現(xiàn)微信通訊錄界面(完善防微信APP)的方法,具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2016-12-12Android開發(fā)之HTTP訪問(wèn)網(wǎng)絡(luò)
這篇文章主要介紹了Android開發(fā)之HTTP訪問(wèn)網(wǎng)絡(luò)的相關(guān)資料,需要的朋友可以參考下2016-07-07Android實(shí)現(xiàn)圖片疊加效果的兩種方法
這篇文章主要介紹了Android實(shí)現(xiàn)圖片疊加效果的兩種方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)圖片疊加效果的兩種操作方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-08-08Android自定義Spinner下拉列表(使用ArrayAdapter和自定義Adapter實(shí)現(xiàn))
這篇文章主要介紹了Android自定義Spinner下拉列表(使用ArrayAdapter和自定義Adapter實(shí)現(xiàn))的相關(guān)資料,需要的朋友可以參考下2015-10-10