Android多媒體框架--08:MediaExtractor和MediaMuxer介紹

"本文轉(zhuǎn)載自:[yanbixing123]的Android MultiMedia框架完全解析 - MediaExtractor和MediaMuxer介紹"

1.概述

??Android中Native層抽象出來MediaMuxer類和MediaExtractor類,MediaMuxer類主要用于將音頻和視頻數(shù)據(jù)進(jìn)行混合生成多媒體文件(如:mp4文件),而MediaExtractor則剛好相反,主要用于多媒體文件的音視頻數(shù)據(jù)的分離,即解封裝。

??而在文件播放中,首先需要做的就是解封裝, 所以在播放過程中,NuPlayer使用了這個(gè)MediaExtractor類,先來看看MediaExtractor在NuPlayer框架中所處的位置:

01.png

??下面給出一個(gè)Android App中使用MediaExtractor和MediaMuxer的例子,先來看看application中是如何使用這些接口的。

2.MediaExtractor

??(1)該類主要用于音視頻混合數(shù)據(jù)的分離,接口比較簡單,首先要通過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è)測試視頻,然后打印其軌道信息,軌道信息的遍歷可以通過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ì)象,如果你希望自己來創(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)通過 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;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容