1. 课程导入

音画同步,说白了就是让声音和画面在时间线上对齐。你想想看,一部电影里演员张嘴说话,声音却慢了半拍,那种感觉有多别扭。我刚开始做流媒体的时候,就经常被这种问题折磨得够呛。

要搞懂音画同步,必须先理解两个核心概念:PTS 和 DTS。这两个东西,是 FFmpeg 里所有时间相关操作的基石。

1.1 PTS 和 DTS 到底是什么?

PTS(Presentation Time Stamp),展示时间戳。它告诉播放器:这一帧画面或这一段音频,应该在什么时间点展示给用户。

DTS(Decoding Time Stamp),解码时间戳。它告诉解码器:这一帧数据,应该在什么时间点开始解码。

你可能会问:为什么要有两个时间戳?直接一个不行吗?

嗯,这里要注意。对于大多数视频编码格式,尤其是 H.264 和 H.265,帧的解码顺序和展示顺序是不一致的。举个例子:

  • B 帧(双向预测帧)需要参考前后的帧才能解码。
  • 所以解码器必须先解码后面的 P 帧,才能回头解码 B 帧。
  • 这就导致 DTS 和 PTS 不一样。

我遇到过不少新手,直接用 DTS 当 PTS 用,结果画面顺序全乱了。说白了,PTS 是给用户看的,DTS 是给解码器用的,千万别搞混。

核心结论:

  • PTS ≥ DTS,对于没有 B 帧的编码(如 MPEG-2 简单档次),PTS = DTS。
  • PTS > DTS,对于有 B 帧的编码(如 H.264 High Profile),PTS 和 DTS 会错开。

1.2 时间基(Time Base)—— 别被单位搞晕

PTS 和 DTS 的值本身只是一个整数,它需要配合时间基(time_base)才能换算成真实的时间。

举个例子:

  • 如果 time_base = 1/25,那么 PTS = 25 就代表 1 秒。
  • 如果 time_base = 1/90000,那么 PTS = 90000 才代表 1 秒。

我个人习惯在调试时,先把所有时间戳统一换算成毫秒或微秒,这样对比起来更直观。FFmpeg 里常用的时间基有:

时间基 常见场景 精度
1/25 PAL 制式视频 40 ms
1/30 NTSC 制式视频 33.33 ms
1/1000 毫秒级精度 1 ms
1/90000 MPEG-TS 流 11.11 μs

小技巧:用 FFmpeg 命令行查看流信息时,可以用 ffprobe -show_streams input.mp4,直接看 time_base 字段。我经常用这招快速定位时间基不匹配的问题。

1.3 音画不同步的常见原因

搞懂了 PTS 和 DTS,我们来看看实际项目中,音画不同步到底是怎么发生的。我总结了几种最常见的场景:

1.3.1 编码延迟

编码器在处理视频帧时,需要时间。尤其是硬件编码器,它可能会把几帧攒在一起处理,导致输出时间戳出现偏移。我曾经在一个直播项目里,用 NVIDIA 的 NVENC 编码器,结果发现音频已经播放了,视频还在编码队列里排队。嗯,这就是典型的编码延迟导致的不同步。

  • 表现:声音先出来,画面慢半拍。
  • 原因:编码器内部缓冲,或者编码复杂度太高。
  • 解决思路:降低编码延迟,或者引入音视频时间戳补偿机制。

1.3.2 网络抖动

这个在直播和点播场景里都很常见。网络传输不稳定,导致数据包到达时间不一致。播放器如果处理不好,就会出现画面卡一下,然后突然快进,声音却还在正常播放。

  • 表现:画面忽快忽慢,声音正常。
  • 原因:网络延迟、丢包、抖动。
  • 解决思路:使用 jitter buffer(抖动缓冲),或者调整播放器的时钟同步策略。

1.3.3 容器封装问题

容器格式(如 MP4、MKV、FLV)在封装音视频流时,时间戳信息可能被错误写入。我遇到过最离谱的一次,是一个 MP4 文件的音频轨 time_base 写成了 1/1000,视频轨却写成了 1/90000,结果播放器直接懵了。

  • 表现:从头到尾都不同步,或者越播越不同步。
  • 原因:封装工具 bug、手动修改时间戳、多段拼接时时间基不一致。
  • 解决思路:用 FFmpeg 重新 remux,或者手动修正时间基。

避坑指南:我曾经在做一个多机位录制项目时,用 FFmpeg 拼接不同摄像头的视频流。结果发现每个摄像头的时钟源不一样,导致时间戳乱跳。后来我强制用 -vsync 2 和 -async 1 参数重新同步,才勉强解决。记住:多源输入时,一定要统一时间基。

1.4 知识体系总览

下面这张图,是我自己梳理的音画同步知识体系。你可以把它当作本章的思维导图:

音画同步知识体系 PTS / DTS Time Base 编码延迟 网络抖动 容器封装问题 降低编码延迟 Jitter Buffer Remux / 修正时间基 核心原则 PTS 决定展示时间,DTS 决定解码时间 统一时间基是解决不同步的第一步

1.5 如何用 FFmpeg 快速检查音画同步状态?

理论说完了,来点实战的。我平时排查音画同步问题,最常用的命令是:

ffprobe -v error -show_entries stream=index,codec_type,start_pts,time_base -of default=noprint_wrappers=1 input.mp4

这条命令会输出每个流的起始 PTS 和时间基。如果音频和视频的起始 PTS 相差太大(比如超过一个帧的时长),那基本可以断定有同步问题。

另一个更直观的方法:

ffmpeg -i input.mp4 -vf "drawtext=text='%{pts\:hms}':x=10:y=10:fontsize=24:fontcolor=white" -an output.mp4

这个命令会在视频画面上叠加当前帧的 PTS 时间。你播放输出文件,同时听音频,就能肉眼判断音画是否同步。我经常用这招给客户演示问题。

个人经验:如果发现音画不同步,先别急着调参数。第一步永远是确认时间基是否一致。我见过太多人花几个小时调编码参数,最后发现只是容器封装时 time_base 写错了。

好了,这一章的内容就到这里。PTS、DTS 和时间基,是音画同步的三大基石。后面的章节,我们会深入每个具体场景,看看 FFmpeg 里有哪些参数可以帮我们解决实际问题。