🎬 第八章 音视频编解码技术与流程
📄 一句话概括

同一段视频里有三个顺序:显示顺序、编码顺序、解码顺序——后两个相同,且都和显示顺序不同,原因就是 B 帧要参考后面的帧。

📝 本节和前面几节的关系

8.2 讲了流程,从这一节开始钻进去——先看帧与帧之间那套最基础的约定。

一、三个顺序

这是一段 I B B P 结构的四帧视频,三个顺序的对照: 显示顺序:1、2、3、4——观众看到的先后; 编码顺序:1、4、2、3——P 帧被提前; 解码顺序:1、4、2、3——与编码顺序相同。 编码顺序与解码顺序必然一致:码流怎么写,就怎么解。

二、为什么 P 帧要被提前

全部原因只有一句话:B 帧要参考它后面的那个 P 帧。 B 帧是双向预测帧,既参考前面、也参考后面(第 6.4.5 节); 后面那个参考帧必须在 B 帧之前进入解码器,否则 B 帧无从解起; 所以编码时就先把 P 编出来、写进码流,位置排在 B 帧前面。 这不是代码顺序问题,是物理约束——谁先用,谁就得先到。

三、重排序缓冲

于是解码器不得不准备一块缓冲: 先解出来的 P 帧,显示时间戳比已解出的帧还晚,暂时不能显示; 把它存进缓冲,等 B 帧解完、按显示时间戳排序,再依次吐出去。 缓冲的深度取决于连续 B 帧的数量与参考距离。 这就是播放器里【解码缓冲】这块设置的来由,也是 DTS 与 PTS 要分开传的直接原因(第 6.5.3 节)。

四、三帧的编码代价

把三种帧放在一起比,代价与收益很清楚: I 帧——不参考别人、码流最大,但提供了随机访问点; P 帧——只参考前面、码流中等,被参考所以有传递风险; B 帧——前后都参考、码流最小,但不被别人参考,等于【只进不出】。 所以工程上的常见配置是多放 B 帧——它们几乎白赚压缩比,只要解码端缓冲与算力跟得上。

五、动手 Demo:显示顺序、编码顺序、解码顺序

以一段 4 帧的小 GOP 为例(I B B P),三个【顺序】并不一样。

frames = [("1", "I", 1, 1), ("2", "B", 2, 3),
        ("3", "B", 3, 4), ("4", "P", 4, 2)]
for fid, kind, disp, enc in frames:
    print("帧 %-3s %s  显示序 %d  编码序 %d"
          % (fid, kind, disp, enc))

实跑输出:

--------------------------------------------------------------------
显示顺序、编码顺序、解码顺序
--------------------------------------------------------------------

以一段 4 帧的小 GOP 为例(I B B P),三个【顺序】并不一样。

  帧     类型     显示序     编码序     解码序
  ---------------------------------------------
  1      I        1          1          1
  2      B        2          3          3
  3      B        3          4          4
  4      P        4          2          2

  三个关键结论:
  一是 **P 帧要抢在 B 帧之前解码**——因为 B 帧一边要参考它;
  二是 **编码顺序就是解码顺序**,两者相同,都和显示顺序不同;
  三是 所以解码器必须有一块**重排序缓冲**,把解出来的帧先存着,按显示序吐出去。
  **这就是第 6.5.3 节里 DTS 与 PTS 要分开传的全部原因。**

P 帧要抢在 B 帧之前解码——因为 B 帧一边要参考它; 编码顺序就是解码顺序,两者相同,都和显示顺序不同。 所以解码器必须有一块重排序缓冲(第 6.5.3 节的 DTS 与 PTS)。

30 秒实验

  1. 用 MediaInfo 打开一个含 B 帧的视频,看视频流的【帧率模式】是不是可变帧率。
  2. 再用码流分析工具随便点几个帧,找找有没有 B 帧、它们的 DTS 与 PTS 差多少。

常见误区

  1. 以为编码顺序与显示顺序一样。B 帧的存在让两者必然不同。
  2. 以为解码顺序可以和编码顺序不一样。码流怎么写就怎么解,两者恒等。
  3. 以为 B 帧是【多余的】。B 帧码流最小,是压缩比的重要来源。
自测三问(点开对答案) 1. 三个顺序里哪两个是相同的? —— 编码顺序与解码顺序相同,都与显示顺序不同。 2. 为什么 P 帧要排在 B 帧前面? —— 因为 B 帧要参考后面的 P 帧,被参考的必须先到位。 3. 重排序缓冲的深度由什么决定? —— 连续 B 帧的数量与参考帧的距离。

相关笔记