PTS 管显示、DTS 管解码——两套时间戳之所以要分开,全是因为 B 帧要求【先解码后面的帧】。
6.5.2 讲了 GOP 的组织,这一节把最后一个结构讲清楚:时间戳。
一、两套时间戳
PTS(Presentation Time Stamp):这一帧该在什么时候显示。 DTS(Decoding Time Stamp):这一帧该在什么时候解码。 在没有 B 帧的情况下,两者可以完全一致。 但一旦有了 B 帧,它们就必然错位。
二、一个具体的例子
假设显示顺序是 I、B、P、B、P: 第 1 位显示 I——解码顺序也是 1。 第 2 位显示 B——但它要等第 3 位的 P 先解,所以解码顺序是 3。 第 3 位显示 P——解码顺序是 2。 后面的 B、P 同理,解码顺序整体被往后推。 结论很直观:要显示的 B 帧排在第 2,但必须等排在第 3 的 P 帧先解。
三、播放器怎么用这两套时间戳
按 DTS 取帧:保证解码时参考帧已就绪。 按 PTS 送显:保证画面出现的时刻正确。 中间需要一个重排序缓冲区——把解好的帧先存着,到 PTS 时刻再送出去。 所以【用了 B 帧就必须有缓冲区】,这也是 B 帧带来延迟的直接原因。
四、排查音画不同步时的用法
这是 5.2.2 提过的那件事,这里给出具体做法: 第一步:看 DTS 序列是不是单调递增——不是就说明编码端写错了。 第二步:看 PTS 与 DTS 的差是不是稳定——差值乱跳说明有问题。 第三步:看两条流的时间戳基准是不是一致——基准不同必然错位。 按这三步走,绝大多数不同步问题都能定位到具体环节。
五、动手 Demo:解码顺序和显示顺序为什么不一样
有 B 帧之后,解码顺序与显示顺序就不再一致了。
rows = [("1", "I", "1", "先解,作为参考"),
("2", "B", "3", "要等 P 解完"),
("3", "P", "2", "B 的参考"),
("4", "B", "5", "要等下一个 P"),
("5", "P", "4", "B 的参考")]
for a, b, c, d in rows:
print("%-4s %-3s %-4s %s" % (a, b, c, d))
实跑输出:
--------------------------------------------------------------------
解码顺序和显示顺序为什么不一样
--------------------------------------------------------------------
有 B 帧之后,解码顺序与显示顺序就不再一致了。
显示顺序 帧 解码顺序 原因
------------------------------------------------
1 I 1 先解,作为参考
2 B 3 要等 P 解完
3 P 2 B 的参考
4 B 5 要等下一个 P
5 P 4 B 的参考
看第 2、3 行:**要显示的 B 帧排在第 2,但必须等排在第 3 的 P 帧先解。**
这就是 **PTS 管显示、DTS 管解码** 的全部原因。
看第 2、3 行:要显示的 B 帧排在第 2,但必须等排在第 3 的 P 帧先解。 这就是 PTS 管显示、DTS 管解码 的全部原因。
30 秒实验
- 用 ffprobe 加
-show_packets看一个含 B 帧的文件。 - 找出 PTS 与 DTS 不相等的那些包。
常见误区
- 以为 PTS 永远大于 DTS。顺序关系取决于 GOP 结构。
- 遇到不同步直接改播放器。先按三步查文件本身。
自测三问(点开对答案)
1. PTS 与 DTS 分别管什么? —— PTS 管显示时刻,DTS 管解码时刻。 2. 为什么有 B 帧时两者会不同? —— B 帧要参考后面的帧,必须先解码后续的 P 帧。 3. 排查音画不同步有哪三步? —— 查 DTS 是否单调递增、查 PTS 与 DTS 差值是否稳定、查两条流时间戳基准是否一致。- ☐ 下一章(第七章 音视频编解码原理与标准)
相关笔记
- GOP详细讲解 — 顺序被打乱的来源
- 音视频的封装 — 时间戳在封装里的位置
- 双向预测 — B 帧为什么要等
- 🎬 FFmpeg入门详解 · 索引