需求
- 转封装时需要将编码信息从输入
AVCodecContext
拷贝到输出AVCodecContext
- 自定义编码格式,需要将
AVCodecContext
中的数据拷贝到AVFormatContext
中 - 需要获取输入的
AVCodecContext
的编码信息
解决方案
旧版本的ffmpeg提供avcodec_copy_context()
,用于拷贝codec
信息,但是该方式已经被遗弃。
- 旧版本的
AVFormatContext
中包含AVCodecContext *codec
,该codec
存储了编码的信息 - 新版本的
AVFormatContext
中不再包含AVCodecContext *codec
,取而代之的是AVCodecParameters *codecpar
AVStream到AVStream
针对需求中的场景1,旧版本可以直接从codec
中获取,新版本ffmpeg操作示范代码如下:
AVFormatContext *input_format_ctx, *output_format_ctx;
/* 打开输入操作... */
for (index = 0; index < input_format_ctx->nb_streams; index++) {
AVStream *input_stream, *output_stream;
input_stream = input_format_ctx->streams[index];
output_stream = avformat_new_stream(output_format_ctx, nullptr);
if (output_stream == nullptr) {
av_log(nullptr, AV_LOG_ERROR, "Could not create output stream.\n");
goto end;
}
if (avcodec_parameters_copy(output_stream->codecpar, input_stream->codecpar) < 0) {
av_log(nullptr, AV_LOG_ERROR, "Failed to copy parameters from input_stream->codecpar"
"to output_stream->codecpar.\n");
goto end;
}
}
AVFormatContext到AVCodec
针对场景2、场景3,ffmpeg提供了两个API供我们使用:
- avcodec_parameters_to_context
- avcodec_parameters_from_context
解码
AVCodecContext *codec_ctx = avcodec_alloc_context3(nullptr);
if (codec_ctx == nullptr) {
av_log(nullptr, AV_LOG_ERROR, "Alloc codec context failed.\n");
goto end;
}
for (index = 0; index < input_format_ctx->nb_streams; index++) {
AVStream *input_stream, *output_stream;
input_stream = input_format_ctx->streams[index];
output_stream = avformat_new_stream(output_format_ctx, nullptr);
/* 将codec从AVFormatContext拷贝出来 */
/* Copy codec parameters from input_stream->codecpar */
if (avcodec_parameters_to_context(codec_ctx, input_stream->codecpar) < 0) {
av_log(nullptr, AV_LOG_ERROR, "Failed to copy codec parameters from "
"input_stream->codecpar to codec_ctx.\n");
goto end;
}
/* 取到输入的codec了,可以任意操作 */
/* do something ... */
if (output_format_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
avcodec_free_context(&codec_ctx);
编码
AVFormatContext *format_ctx;
AVCodecContext *codec_ctx;
AVStream *stream;
format_ctx = avformat_alloc_output_context2(&format_ctx, nullptr, nullptr,
filename);
if (format_ctx == nullptr) {
av_log(nullptr, AV_LOG_ERROR, "Alloc format context failed.\n");
goto end;
}
codec_ctx = avcodec_alloc_context3(nullptr);
if (codec_ctx == nullptr) {
av_log(nullptr, AV_LOG_ERROR, "Alloc codec context failed.\n");
goto end;
}
stream = avformat_new_stream(format_ctx, nullptr);
if (stream == nullptr) {
av_log(nullptr, AV_LOG_ERROR, "Create stream failed.\n");
goto end;
}
codec_ctx->video_type = AVMEDIA_TYPE_VIDEO;
codec_ctx->format = AV_PIX_FMT_YUV420P;
codec_ctx->codec_id = m_fmt_ctx->oformat->video_codec;
codec_ctx->width = 1280;
codec_ctx->height = 720;
codec_ctx->gop_size = 12;
codec_ctx->time_base = AVRational{1, 25};
codec_ctx->bit_rate = 1400 * 1000;
/* 将codec拷贝到AVFormatContext中去 */
/* Copy codec parameters from codec_ctx to output_stream->codecpar */
if (avcodec_parameters_from_context(output_stream->codecpar, codec_ctx) < 0) {
av_log(nullptr, AV_LOG_ERROR, "Failed to copy codec parameters from "
"codec_ctx to output_stream->codecpar.\n");
goto end;
}
avcodec_free_context(&codec_ctx);
avformat_free_context(output_format_ctx);