• 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();
  • 相关阅读:
    010 --- 第14章 观察者模式
    009 --- 第13章 建造者模式
    008 --- 第12章 外观模式
    007 --- 第10章 模板方法模式
    006 --- 第9章 原型模式
    redis lua 中keys[1] 和argv[1] 的理解
    redis 入门总结
    mysql 8.0 特性简单总结
    MySql事务隔离级别 浅见
    浅谈Java中的String、StringBuffer、StringBuilder
  • 原文地址:https://www.cnblogs.com/renhui/p/12104179.html
Copyright © 2020-2023  润新知