Android音視頻開發(fā)之MediaCodec的使用教程
前言
獲取到音視頻軌道(編解碼格式),知道設(shè)備支持哪些編解碼器,下一步就是創(chuàng)建編解碼器去實現(xiàn)數(shù)據(jù)流的編解碼過程了。在Android開發(fā)中提供了實現(xiàn)音視頻編解碼工具MediaCodec,針對對應(yīng)音視頻解碼類型通過該類創(chuàng)建對應(yīng)解碼器就能實現(xiàn)對數(shù)據(jù)進(jìn)行解碼操作。
MediaCodec
MediaCodec所支持的數(shù)據(jù)類型:壓縮的音視頻數(shù)據(jù)、原始音頻數(shù)據(jù)和原始視頻數(shù)據(jù)。 首先show代碼,緊接著之前MediaExtactor提取資源,MediaCodecList遍歷支持格式,確認(rèn)設(shè)置支持該資源格式后通過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ù)據(jù)緩沖去,將數(shù)據(jù)填充到輸入緩沖區(qū)給到CodecCodec通過異步方式處理輸入緩沖區(qū)數(shù)據(jù)將處理好數(shù)據(jù)填充到輸出緩沖區(qū)- 客戶端從輸出緩沖區(qū)獲取到處理好的數(shù)據(jù)去消費,最后把緩沖區(qū)返還給
Codec
部分代碼實現(xiàn)
- dequeueInputBuffer:從輸入流隊列取數(shù)據(jù)進(jìn)行編碼操作
- getInputBuffers: 獲取需要編碼數(shù)據(jù)的輸入隊列 返回ByteBuffer數(shù)組
- queueInoutBuffer: 輸入流入隊列
- dequeueOutputBuffer: 從輸入隊列中取出編碼操作結(jié)果數(shù)據(jù)
- getOutPutBuffer: 獲取編解碼之后數(shù)據(jù)輸出隊列 返回一個ByterBuffer數(shù)組
- releaseOutPutBuffer: 處理完成釋放ByterBuffer數(shù)組
while (!isEnd && !isInterrupted()){
if (!mIsEOS) {
mIsEOS = dequeueInputBuffers();
}
isEnd = dequeueOutputBuffers();
}
//輸入緩沖區(qū)
private boolean dequeueInputBuffers() {
boolean isMediaEOS = false;
// 等待編碼器輸入緩沖區(qū)數(shù)據(jù)出隊
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)識符輸入到編碼器
mediaCodec.queueInputBuffer(inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isMediaEOS = true;
MediaLogUtils.printI(TAG + "end of stream");
} else {
// 輸入緩沖區(qū)數(shù)據(jù)入隊
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)。可通過reset()復(fù)位到Uninitialized
Configured: MediaCodec通過configure方法設(shè)置配置(編解碼器類型等)進(jìn)入到Configured狀態(tài)。
Error: MediaCodec發(fā)生異常情況下會進(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輸入緩存被移除隊列,MediaCodec就會進(jìn)入到Running狀態(tài)。
End-of-Stream:將end-of-stream標(biāo)記輸入InputBuffer隊列,MediaCodec就會進(jìn)入到 End-of-Stream狀態(tài),MediaCodec就不再接收InputBuffer,但不影響輸出隊列OutBuffer產(chǎn)出直到end-of-stream標(biāo)記輸出為止(輸入和輸出中間是有一定處理時間)。
接口簡介
createDecoderByType/createEncoderByType,創(chuàng)建解碼器/編碼器對象
createByCodecName,根據(jù)編解碼器名稱創(chuàng)建
configure,配置編解碼器配置
start,配置完成后需要執(zhí)行start完成配置
dequeueInputBuffer, 輸入隊列取數(shù)據(jù)編碼操作
queueInputBuffer,輸入入隊列
dequeueOutputBuffer,從輸出隊列取出編碼操作后的數(shù)據(jù)
releaseOutputBuffer,釋放輸出隊列釋放ByteBuffer數(shù)據(jù)
getInputBuffers,獲取需要編碼數(shù)據(jù)輸入流隊列,返回ByteBuffer數(shù)組
getOutputBuffers,獲取編解碼后數(shù)據(jù)輸出流隊列,返回ByteBuffer數(shù)組
flush,清空輸入和輸出端口
stop,終止decode/encode會話
release,釋放編解碼器資源
到此這篇關(guān)于Android音視頻開發(fā)之MediaCodec的使用教程的文章就介紹到這了,更多相關(guān)Android MediaCodec內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android ScrollView實現(xiàn)橫向和豎向拖動回彈效果
這篇文章主要為大家詳細(xì)介紹了Android ScrollView實現(xiàn)橫向和豎向拖動回彈效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-09-09
Android 自定義TextView實現(xiàn)滑動解鎖高亮文字
這篇文章主要介紹了Android 自定義TextView實現(xiàn)滑動解鎖高亮文字的相關(guān)資料,需要的朋友可以參考下2018-03-03
Android關(guān)于獲取時間的記錄(小結(jié))
這篇文章主要介紹了Android關(guān)于獲取時間的記錄(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Android項目開發(fā) 教你實現(xiàn)Periscope點贊效果
這篇文章主要為大家分享了Android項目開發(fā),一步一步教你實現(xiàn)Periscope點贊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-12-12

