1. 课程导入

做音频同步问题定位,第一件事不是拿工具抓波形,而是先把脑子里的概念对齐。我见过太多工程师拿着日志说“PTS 跳了”,但问他 PTS 和 DTS 到底谁管谁,说不清楚。嗯,这章我们就先把地基打牢。

1.1 时间基:一切同步的锚点

时间基(Time Base)说白了就是一把尺子。多媒体文件里的时间戳,都是用这把尺子量出来的。常见的尺子有毫秒(ms)、微秒(μs),还有 MPEG 时代留下的 90kHz 时钟。

为什么是 90kHz?

90kHz 是 MPEG 标准里定义的系统时钟频率。它刚好能被常见的视频帧率整除:24、25、30、50、60。这样算 PTS 的时候不会出现小数,避免精度丢失。我在项目中遇到过用 100kHz 做时间基的私有封装,结果每帧都差 0.1 微秒,半小时后音画偏了 3 帧——这种坑踩一次就记住了。

时间基 精度 常见场景
1/90,000 秒(90kHz) ≈ 11.1 μs MPEG-TS、H.264/H.265 封装
1/1,000 秒(毫秒) 1 ms RTMP、FLV、MP4 部分工具链
1/10,000,000 秒(100ns) 100 ns Windows Media、DirectShow

你想想看,如果视频用 90kHz,音频用毫秒,两个时间戳直接做减法,结果一定是乱的。所以第一步永远是确认时间基是否统一。

1.2 PTS 与 DTS:两个时间戳,各管各的事

PTS(Presentation Time Stamp)是“什么时候该显示”。DTS(Decoding Time Stamp)是“什么时候该解码”。对于大部分音频帧来说,PTS 等于 DTS,因为音频帧之间没有依赖关系。但视频不一样——尤其是 B 帧存在的时候。

个人习惯:我排查同步问题时,永远先看 PTS。DTS 乱了一般是编码器或解复用器的问题,但 PTS 乱了一定会直接体现在音画不同步上。

举个例子,一个 GOP 结构是 I-B-P-B-P,解码顺序是 I-P-B-P-B,但显示顺序是 I-B-P-B-P。这时候 DTS 和 PTS 就不一样了。如果解复用器把 DTS 当成 PTS 送出去,画面就会提前或延迟显示。

// 伪代码:PTS 与 DTS 的关系
// 假设帧率 30fps,时间基 90kHz
frame_0 (I): DTS = 0,      PTS = 0
frame_1 (B): DTS = 3000,   PTS = 3000   // 实际显示在 frame_2 之前
frame_2 (P): DTS = 1500,   PTS = 6000   // 解码早,显示晚
frame_3 (B): DTS = 4500,   PTS = 9000
frame_4 (P): DTS = 6000,   PTS = 12000

你看,DTS 是单调递增的,但 PTS 不是。如果播放器按 DTS 送显,B 帧就全乱了。

1.3 音画不同步的宏观分类

我把实际项目中遇到的同步问题分成三类。这样分类不是为了学术,是为了快速定位——拿到问题先对号入座。

  1. 固定偏移型:音频始终比视频快 N 毫秒,或者始终慢 N 毫秒。原因通常是封装时时间基没对齐,或者某个环节做了固定延迟补偿。
  2. 渐进漂移型:刚开始是同步的,播放 10 分钟后开始偏,越偏越多。这是采样率偏差或时钟漂移的典型症状。
  3. 随机跳变型:音画时而同步,时而突然错位,过一会儿又自己恢复。这种最头疼,通常是网络抖动、丢帧重传、或者解复用器 PTS 处理逻辑有 bug。

我曾经遇到一个直播项目,音频始终比视频慢 200ms。排查了两天,最后发现是音频采集设备内部做了 200ms 的降噪缓冲,但 SDK 没把这个延迟告诉封装层。嗯,从那以后我养成了一个习惯:任何音频设备接入,先测端到端延迟。

下面这张图可以帮你快速理解三类问题的区别:

音画不同步宏观分类 固定偏移型 音频始终快/慢 N ms 渐进漂移型 随时间累积偏差 随机跳变型 时好时坏,无规律 常见原因 • 时间基未对齐 • 固定延迟补偿错误 • 封装阶段 PTS 偏移 常见原因 • 采样率偏差(48k vs 48.1k) • 主时钟漂移 • 音视频驱动不同步 常见原因 • 网络丢包/重传 • 解复用器 PTS 处理 bug • 播放器缓冲策略异常 定位原则:先分类,再定位,最后修复 不同分类对应不同的排查工具和修复策略

1.4 时间戳的连续性检查

拿到一个不同步的流,我第一件事就是拉 PTS 曲线。不是看绝对值,而是看差值。音频 PTS 差值应该等于音频帧时长,视频 PTS 差值应该等于视频帧间隔。如果差值忽大忽小,那问题大概率出在封装或传输环节。

一个小技巧:用 ffprobe 把 PTS 导出来,扔到 Excel 里画折线图。斜率稳定的就是正常的,斜率突变的地方就是问题点。我靠这个方法在半小时内定位过一个直播推流 SDK 的 PTS 重置 bug。

# 导出视频帧 PTS 到文本
ffprobe -v error -select_streams v:0 -show_entries frame=pkt_pts_time -of csv=p=0 input.mp4 > video_pts.txt

# 导出音频帧 PTS
ffprobe -v error -select_streams a:0 -show_entries frame=pkt_pts_time -of csv=p=0 input.mp4 > audio_pts.txt

然后计算相邻帧的 PTS 差值。如果视频是 30fps,理想差值应该是 33.33ms。如果出现 66ms 或 16ms,说明有丢帧或重复帧。音频同理,48kHz 采样率、每帧 1024 个采样,理想差值应该是 21.33ms。

1.5 小结

这一章我们聊了时间基、PTS、DTS 和同步问题的宏观分类。说白了,同步问题的根因就两个:时间戳算错了,或者时间戳用错了。后面的章节我们会深入每个环节,从采集到渲染,一步步拆解。

嗯,概念部分就到这里。记住一句话:先确认时间基,再检查 PTS 连续性,最后对号入座找分类。这三步走完,80% 的问题已经能定位到方向了。