FFmpeg 新编码 API 完全指南:avcodec_send_frame / avcodec_receive_packet

目录

[一、为什么要淘汰旧的 avcodec_encode_video2](#一、为什么要淘汰旧的 avcodec_encode_video2)

[旧 API 的问题](#旧 API 的问题)

[新 API 的设计哲学](#新 API 的设计哲学)

[二、新 API 核心的4个函数](#二、新 API 核心的4个函数)

三、核心语义

[Rule 1](#Rule 1)

[Rule 2](#Rule 2)

[Rule 3](#Rule 3)

四、标准编码循环

初始化

编码主循环

五、Flush

旧写法(错误)

[新 API 正确 Flush](#新 API 正确 Flush)

[六、send / receive 返回码速查表](#六、send / receive 返回码速查表)

记忆口诀

[七、B 帧 / 延迟编码器为什么必须新 API](#七、B 帧 / 延迟编码器为什么必须新 API)

[八、和 HW Encoder(NVENC / AMF / QSV)一模一样](#八、和 HW Encoder(NVENC / AMF / QSV)一模一样)

[NVENC 例子(伪代码)](#NVENC 例子(伪代码))

[九、最容易踩的 5 个坑(血泪版)](#九、最容易踩的 5 个坑(血泪版))

[1. 忘了 av_packet_unref](#1. 忘了 av_packet_unref)

[2. send NULL 后又 send frame](#2. send NULL 后又 send frame)

[3. receive 一次就停](#3. receive 一次就停)

[4. pts 没设](#4. pts 没设)

[5. time_base 乱写](#5. time_base 乱写)

十、最小完整可编译骨架

十一、总结


这两个函数是 FFmpeg 中用于‌视频编码 ‌的核心 API,与解码流程(avcodec_send_packet / avcodec_receive_frame)正好相反。它们采用了异步推拉模型,将编码过程分为"发送原始帧"和"接收压缩包"两个步骤。

一、为什么要淘汰旧的 avcodec_encode_video2

如果你搜过 FFmpeg 编码例子,大概率见过这句:

复制代码
avcodec_encode_video2(codec_ctx, pkt, frame, &got_packet);

这是历史包袱

旧 API 的问题

问题 说明
同步语义混乱 got_packet 是 int 指针
不支持 B 帧延迟 最后一帧 flush 很丑
线程不安全 内部状态不可控
不支持 AV1 / Vulkan / HW enc 新编码器根本不兼容

新 API 的设计哲学

"生产者 / 消费者"模型

复制代码
send_frame  →  [Encoder Queue]  →  receive_packet

像 Qt 的 enqueue()/ dequeue()

编码器想缓存几帧就缓存几帧

flush / drain 语义清晰


二、新 API 核心的4个函数

复制代码
int avcodec_send_frame(AVCodecContext *ctx, const AVFrame *frame);
int avcodec_receive_packet(AVCodecContext *ctx, AVPacket *pkt);
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
int avcodec_flush_buffers(AVCodecContext *ctx);   // 辅助

三、核心语义

Rule 1

send 返回 0 ≠ 立刻有 packet

编码器可能:

  • 缓存 B 帧

  • GOP 没结束

  • Lookahead 没满


Rule 2

receive 返回 EAGAIN → 再送 frame

receive 返回 EOF → 没数据了


Rule 3

frame = NULL → 告诉编码器"我完了"


四、标准编码循环

初始化

复制代码
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodecContext *cctx = avcodec_alloc_context3(codec);

cctx->width  = 1280;
cctx->height = 720;
cctx->time_base = (AVRational){1, 25};
cctx->framerate = (AVRational){25, 1};
cctx->gop_size = 25;
cctx->max_b_frames = 3;
cctx->pix_fmt = AV_PIX_FMT_YUV420P;

avcodec_open2(cctx, codec, NULL);

编码主循环

复制代码
AVFrame *frame = av_frame_alloc();
AVPacket *pkt = av_packet_alloc();

while (have_frame_to_encode()) {

    fill_yuv_frame(frame);   // 自己写:sws_scale / hw map

    /* ---------- send ---------- */
    ret = avcodec_send_frame(cctx, frame);
    if (ret < 0) {
        fprintf(stderr, "send error: %s\n", av_err2str(ret));
        break;
    }

    /* ---------- receive ---------- */
    while (ret >= 0) {
        ret = avcodec_receive_packet(cctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            break;
        else if (ret < 0)
            goto end;

        printf("got packet pts=%lld size=%d\n", pkt->pts, pkt->size);

        write_output_file(pkt);
        av_packet_unref(pkt);
    }
}

av_frame_free(&frame);

五、Flush

旧写法(错误)

复制代码
while (got_packet)
    encode_video2(...);

新 API 正确 Flush

复制代码
/* 告诉编码器:没 frame 了 */
avcodec_send_frame(cctx, NULL);

while (avcodec_receive_packet(cctx, pkt) == 0) {
    pkt->pts = av_rescale_q(pkt->pts, cctx->time_base, out_timebase);
    write_output_file(pkt);
    av_packet_unref(pkt);
}

NULL frame = drain encoder queue


六、send / receive 返回码速查表

返回值 含义 你该干啥
0 OK 继续
AVERROR(EAGAIN) encoder 满了 receive_packet再试
AVERROR_EOF 队列空了 结束
<0 other 真出错 abort

记忆口诀

send 报 EAGAIN → receive 再收

receive 报 EAGAIN → send 再喂


七、B 帧 / 延迟编码器为什么必须新 API

举个 H264 bframes=3

复制代码
send f0
send f1
send f2
send f3  → receive PTS=0
send f4  → receive PTS=1
flush     → receive PTS=2,3,4

旧 API:

  • 你得自己数 flush 次数

  • x264 和 nvenc 行为不一致

新 API:

  • 编码器说了算

  • FFmpeg 官方保证语义一致


八、和 HW Encoder(NVENC / AMF / QSV)一模一样

NVENC 例子(伪代码)

复制代码
AVCodec *enc = avcodec_find_encoder_by_name("h264_nvenc");
avcodec_open2(cctx, enc, NULL);

avcodec_send_frame(cctx, frame);
while (avcodec_receive_packet(cctx, pkt) >= 0) {
    // same code
}

软件 / GPU API 完全一致

不用学两套


九、最容易踩的 5 个坑(血泪版)

1. 忘了 av_packet_unref

内存泄漏第一名


2. send NULL 后又 send frame

复制代码
send(NULL)
send(frame) // ❌ ENCODER_FLUSHED

只能 reopen ctx


3. receive 一次就停

复制代码
if (avcodec_receive_packet() == 0)
    write();
// ❌ 可能还有 B 帧

必须 while loop


4. pts 没设

复制代码
frame->pts = frame_index++; // ✅ 必须

否则 x264 直接丢帧


5. time_base 乱写

复制代码
cctx->time_base = {1, 1000}; // ❌ 某些 enc 不接受

推荐 {1, 25}/ {1, 90000}(TS)


十、最小完整可编译骨架

复制代码
while (get_next_frame(&frm)) {
    frm->pts = pts++;

    if (avcodec_send_frame(cc, frm) < 0) continue;

    while (avcodec_receive_packet(cc, pkt) == 0) {
        pkt->stream_index = st->index;
        av_interleaved_write_frame(fmt, pkt);
        av_packet_unref(pkt);
    }
}

avcodec_send_frame(cc, NULL);
while (avcodec_receive_packet(cc, pkt) == 0) {
    write_flush_packet(pkt);
    av_packet_unref(pkt);
}

十一、总结

FFmpeg 编码新 API = 不再问"我有没有 packet",只问"要不要继续收"。

相关推荐
A13345551 小时前
2026网盘视频播放器推荐 支持4K全平台
音视频
quweiie3 小时前
腾讯云视频点播-web上传视频
前端·音视频·腾讯云·上传视频·腾讯云视频点播
揽秀亭长4 小时前
音频转简谱实测:AI扒谱与传统制谱方式有什么区别
人工智能·音视频
ai产品老杨5 小时前
越界检测算法接入 AI 视频分析平台的流程与误报优化指南
人工智能·算法·音视频
阿图灵6 小时前
OpenCV 实用技巧三连:视频转图片、图片转视频、Pillow 与 OpenCV 互转
图像处理·python·opencv·计算机视觉·音视频·pillow
SEONIB_Explorer7 小时前
VEONIB 电商 UGC 视频自动化生产实战指南
人工智能·自动化·音视频·跨境电商·视频制作·veonib
美狐美颜SDK开放平台7 小时前
直播APP开发技术详解:视频美颜SDK在人脸识别、美型算法与渲染优化中的应用
人工智能·音视频·美颜sdk·直播美颜sdk·第三方美颜sdk
禹亮科技7 小时前
110㎡纵深长桌会议室音视频工程方案复盘|上海制药企业精装场景无改造亿联+耳目达落地实践
音视频·视频会议解决方案
乐橙开放平台1 天前
养老 SaaS 笔记:setMessageCallback 事件桥接 + msgType 映射 P0/P1,先 ACK 再分流值班流
笔记·物联网·音视频·智能家居
H0311169851 天前
关于当前主流知识库工具在分类整理学习资料场景下的选用参考
人工智能·信息可视化·音视频