1. 课程导入

做音视频开发这些年,我见过太多人栽在音画不同步这个坑里。说白了,你看到的画面和听到的声音,本质上是两条独立的数据流。想让它们对齐,就得靠一套精准的时间标尺。今天咱们就把这套标尺拆开看看。

1.1 为什么需要时间戳?

想象一下这个场景:你录了一段视频,摄像头每秒抓取30帧画面,麦克风每秒采集48000个音频样本。这两路数据各自独立编码、打包、传输。到了播放端,如果没有统一的时间标记,画面和声音就会各走各的路。

我早期做过一个直播项目,上线后用户反馈口型对不上。排查了半天,发现是推流端的时间戳单位搞混了——视频用毫秒,音频用微秒。嗯,这种低级错误,犯过一次就再也不会忘了。

核心结论:时间戳是音视频同步的基石。没有时间戳,解码器不知道什么时候该显示哪一帧,播放器也不知道什么时候该出声。

1.2 PTS 与 DTS:两个容易混淆的概念

先看定义:

  • PTS(Presentation Time Stamp)——显示时间戳,告诉解码器「这一帧该在什么时候显示」
  • DTS(Decoding Time Stamp)——解码时间戳,告诉解码器「这一帧该在什么时候解码」

你可能会问:解码和显示的时间还能不一样?

会的。这跟视频编码的帧类型有关。

帧类型 全称 特点 依赖关系
I帧 Intra Frame 完整图像,可独立解码 无依赖
P帧 Predictive Frame 参考前一帧的差异数据 依赖前一个I/P帧
B帧 Bidirectional Frame 参考前后帧的差异数据 依赖前后I/P帧

B帧是导致PTS和DTS不一致的元凶。因为B帧需要参考后面的帧才能解码,所以解码顺序和显示顺序是错开的。

举个例子:

显示顺序(PTS): I1  B2  B3  P4  B5  B6  P7
解码顺序(DTS): I1  P4  B2  B3  P7  B5  B6

看到没?P4在显示顺序里排第4,但在解码顺序里排第2。因为B2和B3需要P4的数据才能解出来。

实战经验:我在做播放器内核时,遇到过一种情况——某些编码器会把B帧的PTS和DTS设成相同值。这其实是编码器偷懒,把B帧当P帧用了。虽然能播放,但压缩率会下降不少。

1.3 时间基(Time Base):时间戳的刻度尺

时间戳本身只是一个整数,比如3600。但3600是什么?3.6秒?36秒?还是3600秒?

这就需要时间基来定义了。

时间基通常用分数表示,比如1/90000。意思是每个时间戳单位代表1/90000秒。那么时间戳3600就代表:

3600 × (1/90000) = 0.04秒 = 40毫秒

常见的几种时间基:

时间基 精度 常见场景
1/1000 1毫秒 MP4容器、RTMP流
1/90000 约11微秒 H.264/H.265视频流
1/48000 约20.8微秒 AAC音频流(48kHz采样率)
1/1000000 1微秒 某些专业设备

这里有个坑:不同媒体流的时间基可能不一样。视频用1/90000,音频用1/48000。播放器必须把它们统一换算到同一个时间基准上,才能做同步。

注意:时间基换算时容易出现精度丢失。比如把1/90000的时间戳转成毫秒,除不尽就会产生误差。累积多了,音画就偏了。我一般建议用64位整数做中间计算,保留足够精度。

1.4 音画同步的底层逻辑

有了PTS和时间基,同步的原理其实很简单:

  1. 解码器按DTS顺序解码每一帧
  2. 播放器读取每一帧的PTS,换算成实际时间
  3. 比较当前播放时间和PTS的差值,决定是等一等还是追一追

具体策略分三种:

同步模式 主时钟 适用场景
视频同步到音频 音频时钟 最常见,音频连续性要求高
音频同步到视频 视频时钟 视频帧率严格固定的场景
外部时钟同步 系统时钟 多设备协同播放

大部分播放器默认用「视频同步到音频」。为什么?因为人耳对声音的断续更敏感。画面卡一下还能忍,声音断断续续就完全没法听了。

下面这张图展示了音视频同步的核心流程:

音视频同步核心流程 视频流(H.264/H.265) 音频流(AAC/MP3) 解封装 → 提取PTS/DTS 解封装 → 提取PTS 视频解码(按DTS顺序) 音频解码 同步引擎 比较PTS与主时钟 → 计算差值 → 决定策略 音画同步输出

1.5 实际项目中的避坑指南

这些年踩过的坑,我总结了几条:

  • 时间基不统一——视频和音频的时间基不同,换算时精度丢失。我建议统一用微秒(1/1000000)做内部计算。
  • B帧导致PTS乱序——解码器输出帧的顺序可能和PTS顺序不一致。播放器必须按PTS重排后再送显。
  • 起始时间戳不为0——有些封装格式的PTS不从0开始。播放器需要做归一化处理,否则首帧就偏了。
  • 时间戳回绕——32位时间戳最大只能表示约4.29秒(1/90000精度下)。超过后会回绕到0。现在主流方案都用64位时间戳。

我的习惯:在项目初期就定好时间戳规范。视频用1/90000,音频用1/48000,内部统一转成纳秒。这样不管对接什么格式,换算逻辑都清晰。

1.6 小结

PTS决定什么时候显示,DTS决定什么时候解码,时间基决定时间戳的刻度。这三者配合好了,音画同步就稳了。

说白了,同步不是什么高深算法,就是比大小、算差值、做调整。但细节决定成败——时间基换算的精度、B帧的处理、回绕的检测,任何一个环节出问题,用户看到的就是口型对不上的尴尬画面。