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",只问"要不要继续收"。

相关推荐
UWA11 小时前
隐藏视频变“常驻刺客”,VideoPlayer与“N/A”纹理该怎么查
人工智能·性能优化·音视频·memory·cpu·游戏开发
DisonTangor11 小时前
【SeeDream开源平替】MiniMax H3 重磅开源:全模态视频生成新标杆,2K 画质 + 原生立体声,15 秒大片一键生成!
人工智能·ai作画·开源·aigc·音视频
cpp_learners15 小时前
FFmpeg H264视频编码全流程解析
ffmpeg·音视频
cpp_learners20 小时前
FFmpeg H264视频解码全流程解析
ffmpeg·音视频·解码
YH552698421 小时前
有什么AI工具能将网课和视频播客的内容转化成文档?
人工智能·音视频
程序员老陆1 天前
FFmpeg 像素格式(pix_fmt)通关指南:从 YUV420P 到 NV12、RGB24 到底怎么选
ffmpeg·音视频·yuv·像素格式
小白学大数据1 天前
基于Python的抖音网页版视频点赞数增长趋势追踪系统设计与实现
开发语言·爬虫·python·搜索引擎·音视频
DogDaoDao1 天前
FFmpeg 9.0 “Lei“ 正式发布:十年之后,以你之名
ffmpeg·音视频·实时音视频·视频编解码·ffprobe·ffmpeg 9.0
开开心心_Every1 天前
电脑文件搜索软件支持内容和拼音搜索
linux·服务器·人工智能·r语言·pdf·音视频·symfony