Android音視頻處理之基於MediaCodec合並音視頻


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

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()設置數據源,數據源可以是本地文件地址,也可以是網絡地址:

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中讀取數據了:

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

    mVideoExtractor.advance();  // 移動到下一幀
}
在讀取結束之后,記得釋放資源:
mVideoExtractor.release();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM