解码器每解一帧都要做一次判断:这帧能立刻显示吗?——不能的就得入缓冲,等后面的帧凑齐了再按显示顺序吐出来。
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 秒实验
- 在播放器里找找【解码缓冲】或【重排序缓冲】相关的设置项,看默认值是多少。
- 把一个 B 帧很多的视频与一个全是 I 帧的视频分别播放,观察拖动时的响应差别。
常见误区
- 把解码缓冲与网络缓冲当成一回事。一个为 B 帧服务、一个为网络抖动服务。
- 以为 PTS 与 DTS 只是两个叫法。它们排序出来的顺序根本不同。
- 以为缓冲越深越保险。越深意味着延迟越大,直播场景要付出代价。
自测三问(点开对答案)
1. 解码器先解出来的 P 帧为什么要进缓冲? —— 它的 PTS 比已解出的帧晚,现在显示会乱序。 2. 重排序缓冲的深度由什么决定? —— 连续 B 帧的数量与参考帧的距离。 3. 解码缓冲与播放缓冲分别为什么服务? —— 前者为 B 帧重排序,后者为网络抖动。- ☐ 下一节(8.3.4 帧内与帧间编码)
相关笔记
- I/P/B帧的特点 — 三类帧的性质来源
- 帧内与帧间编码 — 镜头推进到帧内部
- DTS和PTS详细讲解 — 两个时间戳的定义
- 音视频的封装 — 时间戳在封装里的位置
- 🎬 FFmpeg入门详解 · 索引