继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

MediaCodec 编码后的数据使用 FFmpeg 进行封装

慕哥9229398
关注TA
已关注
手记 1269
粉丝 199
获赞 913

由于使用 MediaMuxer 进行文件封装不支持边转码边分块,所以选择通过使用 FFmpeg muxer 进行文件封装,  在封装过程中完成文件的分块。

FFmpeg 音视频复用器(Muxer)

音视频封装 - 将视频压缩数据(例如H.264)和音频压缩数据(例如AAC)合并到一个封装格式数据(例如MP4)中去。如图所示:

webp

音视频复用.png

参考雷霄华的博客,整理出 FFmpeg 封装音视频的流程:

webp

FFmpeg muxer流程.png

  1. 创建 AVFormatContext 结构体

  2. 调用 avformat_alloc_output_context2() 方法初始化 AVFormatContext 关于输出文件相关属性

  3. avio_open2() - 打开输出文件

    AVDictionary *options = NULL;// 指定分块文件的大小av_dict_set(&options, "BLK_BUF_TAG", buff, 0);// 回调函数结构体AVIOInterruptCB interrupt_callBack = (AVIOInterruptCB *)malloc(sizeof(AVIOInterruptCB));
    memset(interrupt_callBack, 0, sizeof(*interrupt_callBack));// 指定回调函数,处理分块文件时调用interrupt_callBack->callback_blk = ffmuxer_notify_blk_callback;
    interrupt_callBack->opaque = NULL;
    interrupt_callBack->handle = (void *)pMuxer;
    
    res = avio_open2(&fmtCtx->pb, out, AVIO_FLAG_BLK_WRITE, interrupt_callBack, &options);
  4. 通过 avformat_new_stream 创建 video & audio stream 并设置相关配置信息

  5. avformat_write_header() - 写入文件头

  6. av_interleaved_write_frame() - 写入一个 AVPacket 到输出文件

  7. av_write_trail() - 写入文件尾

关于 FFmpeg 复用音视频的流程介绍到此,总结一下重点就是:初始化相关结构体,配置属性,写入压缩数据,释放。 下面介绍在 MediaCodec 中如何调用:

因为 MediaCodec 调用 FFmpeg 相关代码是通过 jni 调用,这里不展开介绍。下面的 native*** 方法均为 FFmpeg 对应的原生方法

  1. nativeMuxerOpen

    初始化 FFmpeg muxer: 当 video&audio 编码器的输出 MediaFormat 均发生变化,说明编码器即将输出数据。此时即可初始化 FFmpeg muxer(执行 FFmpeg 复用流程中的 1,2,3步骤)

  2. nativeAddAudioTrack & nativeAddVideoTrack

    添加 video & audio stream: 编码器输出压缩数据,且 flag 为 BUFFER_FLAG_CODEC_CONFIG(即配置信息)时,

    // 添加 video streamAVStream *stream = avformat_new_stream(fmtCtx, avcodec_find_encoder(codec));
    stream->codec->width = info.videoWidth;
    stream->codec->height = info.videoHeight;
    stream->codec->extradata = (uint8_t *)av_mallocz(info.bufferLen + FF_INPUT_BUFFER_PADDING_SIZE);
    memc(stream->codec->extradata, info.buffer, info.bufferLen);
    stream->codec->extradata_size = info.bufferLen;// 添加 audio streamAVStream *stream = avformat_new_stream(fmtCtx, avcodec_find_encoder(codec));
    stream->codec->sample_fmt = AV_SAMPLE_FMT_S32;
    stream->codec->sample_rate = info.audioSampleRate;
    stream->codec->channel_layout = getChannelLayout(info.audioChannel);
    stream->codec->channels = info.audioChannel;
    stream->codec->bit_rate = info.audioBitrate;
    stream->codec->extradata = (uint8_t *)av_mallocz(info.bufferLen + FF_INPUT_BUFFER_PADDING_SIZE);
    memc(stream->codec->extradata, info.buffer, info.bufferLen);
    stream->codec->extradata_size = info.bufferLen;
  3. nativeWriteVideoStream & nativeWriteAudioStream

    写入 video & audio 压缩数据:编码器输出压缩数据,且 flag 不是 BUFFER_FLAG_CODEC_CONFIG:

    // 写入 video packetAVPacket packet;
    av_init_packet(&packet);
    
    packet.stream_index = pMuxer->videoIndex;
    packet.data = info.buffer;
    packet.size = info.bufferLen;
    packet.pts = rescaleTime(info.stamp, stream->time_base);
    packet.duration = 0;
    packet.dts = packet.pts;
    packet.pos = -1;// 关键帧if (info.flag == BUFFER_FLAG_KEY_FRAME) {
     packet.flags |= AV_PKT_FLAG_KEY;
    }// 写入一个 AVPacket 到输出文件int ret = av_interleaved_write_frame(fmtCtx, &packet);
    // 写入 audio packetAVPacket packet;
    av_init_packet(&packet);
    
    packet.stream_index = pMuxer->audioIndex;
    packet.data = buffer;
    packet.size = bufferLen;
    packet.pts = rescaleTime(stamp, stream->time_base);
    packet.dts = packet.pts;
    packet.pos = -1;    
    // 写入一个 AVPacket 到输出文件int ret = av_interleaved_write_frame(fmtCtx, &packet);
  4. nativeMuxerClose

    写入文件尾及释放相关结构体:当编码完成后,调用 muxerclose 方法进行释放:

    if (pMuxer->videoStream) {
     avcodec_close(((AVStream *)pMuxer->videoStream)->codec);
        pMuxer->videoStream = 0;
    }        
    if (pMuxer->audioStream) {
     avcodec_close(((AVStream *)pMuxer->audioStream)->codec);
        pMuxer->audioStream = 0;
    }if (pMuxer->pContext) {
     AVFormatContext *fmtCtx = (AVFormatContext *)pMuxer->pContext;    // 写入文件尾
        av_write_trailer(fmtCtx);
        avio_close(fmtCtx->pb);
     avformat_free_context(fmtCtx);
        pMuxer->pContext = 0;
    }



作者:GeorgeMR
链接:https://www.jianshu.com/p/7bb48641b71e


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP