• 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();
  • 相关阅读:
    宝塔面板连接数据库失败
    fastadmin上线部署中遇到访问路径问题
    宝塔部署时,出现“open_basedir restriction in effect”错误
    layui hint:upload is not a valid module
    thinkphp--控制器怎么分配变量到公共模板
    jquey click事件无效
    1.31 SVN代码版本控制
    8.1 性能优化简介
    5.31 Nginx最全面知识
    4.115 Spring的事务管理
  • 原文地址:https://www.cnblogs.com/renhui/p/12104179.html
Copyright © 2020-2023  润新知