avcodec_open2调用失败主因是未初始化或参数不匹配:需用avcodec_alloc_context3分配上下文,H.264编码必须设pix_fmt为AV_PIX_FMT_YUV420P,解码MP4中H.264需转Annex-B格式。

ffmpeg\_avcodec\_open2 调用失败的常见原因
绝大多数初学者卡在第一步:调用 avcodec_open2 返回负值。这不是代码写错了,而是没完成前置初始化或参数不匹配。
-
avcodec_register_all()已废弃,必须改用avcodec_register()或更推荐的av_log_set_level(AV_LOG_DEBUG)+avcodec_find_encoder/decoder前检查返回值是否为nullptr -
编码器需手动分配
AVCodecContext:用avcodec_alloc_context3(codec),不能直接栈上声明结构体 - H.264 编码时,
codec_ctx->pix_fmt必须设为AV_PIX_FMT_YUV420P(x264 不支持 RGB 直接编码),否则avcodec_open2静默失败 - 解码时若输入是 MP4 容器里的 H.264,注意
AVPacket的data可能带 AVCC 格式头(sps/pps 内嵌),需用av_bsf_init+av_bsf_send_packet转成 Annex-B 格式再送解码器
如何正确分配和填充 AVFrame 用于编码
AVFrame 不是“填完数据就能扔给编码器”,它的内存布局、对齐、生命周期都必须严格符合编码器要求。
- 编码前必须调用
av_frame_get_buffer(frame, 32)分配内部缓冲区(32 是内存对齐字节数),不能只 mallocframe->data[0] - YUV420P 布局下:
frame->data[0]是 Y 平面,frame->data[1]是 U,frame->data[2]是 V;frame->linesize[0]是 Y 行字节数(可能 > width),U/V 行宽通常是 Y 的一半 - 务必设置
frame->width、frame->height、frame->format(如AV_PIX_FMT_YUV420P)和frame->pts(时间戳,单位是codec_ctx->time_base的 tick 数) - 编码器可能重用
AVFrame内部缓冲,所以每次填新帧前要调用av_frame_make_writable(frame),否则写入可能崩溃
avcodec\_receive\_packet 接收不到输出帧?
这是最隐蔽的问题:你以为数据送进去了,其实编码器还在攒帧(B帧、延迟队列),或者你没按“推一帧 → 拉多帧”循环处理。
- 编码流程必须是:
avcodec_send_frame()→ 循环调用avcodec_receive_packet()直到返回AVERROR(EAGAIN)或AVERROR_EOF - 解码同理:
avcodec_send_packet()→ 循环avcodec_receive_frame(),尤其注意视频关键帧(IDR)后可能有多个非参考帧堆积 - 编码结束时,必须传
nullptr给avcodec_send_frame()触发 flush,否则最后一组帧永远出不来 - 检查
avcodec_receive_packet()返回值:0 成功,AVERROR(EAGAIN)表示输入不足,AVERROR_EOF表示已 flush 完毕,其他负值才是错误
为什么编码出来的视频播放卡顿或花屏?
大概率是时间基(time base)和 PTS/DTS 混乱,而不是画质参数问题。
立即学习“C++免费学习笔记(深入)”;
- 编码器输出的
AVPacket->pts和dts默认基于codec_ctx->time_base,但封装到 MP4 时 muxer 通常要求基于AV_TIME_BASE或流自己的time_base—— 必须用av_packet_rescale_ts(pkt, codec_ctx->time_base, stream->time_base) - 不要手动算 PTS:用
frame->pts = frame_index * (codec_ctx->time_base.den / codec_ctx->time_base.num) / fps这类整数运算,浮点误差会累积导致音画不同步 - 启用 B 帧时(
codec_ctx->max_b_frames = 3),DTS 会小于 PTS,muxer 必须按 DTS 排序写包,否则播放器解码顺序错乱 - MP4 封装前漏掉
avformat_write_header或没写AVStream->codecpar(从codec_ctx复制),会导致播放器无法解析时间戳
// 示例:正确发送 flush 并收完剩余 packet
avcodec_send_frame(codec_ctx, nullptr);
while (1) {
int ret = avcodec_receive_packet(codec_ctx, &pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
if (ret < 0) {
fprintf(stderr, "Flush error: %s\n", av_err2str(ret));
exit(1);
}
av_packet_rescale_ts(&pkt, codec_ctx->time_base, stream->time_base);
av_interleaved_write_frame(fmt_ctx, &pkt);
av_packet_unref(&pkt);
}
真正难的不是调哪个函数,而是理解每个 AVPacket 和 AVFrame 在 pipeline 中携带的语义:它的时间基准是什么、内存是否可写、是否被引用计数持有、是否需要 rescale。这些信息不会报错,但会让结果不可预测。








