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

解码器每解一帧都要做一次判断:这帧能立刻显示吗?——不能的就得入缓冲,等后面的帧凑齐了再按显示顺序吐出来。

📝 本节和前面几节的关系

8.3.2 讲了三类帧的性质,这一节把它们放进解码器,看逐帧到底在做什么动作。

一、解码器的逐帧动作

沿用那个 4 帧的小 GOP(显示序 I B B P),解码顺序是 I、P、B、B: 解出 I——显示时间戳就是当前时间,直接显示; 解出 P——它的显示时间戳还早,现在拿出来会跳帧,先入缓冲; 解出 B——参考帧齐了,能解、也能显示,直接显示; 解出 B——同上; 最后把缓冲里的 P 取出来显示。 整个过程是【一个缓冲深度】就够用的最小例子。

二、缓冲深度的由来

缓冲不是随便开的,深度由两个因素决定: 连续 B 帧的数量——两个连续 B 帧 → 至少要能存 1 个未来的参考帧; 参考距离——如果 B 帧参考的不是紧接着的下一个 P,深度还要再加; 所以 B 帧越多、参考越远,缓冲越深、延迟越大。 这也是为什么直播编码器常常把 B 帧数设成 0 或 1——省下的延迟比省下的码率更值钱。

三、时间戳在这里的角色

解码器靠什么知道该先显示谁?全靠 PTS。 DTS(解码时间戳)决定什么时候解——所以按 DTS 排是【编码顺序】; PTS(显示时间戳)决定什么时候显示——所以按 PTS 排是【显示顺序】; 两者的差就是这一帧在缓冲里待了多久。 第 6.5.3 节讲过 DTS 与 PTS 的关系,这一节是它在解码器里的落地。

四、播放器上的两种缓冲不要混淆

实际播放器里同时存在两种缓冲,作用完全相反: 解码缓冲(重排序缓冲)——为 B 帧服务,存的是帧,与编码结构有关; 播放缓冲(网络缓冲)——为网络抖动服务,存的是压缩数据,与网络有关。 前者由编码结构决定,后者由网络状况决定。 卡顿先分清是哪种缓冲见底——这是排障的第一步。

五、从流程回看前面几节

这一节把前面三节串成了一条完整链路: 8.3.1 给了三个顺序——这是流程的【输入】; 8.3.2 给了三类帧的性质——这是流程的【约束】; 8.3.3 把两步合成解码器的逐帧动作——这是流程的【执行】。 下一节开始,镜头从【帧之间】推进到【帧内部】,看帧内与帧间编码各怎么做。

六、动手 Demo:重排序缓冲到底要存几帧

沿用上一个小 GOP,看解码器逐帧做了什么。

order = [("1", "I", 1, "直接显示"), ("2", "P", 4, "存起来等 B"),
         ("3", "B", 2, "直接显示"), ("4", "B", 3, "直接显示")]
buf = []
for seq, kind, pts, act in order:
    print("解码 %s  %s  PTS=%d  %s" % (seq, kind, pts, act))

实跑输出:

--------------------------------------------------------------------
重排序缓冲到底要存几帧
--------------------------------------------------------------------

沿用上一个小 GOP,看解码器逐帧做了什么。

  解码序     帧     PTS     动作                   缓冲内容
  ----------------------------------------------------------
  1          I      1       可直接显示             —
  2          P      4       存起来等 B             P(4)
  3          B      2       可直接显示             P(4)
  4          B      3       可直接显示             P(4)
  5          —      —       把 P(4) 取出来显示     空

  缓冲**深度只要 1 帧**就够了——因为本例里 B 帧最多等一个 P。
  但结构一改就会变:**连续 B 帧越多、参考距离越远,缓冲就要越深**。
  播放器上那句【解码缓冲不足】的报错,说的就是这里。

缓冲深度只要 1 帧就够了——因为本例里 B 帧最多等一个 P。 但结构一改就会变:连续 B 帧越多、参考距离越远,缓冲就要越深。

30 秒实验

  1. 在播放器里找找【解码缓冲】或【重排序缓冲】相关的设置项,看默认值是多少。
  2. 把一个 B 帧很多的视频与一个全是 I 帧的视频分别播放,观察拖动时的响应差别。

常见误区

  1. 把解码缓冲与网络缓冲当成一回事。一个为 B 帧服务、一个为网络抖动服务。
  2. 以为 PTS 与 DTS 只是两个叫法。它们排序出来的顺序根本不同。
  3. 以为缓冲越深越保险。越深意味着延迟越大,直播场景要付出代价。
自测三问(点开对答案) 1. 解码器先解出来的 P 帧为什么要进缓冲? —— 它的 PTS 比已解出的帧晚,现在显示会乱序。 2. 重排序缓冲的深度由什么决定? —— 连续 B 帧的数量与参考帧的距离。 3. 解码缓冲与播放缓冲分别为什么服务? —— 前者为 B 帧重排序,后者为网络抖动。

相关笔记