🎬 第六章 音视频压缩编码基础
📄 一句话概括

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 秒实验

  1. 用 ffprobe 加 -show_packets 看一个含 B 帧的文件。
  2. 找出 PTS 与 DTS 不相等的那些包。

常见误区

  1. 以为 PTS 永远大于 DTS。顺序关系取决于 GOP 结构。
  2. 遇到不同步直接改播放器。先按三步查文件本身。
自测三问(点开对答案) 1. PTS 与 DTS 分别管什么? —— PTS 管显示时刻,DTS 管解码时刻。 2. 为什么有 B 帧时两者会不同? —— B 帧要参考后面的帧,必须先解码后续的 P 帧。 3. 排查音画不同步有哪三步? —— 查 DTS 是否单调递增、查 PTS 与 DTS 差值是否稳定、查两条流时间戳基准是否一致。

相关笔记