1. 音视频基础概念:PTS、DTS、时间基与音画同步的底层逻辑
做音视频开发这些年,我见过太多人栽在音画不同步这个坑里。说白了,你看到的画面和听到的声音,本质上是两条独立的数据流。想让它们对齐,就得靠一套精准的时间标尺。今天咱们就把这套标尺拆开看看。
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和时间基,同步的原理其实很简单:
- 解码器按DTS顺序解码每一帧
- 播放器读取每一帧的PTS,换算成实际时间
- 比较当前播放时间和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帧的处理、回绕的检测,任何一个环节出问题,用户看到的就是口型对不上的尴尬画面。