Android音視頻處理之MediaExtractor

Android提供了一個MediaExtractor類,可以用來分離容器中的視頻track和音頻track,下面的例子展示了使用MediaExtractor和MediaMuxer來實現(xiàn)視頻的換音:

private void muxingAudioAndVideo() throws IOException {
    MediaMuxer mMediaMuxer = new MediaMuxer(mOutputVideoPath, 
                MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

    // 視頻的MediaExtractor
    MediaExtractor mVideoExtractor = new MediaExtractor();
    mVideoExtractor.setDataSource(mVideoPath);
    int videoTrackIndex = -1;
    for (int i = 0; i < mVideoExtractor.getTrackCount(); i++) {
        MediaFormat format = mVideoExtractor.getTrackFormat(i);
        if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
            mVideoExtractor.selectTrack(i);
            videoTrackIndex = mMediaMuxer.addTrack(format);
            break;
        }
    }

    // 音頻的MediaExtractor
    MediaExtractor mAudioExtractor = new MediaExtractor();
    mAudioExtractor.setDataSource(mAudioPath);
    int audioTrackIndex = -1;
    for (int i = 0; i < mAudioExtractor.getTrackCount(); i++) {
        MediaFormat format = mAudioExtractor.getTrackFormat(i);
        if (format.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
            mAudioExtractor.selectTrack(i);
            audioTrackIndex = mMediaMuxer.addTrack(format);
        }
    }

    // 添加完所有軌道后start
    mMediaMuxer.start();

    // 封裝視頻track
    if (-1 != videoTrackIndex) {
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        info.presentationTimeUs = 0;
        ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
        while (true) {
            int sampleSize = mVideoExtractor.readSampleData(buffer, 0);
            if (sampleSize < 0) {
                break;
            }

            info.offset = 0;
            info.size = sampleSize;
            info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
            info.presentationTimeUs = mVideoExtractor.getSampleTime();
            mMediaMuxer.writeSampleData(videoTrackIndex, buffer, info);

            mVideoExtractor.advance();
        }
    }

    // 封裝音頻track
    if (-1 != audioTrackIndex) {
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        info.presentationTimeUs = 0;
        ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
        while (true) {
            int sampleSize = mAudioExtractor.readSampleData(buffer, 0);
            if (sampleSize < 0) {
                break;
            }

            info.offset = 0;
            info.size = sampleSize;
            info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
            info.presentationTimeUs = mAudioExtractor.getSampleTime();
            mMediaMuxer.writeSampleData(audioTrackIndex, buffer, info);

            mAudioExtractor.advance();
        }
    }

    // 釋放MediaExtractor
    mVideoExtractor.release();
    mAudioExtractor.release();

    // 釋放MediaMuxer
    mMediaMuxer.stop();
    mMediaMuxer.release();
}

MediaExtractor的接口比較簡單,首先通過setDataSource()設(shè)置數(shù)據(jù)源,數(shù)據(jù)源可以是本地文件地址,也可以是網(wǎng)絡(luò)地址:

MediaExtractor mVideoExtractor = new MediaExtractor();
mVideoExtractor.setDataSource(mVideoPath);

然后可以通過getTrackFormat(int index)來獲取各個track的MediaFormat,通過MediaFormat來獲取track的詳細信息,如:MimeType、分辨率、采樣頻率、幀率等等:

for (int i = 0; i < mVideoExtractor.getTrackCount(); i++) {
    MediaFormat format = mVideoExtractor.getTrackFormat(i);
}

獲取到track的詳細信息后,通過selectTrack(int index)選擇指定的通道:

if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
    mVideoExtractor.selectTrack(i);
    break;
}

指定通道之后就可以從MediaExtractor中讀取數(shù)據(jù)了:

while (true) {
    int sampleSize = mVideoExtractor.readSampleData(buffer, 0);
    if (sampleSize < 0) {
        break;
    }
    // do something

    mVideoExtractor.advance();  // 移動到下一幀
}

在讀取結(jié)束之后,記得釋放資源:

mVideoExtractor.release();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容