🎬 第五章 视频基础
📄 一句话概括

用 FFmpeg 写播放器就四步:用 avformat 解封装拿到【包】,用 avcodec 解码拿到【帧】,再转格式、输出——记住包与帧这两个名字,整条链路就通了。

📝 本节和前面几节的关系

5.4.1 讲了播放器的四个环节,这一节看用 FFmpeg 库具体怎么落地。

一、两个关键数据结构

AVPacket(包):压缩后的一段数据——解封装之后、解码之前。 AVFrame(帧):解码后的原始数据——解码之后、送屏之前。 整条播放链路,本质就是在【包】与【帧】之间来回转换。 记住这两个名字,看任何播放器代码都不会迷路。

二、四步走

① 打开与解封装(libavformat):读文件、找出各路流、读出包。 ② 解码(libavcodec):把包送进解码器,取得帧。 ③ 格式转换:视频用 libswscale 转像素格式,音频用 libswresample 转采样格式。 ④ 输出与呈现:交给显示与音频设备。

三、第三步为什么不能省

因为解码器输出的格式,往往不是显示设备想要的格式。 比如解码出来是 YUV 4:2:0,而渲染层要 RGB——中间必须转一次。 音频同理:解码出来可能是 48 kHz 单声道,设备要 44.1 kHz 立体声。 所以格式转换是播放链路里固定的一环,不是可选的优化。

四、和 3.5.1 的分工对上

第 3.5.1 节讲过 FFmpeg 的六个核心库,这里正好用到了四个。 avformat 管进出、avcodec 管压解、swscale 管视频换算、swresample 管音频换算。 剩下两个(avutil 管公共工具、avfilter 管滤镜)在播放链路里是辅助角色。 这也印证了那句话:这些库本来就是一条流水线拆开的结果。

五、动手 Demo:FFmpeg 播放架构,四步走

用 FFmpeg 库写播放器,核心就是这四步。

rows = [("打开与解封装", "libavformat", "AVPacket 压缩包"),
        ("解码", "libavcodec", "AVFrame 原始帧"),
        ("格式转换", "libswscale/libswresample", "统一的像素或采样格式"),
        ("输出与呈现", "(交给设备)", "画面、声音")]
for a, b, c in rows:
    print("%-10s %-24s %s" % (a, b, c))

实跑输出:

--------------------------------------------------------------------
FFmpeg 播放架构,四步走
--------------------------------------------------------------------

用 FFmpeg 库写播放器,核心就是这四步。

  步骤             用哪个库                     产出
  -------------------------------------------------------------------
  打开与解封装     libavformat                  AVPacket 压缩包
  解码             libavcodec                   AVFrame 原始帧
  格式转换         libswscale/libswresample     统一的像素或采样格式
  输出与呈现       (交给设备)                 画面、声音

  记住两个名字:**包(Packet)是压缩后的数据,帧(Frame)是解码后的数据。**
  整条链路就是在两者之间来回转换。

记住两个名字:包(Packet)是压缩后的数据,帧(Frame)是解码后的数据。 整条链路就是在两者之间来回转换。

30 秒实验

  1. 翻一遍 FFmpeg 官方示例里的最小播放程序,找出这四步分别对应哪几行。
  2. 你会发现代码结构和这张表一一对应。

常见误区

  1. 跳过格式转换直接送屏。解码器输出格式与设备要求常常不一致。
  2. 把包与帧混用。包是压缩的,帧是解压的,两者不能互换。
自测三问(点开对答案) 1. AVPacket 与 AVFrame 分别是什么? —— AVPacket 是压缩后的一段数据,AVFrame 是解码后的原始数据。 2. FFmpeg 播放架构的四步是什么? —— 解封装、解码、格式转换、输出呈现。 3. 为什么格式转换不能省? —— 解码器输出的像素或采样格式常常与显示/播放设备要求不一致。

相关笔记