Visual LabINTERACTIVE LEARNING
阶段 9 · 原生实践进阶30 分钟

原生解封装与解码:FFmpeg 最小闭环

AVFormatContext、AVPacket、AVCodecContext 与 AVFrame

用 FFmpeg 对象关系理解打开输入、查找流、读取 Packet、send/receive 解码和 flush 的完整协议。

LEARNING GOALS

先明确本课目标

1

区分 AVFormatContext 与 AVCodecContext

2

正确使用 send_packet / receive_frame

3

处理 EOF、EAGAIN 与 flush

MENTAL MODEL · 再建立心智模型

libavformat 把容器拆成 AVPacket;libavcodec 把 Packet 消费成 AVFrame。两个库之间以 Codec 参数、时间戳和流索引交接。

MINIMAL EXAMPLE

FFmpeg 解码循环为什么有两层 while?

解码器有内部输入和输出队列。外层读取 Packet 并发送,内层持续接收所有已可用 Frame;遇到 EAGAIN 后再回到另一侧推进。

最小例子cpp
while (av_read_frame(format, packet) >= 0) {
  if (packet->stream_index == videoStream) {
    avcodec_send_packet(codec, packet);
    while (avcodec_receive_frame(codec, frame) == 0) {
      render(frame);
      av_frame_unref(frame);
    }
  }
  av_packet_unref(packet);
}
  1. Demux 读取 Packet av_read_frame 按文件交错顺序返回不同轨道 Packet。
  2. 发送匹配轨道数据 只有对应 stream_index 的 Packet 进入该解码器。
  3. 取尽当前可用 Frame receive 直到 EAGAIN,避免输出积压。

容器 Packet 被稳定转换为带像素格式和 PTS 的 AVFrame,且引用得到及时释放。

本课概念
OPTIONAL · 按需查阅完整术语表与概念边界

遇到陌生术语时再展开,不打断主线实验。

展开⌄
LIVE EXPERIMENT · 先获得反馈

边改边运行

浏览器加载后会激活代码编辑器、独立运行环境与实时输出。

本课挑战

写出同时解码音频和视频、保留 PTS 并正确 flush 的最小 C++ 循环。

DEEP DIVE · 实验之后

回到原理,解释刚才发生了什么

先用结果建立反馈,再按运行流程、核心原理和知识关系逐层深入。

CORE IDEA

核心讲解

01

Packet 和 Frame 不是一一对应

send 一个 Packet 可能暂时没有输出,也可能 receive 多个 Frame。EAGAIN 表示需要在发送和接收之间继续推进状态机。

02

EOF 后仍要冲刷解码器

有 B 帧或内部延迟时,最后一些 Frame 仍留在解码器。向 send_packet 传 null 再持续 receive 才能取完。

OPTIONAL · 迁移时查阅Web 与原生实现对照

主线实验完成后,再用完整映射处理跨平台或跨 API 迁移。

展开⌄
API COMPARISON

FFmpeg 把浏览器内部 Demux/Decode 的核心对象显式交给原生应用。

WebGL

libavformat 直接解析容器,libavcodec 通过 Packet/Frame send-receive API 解码。

WebGPU

WebCodecs 接受应用已经 Demux 的 EncodedVideoChunk,并输出 VideoFrame。

WebCodecs 从 FFmpeg 流程中拿走了 Demux 层;原生 FFmpeg 则把容器和 Codec 都补齐。