MediaCodec编码结合FFmpeg封装流

在Android平台上合成视频一般使用MediaCodec进行硬编码,使用MediaMuxer进行封装,但是因为MediaMuxer在某些机型上合成的视频在其他手机上播放会出现问题,而且只支持一个音频轨道,因此可以选用FFmpeg来封装编码后的音视频流。
 

创建FFmpeg AVFormatContext AVFormatContext*ofmt_ctx=nullptr; intret=avformat_alloc_output_context2(&ofmt_ctx, nullptr, "mp4", filePath); AVOutputFormat*ofmt=ofmt_ctx->oformat; ret=avio_open(&ofmt_ctx->pb, filePath, AVIO_FLAG_WRITE);

 

添加音视频流

这里以添加h264视频流为例

AVStream*stream=avformat_new_stream(ofmt_ctx, nullptr); intvideo_stream=stream->index; AVCodecParameters*codecpar=stream->codecpar; codecpar->codec_type=AVMEDIA_TYPE_VIDEO; codecpar->codec_id=AV_CODEC_ID_H264; codecpar->width=width; codecpar->height=height;

 

设置视频流sps和pps

sps和pps能在MediaCodec产生第一帧画面之前获取到,以java MediaCodec异步编码方式为例

@Override publicvoidonOutputBufferAvailable(@NonNullMediaCodeccodec, intindex, @NonNullMediaCodec.BufferInfoinfo) { ByteBufferbuffer=encoder.getOutputBuffer(index); if ((info.flags&MediaCodec.BUFFER_FLAG_CODEC_CONFIG) !=0) { // 传递 buffer和info.size到native } // ... } // native获取sps和pps数据地址 uint8_t*data=static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer)); // 复制给视频流extradata AVCodecParameters*codecpar=ofmt_ctx->streams[video_stream]->codecpar; codecpar->extradata= (uint8_t*) av_mallocz(size+AV_INPUT_BUFFER_PADDING_SIZE); memcpy(codecpar->extradata, data, size); codecpar->extradata_size=size;

 

写入视频文件头信息,放在文件开头位置 AVDictionary*dict=nullptr; av_dict_set(&dict, "movflags", "faststart", 0); intret=avformat_write_header(ofmt_ctx, &dict);

 

写入视频流和音频流已编码数据

同样以写入视频流数据为例,⚠️注意视频流和音频流在不同线程写入时需要同步

// onOutputBufferAvailable回调中 booleanisKeyFrame= (info.flags&MediaCodec.BUFFER_FLAG_KEY_FRAME) !=0; // 传递buffer, info.size, isKeyFrame, info.presentationTimeUs到native // 获取视频编码数据地址 uint8_t *data = static_cast<uint8_t *>(env->GetDirectBufferAddress(buffer)); AVPacket *packet = av_packet_alloc(); av_init_packet(packet); packet->stream_index = video_stream; packet->data = data; packet->size = size; packet->pts = av_rescale_q(pts, { 1, 1000000 }, ofmt_ctx->streams[video_stream]->time_base); if (isKeyFrame) packet->flags |= AV_PKT_FLAG_KEY; int ret = av_interleaved_write_frame(ofmt_ctx, packet); av_packet_unref(packet); av_packet_free(&packet);

 

结束并关闭文件

至此,整个流程就结束了

av_write_trailer(ofmt_ctx); avio_closep(&ofmt_ctx->pb); avformat_free_context(ofmt_ctx);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsppsg.html