🎬 第五章 视频基础
📄 一句话概括

封装不只是把两路流放进一个文件,还要用时间戳把它们对齐——音画不同步,本质就是两条时间戳对不上。

📝 本节和前面几节的关系

5.2.1 讲了封装是什么,这一节讲它封装的两路(或多路)流怎么配合。

一、封装里装了什么

一个典型的音视频文件里装着两条独立的流: 视频流:一串视频帧,每帧带自己的时间戳。 音频流:一串音频帧,每帧也带自己的时间戳。 再外加一份索引表与元信息(时长、码率、宽高)。

二、PTS 与 DTS

PTS(显示时间戳):这一帧该在什么时候被显示出来。 DTS(解码时间戳):这一帧该在什么时候被解码。 两者不同,是因为 B 帧要参考后面的帧——所以必须【先解码、后显示】。 所以排查音画不同步时,要看的是这两个时间戳的顺序对不对。

三、为什么会不同步

常见三种原因: 时间戳本身写错——编码或转码时对不上; 丢帧但没有跳时间戳——音频少了一段,视频照走; 播放器缓冲策略不同——音频缓冲与视频缓冲不等长。 三种里前两种是【文件的问题】,第三种是【播放器的问题】。

四、封装时的重要决定

封装时要决定几件事:流的顺序、索引放在哪、时间戳的基准是多少。 索引放在文件头,就能边下边播;放在文件尾,就必须下完才能播。 这就是 MP4 文件常见的那个坑——一个【快启动】处理就能解决。

五、动手 Demo:两条流是怎么对上的

封装时,视频流和音频流各自带着时间戳,靠它对齐。

rows = [("视频流", "一串视频帧,每帧带自己的时间戳"),
        ("音频流", "一串音频帧,每帧带自己的时间戳"),
        ("PTS", "显示时间戳:这一帧该在什么时候出现"),
        ("DTS", "解码时间戳:这一帧该在什么时候解码"),
        ("索引表", "告诉播放器每一帧在文件的哪个位置")]
for a, b in rows:
    print("%-8s %s" % (a, b))

实跑输出:

--------------------------------------------------------------------
两条流是怎么对上的
--------------------------------------------------------------------

封装时,视频流和音频流各自带着时间戳,靠它对齐。

  元素       作用
  ----------------------------------------------
  视频流     一串视频帧,每帧带自己的时间戳
  音频流     一串音频帧,每帧带自己的时间戳
  PTS        显示时间戳:这一帧该在什么时候出现
  DTS        解码时间戳:这一帧该在什么时候解码
  索引表     告诉播放器每一帧在文件的哪个位置

  **音画不同步,本质就是这两条时间戳对不上。**
  所以排查不同步,先看时间戳,而不是先怀疑解码器。

音画不同步,本质就是这两条时间戳对不上。 所以排查不同步,先看时间戳,而不是先怀疑解码器。

30 秒实验

  1. 用 ffprobe 加 -show_packets 看一个文件的包列表,找出 PTS 与 DTS。
  2. 看有没有哪一帧两个值不相等。

常见误区

  1. 把 PTS 和 DTS 当成同一个东西。有 B 帧时它们就不一样。
  2. 遇到不同步先去改播放器。先确认是文件的问题还是播放器的问题。
自测三问(点开对答案) 1. PTS 和 DTS 分别是什么? —— PTS 是显示时间戳,DTS 是解码时间戳。 2. 为什么有 B 帧时 PTS 与 DTS 会不同? —— B 帧要参考后面的帧,必须先解码、后显示。 3. MP4 的索引放在文件尾会有什么问题? —— 必须把文件下完才能开始播放。

相关笔记