"本文轉(zhuǎn)載自:[yanbixing123]的Android MultiMedia框架完全解析 - MediaExtractor和MediaMuxer介紹"
1.概述
??Android中Native層抽象出來(lái)MediaMuxer類和MediaExtractor類析砸,MediaMuxer類主要用于將音頻和視頻數(shù)據(jù)進(jìn)行混合生成多媒體文件(如:mp4文件),而MediaExtractor則剛好相反,主要用于多媒體文件的音視頻數(shù)據(jù)的分離,即解封裝。
??而在文件播放中糠溜,首先需要做的就是解封裝躬贡, 所以在播放過(guò)程中劫谅,NuPlayer使用了這個(gè)MediaExtractor類氢妈,先來(lái)看看MediaExtractor在NuPlayer框架中所處的位置:
??下面給出一個(gè)Android App中使用MediaExtractor和MediaMuxer的例子粹污,先來(lái)看看application中是如何使用這些接口的。
2.MediaExtractor
??(1)該類主要用于音視頻混合數(shù)據(jù)的分離首量,接口比較簡(jiǎn)單壮吩,首先要通過(guò)setDataSource(String path)函數(shù)設(shè)置數(shù)據(jù)源,數(shù)據(jù)源可以是本地文件地址加缘,也可以使用HTTP協(xié)議的網(wǎng)絡(luò)碼流地址鸭叙。
extractor = new MediaExtractor();
extractor.setDataSource("/sdcard/test.mp4");
dumpFormat(extractor);
private void dumpFormat(MediaExtractor extractor) {
int count = extractor.getTrackCount();
Log.i(TAG, "playVideo: track count: " + count);
for (int i = 0; i < count; i++) {
MediaFormat format = extractor.getTrackFormat(i);
Log.i(TAG, "playVideo: track " + i + ":" + getTrackInfo(format));
String mime = format.getString(MediaFormat.KEY_MIME);
if(mime.startsWith("Video/")){
videoTrackIndex = i;
} else if(mime.startsWith("audio/")){
audioTrackIndex = 0;
}
}
}
打開sdcard下的一個(gè)測(cè)試視頻,然后打印其軌道信息生百,軌道信息的遍歷可以通過(guò)MediaExtractor的 getTrackCount 和 getTrackFormat 配合完成递雀,如:MimeType,分辨率蚀浆、編碼格式缀程、碼率、幀率等等市俊。
??(2)獲取到媒體文件的詳細(xì)信息之后杨凑,就可以選擇指定的通道,并分離和讀取數(shù)據(jù)了:
mMediaExtractor.selectTrack(videoTrackIndex);
while(true) {
int sampleSize = mMediaExtractor.readSampleData(buffer, 0);
if(sampleSize < 0){
break;
}
mMediaExtractor.advance(); //移動(dòng)到下一幀
}
mMediaExtractor.release(); //讀取結(jié)束后摆昧,要記得釋放資源
3.MediaMuxer
??(1)該類主要用于將音頻和視頻進(jìn)行混合生成多媒體文件撩满,創(chuàng)建該類對(duì)象,需要傳入輸出的文件位置以及格式绅你,構(gòu)造函數(shù)如下:
public MediaMuxer(String path, int format);
創(chuàng)建對(duì)象之后伺帘,一個(gè)比較重要的操作就是addTrack(),添加數(shù)據(jù)通道忌锯,該函數(shù)需要傳入MediaFormat對(duì)象伪嫁,MediaFormat即媒體格式類,用于描述媒體的格式參數(shù)偶垮,如視頻幀率张咳、音頻采樣率等。
??(2)在本示例中似舵,可以直接使用MediaExtractor.getTrackFormat()解析得到的MediaFormat對(duì)象脚猾,如果你希望自己來(lái)創(chuàng)建這個(gè)MediaFormat對(duì)象的話,可以使用該類的如下靜態(tài)方法創(chuàng)建:
MediaFormat format = MediaFormat.createVideoFormat("video/avc", 320, 240);
注意砚哗,如果手動(dòng)創(chuàng)建MediaFormat對(duì)象的話龙助,一定要記得設(shè)置"csd-0"和"csd-1"這兩個(gè)參數(shù):
byte[] csd0 = {x, x, x, x, x, ,x ...}
byte[] csd1 = {x, x, x, x, x, ,x ...}
format.setByteBuffer("csd-0", ByteBuffer.wrap(csd0));
format.setByteBuffer("csd-1", ByteBuffer.wrap(csd1));
至于"csd-0"和"csd-1"是什么,對(duì)于H264視頻的話蛛芥,它對(duì)應(yīng)的是sps和pps泌参,對(duì)于AAC音頻的話幢痘,對(duì)應(yīng)的是ADTS宣赔,它一般存在于編碼器生成的IDR幀之中。
??(3)通過(guò) addTrack() 添加了數(shù)據(jù)通道之后答姥,記錄下函數(shù)返回的 trackIndex漓糙,然后就可以調(diào)用 MediaMuxer.writeSampleData() 愉快地向mp4文件中寫入數(shù)據(jù)了铣缠。
mMediaMuxer.writeSampleData(mVideoTrackIndex,buffer,info);
這里需要注意的就是writeSampleData函數(shù)的最后一個(gè)參數(shù)是一個(gè)BufferInfo對(duì)象,你必須認(rèn)真地填入“正確”的值:
BufferInfo info = new BufferInfo();
info.offset = 0;
info.size = sampleSize;
info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
info.presentationTimeUs = timestamp;
info.size:必須填入數(shù)據(jù)的大欣デ荨蝗蛙;
info.flags:需要給出是否為同步幀/關(guān)鍵幀;
info.presentationTimeUs:必須給出正確的時(shí)間戳醉鳖,注意單位是 us捡硅,例如,對(duì)于幀率為 x f/s 的視頻而言盗棵,時(shí)間戳的間隔就是 1000/x ms壮韭。
??(4)完成后需要關(guān)閉以及釋放資源
mMediaMuxer.stop();
mMediaMuxer.releaser();
4.demo
??完整的demo代碼如下:
protected boolean process() throws IOException {
mMediaExtractor = new MediaExtractor();
mMediaExtractor.setDataSource(SDCARD_PATH+"/input.mp4");
int mVideoTrackIndex = -1;
int framerate = 0;
for(int i = 0; i < mMediaExtractor.getTrackCount(); i++) {
MediaFormat format = mMediaExtractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if(!mime.startsWith("video/")) {
continue;
}
framerate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
mMediaExtractor.selectTrack(i);
mMediaMuxer = new MediaMuxer(SDCARD_PATH+"/ouput.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4);
mVideoTrackIndex = mMediaMuxer.addTrack(format);
mMediaMuxer.start();
}
if(mMediaMuxer == null) {
return false;
}
BufferInfo info = new BufferInfo();
info.presentationTimeUs = 0;
ByteBuffer buffer = ByteBuffer.allocate(500*1024);
while(true) {
int sampleSize = mMediaExtractor.readSampleData(buffer, 0);
if(sampleSize < 0) {
break;
}
mMediaExtractor.advance();
info.offset = 0;
info.size = sampleSize;
info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
info.presentationTimeUs += 1000*1000/framerate;
mMediaMuxer.writeSampleData(mVideoTrackIndex,buffer,info);
}
mMediaExtractor.release();
mMediaMuxer.stop();
mMediaMuxer.release();
return true;
}