• Android音频流+视频流合成视频及提取音频流、视频流数据


       本文介绍的是:针对Android 视频的合成以及 视频、音频的分离

            1、MediaMuxer+MediaCodc合成视频

            2、MediaExtractor分离视频流以及音频流

       首先针对视频:一个视频是可以有多个视轨、多个音轨  这些轨道都是可以放入音频数据、视频数据的   

       经测试一个轨道也是可以放来源不同的视频流或者音频流

       1、MediaMuxer+MediaCodc合成视频:

              

       以上是经典的音视频流合成视频的图

       使用MediaCodec的基本顺序:

           1、int addTrack(@NonNull MediaFormat format)

           2、start()    一旦开始将不能再addTrack

           3、void writeSampleData(int trackIndex, @NonNull ByteBuffer byteBuf,@NonNull BufferInfo bufferInfo)

           4、void stop() 

           5、void release()

       以上顺序不可颠倒 否则会报IllegalStateException

       通常视频的提供者可以是Camera的预览数据,也可以是录屏的视频流或者是网络实时的视频流;音频数据是麦克风采集的声音或者其他的音频流,这些数据都是字节数组

       当我们拿到这些音视频数据  第一步要将该数据写到指定的轨道上,此时要先去执行MediaMuxer的addTrack()方法添加对应轨道

     例如音频数据
    MediaFormat newFormat = mAudioEncoder.getOutputFormat(); mAudioTrackIndex = mMuxer.addTrack(newFormat);

    当音频以及视频的轨道全部添加完成之后再去调用start()方法开启混流器此时可以往混流器里写入数据,此步骤涉及音频线程以及视频线程和混流器线程的控制,可自由发挥

    while(true) {
        int encoderStatus = mAudioEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);//获取输出缓冲区的index
        if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
            ...
        } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            encoderOutputBuffers = mAudioEncoder.getOutputBuffers();
        } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            MediaFormat newFormat = mAudioEncoder.getOutputFormat();
            mAudioTrackIndex = mMuxer.addTrack(newFormat);//添加轨道
        } else if (encoderStatus < 0) {
            ...
        } else {
            ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];//真实的音频或视频数据
            ...//组织mBufferInfo的数据  包括有size offset PTS时间戳
            if (mBufferInfo.size != 0) {
                mMuxer.writeSampleData(mAudioTrackIndex, encodedData, mBufferInfo);//写入轨道
            }
            mAudioEncoder.releaseOutputBuffer(encoderStatus, false);//释放该输出缓冲区
            if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                break;        
            }
        }

    写完数据之后 调用stop  release 释放资源

    2、视频  音频数据、视频数据的的分离提取 首先获取所有轨道数量  然后遍历这些轨道  找出我们感兴趣的轨道  直接获取轨道里的音视频数据

    MediaExtractor extractor = new MediaExtractor();
     extractor.setDataSource(...);
     int numTracks = extractor.getTrackCount();
     for (int i = 0; i < numTracks; ++i) {
       MediaFormat format = extractor.getTrackFormat(i);
       String mime = format.getString(MediaFormat.KEY_MIME);
       if (weAreInterestedInThisTrack) {
         extractor.selectTrack(i);
       }
     }
     ByteBuffer inputBuffer = ByteBuffer.allocate(...)
     while (extractor.readSampleData(inputBuffer, ...) >= 0) {
       int trackIndex = extractor.getSampleTrackIndex();
       long presentationTimeUs = extractor.getSampleTime();
       ...
       extractor.advance();
     }
     extractor.release();
     extractor = null;

    实际项目中这些操作都是在子线程中执行的

  • 相关阅读:
    Fatal error: Maximum execution time of 30 seconds exceeded in
    常见变量命名规则
    Rust使用国内镜像安装依赖
    flutter使用国内镜像源
    7个每天晚上应该坚持的好习惯
    网络数据传输格式的思考
    Deepin安装 ruby 包管理工具 RVM(适用于 Debian 系列)
    C语言数据类型关键字
    win10 powershell禁止运行脚本解决
    Linux 查看系统相关信息(系统发型版本及内核版本等)
  • 原文地址:https://www.cnblogs.com/bimingcong/p/13688407.html
Copyright © 2020-2023  润新知