如何使用c++和FFmpeg进行视频编解码? (多媒体处理)

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

如何使用c++和ffmpeg进行视频编解码? (多媒体处理)

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,注意 AVPacketdata 可能带 AVCC 格式头(sps/pps 内嵌),需用 av_bsf_init + av_bsf_send_packet 转成 Annex-B 格式再送解码器

如何正确分配和填充 AVFrame 用于编码

AVFrame 不是“填完数据就能扔给编码器”,它的内存布局、对齐、生命周期都必须严格符合编码器要求。

  • 编码前必须调用 av_frame_get_buffer(frame, 32) 分配内部缓冲区(32 是内存对齐字节数),不能只 malloc frame->data[0]
  • YUV420P 布局下:frame->data[0] 是 Y 平面,frame->data[1] 是 U,frame->data[2] 是 V;frame->linesize[0] 是 Y 行字节数(可能 > width),U/V 行宽通常是 Y 的一半
  • 务必设置 frame->widthframe->heightframe->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)后可能有多个非参考帧堆积
  • 编码结束时,必须传 nullptravcodec_send_frame() 触发 flush,否则最后一组帧永远出不来
  • 检查 avcodec_receive_packet() 返回值:0 成功,AVERROR(EAGAIN) 表示输入不足,AVERROR_EOF 表示已 flush 完毕,其他负值才是错误

为什么编码出来的视频播放卡顿或花屏?

大概率是时间基(time base)和 PTS/DTS 混乱,而不是画质参数问题。

玫瑰克隆工具

玫瑰克隆工具

AI图文笔记一键生成创作并自动发布助手

下载

立即学习C++免费学习笔记(深入)”;

  • 编码器输出的 AVPacket->ptsdts 默认基于 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);
}

真正难的不是调哪个函数,而是理解每个 AVPacketAVFrame 在 pipeline 中携带的语义:它的时间基准是什么、内存是否可写、是否被引用计数持有、是否需要 rescale。这些信息不会报错,但会让结果不可预测。

https://www.php.cn/faq/1976479.html

发表回复

Your email address will not be published. Required fields are marked *