3. RTP时间戳与时钟同步:音频与视频的RTP时间戳单位、NTP时间戳的作用、RTP与NTP的映射关系
音画同步这件事,说白了就是让音频和视频在时间线上对齐。但音频和视频的时钟天生就不一样——音频采样率可能是48kHz,视频帧率可能是30fps,它们各自的时间刻度完全不同。那WebRTC是怎么把它们拉到一起的?答案就在RTP时间戳和NTP时间戳的配合上。
我个人习惯把RTP时间戳看作“本地节拍器”,而NTP时间戳是“全局挂钟”。两者缺一不可。下面我们一个一个拆开看。
3.1 音频与视频的RTP时间戳单位
RTP时间戳的单位不是毫秒,也不是微秒。它取决于媒体流的采样率。这一点很多人刚接触时会搞混,我当年也踩过坑。
核心规则:RTP时间戳的增量 = 采样周期 × 采样率。
换句话说,每采集一个样本,时间戳就加1。
3.1.1 音频的RTP时间戳
音频的采样率通常是固定的。常见的有:
- 8kHz(电话语音)
- 16kHz(宽带语音)
- 48kHz(Opus 编码,WebRTC 默认)
举个例子。假设音频采样率是48kHz,每帧包含480个样本。那么每帧的RTP时间戳增量就是480。如果第一帧的时间戳是0,第二帧就是480,第三帧960,以此类推。
我在项目中遇到过一个问题:有人把音频时间戳按毫秒来算,结果音画不同步越来越严重。原因就是单位搞错了——音频时间戳的单位是“样本数”,不是毫秒。
小技巧:音频RTP时间戳转毫秒的公式很简单:毫秒 = 时间戳差值 / 采样率 × 1000。比如480个样本在48kHz下就是10ms。
3.1.2 视频的RTP时间戳
视频的RTP时间戳单位是“时钟周期”,时钟频率固定为90kHz。为什么是90kHz?因为它是常见帧率(24、25、30、60fps)的最小公倍数,能保证整数增量。
举个例子:
- 30fps:每帧间隔 = 90000 / 30 = 3000 个时钟周期
- 60fps:每帧间隔 = 90000 / 60 = 1500 个时钟周期
- 25fps:每帧间隔 = 90000 / 25 = 3600 个时钟周期
你看,都是整数。这就是90kHz的妙处。
注意:视频RTP时间戳的初始值通常是随机的,不要假设从0开始。接收端只关心时间戳的差值,不关心绝对值。
3.2 NTP时间戳的作用
RTP时间戳只能告诉你“这一帧和上一帧之间隔了多少样本”,但它不知道“现在是几点几分”。这就是NTP时间戳登场的地方。
NTP时间戳是绝对时间,单位是秒,精度高达232分之一秒。它来自NTP时钟同步协议,所有参与通话的端都能拿到同一个时间基准。
NTP时间戳在RTCP SR(Sender Report)中携带。发送端会定期(通常每1-2秒)发送一次SR包,里面包含:
- NTP时间戳:当前绝对时间
- RTP时间戳:与NTP时间戳对应的RTP时间戳值
- 发送包计数:已发送的RTP包总数
- 发送字节数:已发送的RTP负载总字节数
有了这个映射关系,接收端就能把RTP时间戳换算成绝对时间,从而实现音画同步。
一句话总结:RTP时间戳管“相对顺序”,NTP时间戳管“绝对时刻”。两者结合,才能知道音频和视频是否在同一个时间点上。
3.3 RTP与NTP的映射关系
映射关系其实就是一个线性方程。假设在某个时刻,SR包告诉我们:
- NTP时间戳 = NTP0
- RTP时间戳 = RTP0
那么对于任意一个RTP时间戳 RTPx,对应的NTP时间戳就是:
NTP_x = NTP_0 + (RTP_x - RTP_0) / 采样率
注意这里的采样率:音频用实际采样率(如48000),视频固定用90000。
举个例子。假设视频SR包中:
- NTP0 = 1000.0 秒
- RTP0 = 0
现在收到一个视频帧,RTP时间戳 = 3000。那么它的绝对时间就是:
NTP_x = 1000.0 + (3000 - 0) / 90000 = 1000.0333 秒
也就是1000秒又33.3毫秒。
同样的方法可以算出音频帧的绝对时间。然后比较两者的NTP时间戳,差值在±40ms以内就算同步。
实战经验:我曾经调试过一个音画不同步的bug,发现接收端把视频的采样率误用成了48000而不是90000,导致时间换算差了将近一倍。排查了整整一天才找到问题。所以,采样率一定要从SDP或编码参数中正确获取,不要硬编码。
3.4 整体映射流程
下面这张图展示了从发送端到接收端的完整时间戳映射流程:
3.5 常见问题与避坑
在实际开发中,时间戳相关的问题非常隐蔽。我总结了几条高频踩坑点:
| 问题 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 音频时间戳单位错误 | 音画不同步随时间加剧 | 误用毫秒而非样本数 | 确认采样率,用样本数计算 |
| 视频采样率错误 | 视频播放速度异常 | 误用48000而非90000 | 视频固定使用90000Hz |
| SR包丢失 | 接收端无法换算绝对时间 | 网络丢包导致映射对缺失 | 缓存历史SR,或使用RTP时间戳差值推算 |
| NTP时钟不同步 | 音画同步抖动 | 发送端和接收端NTP时间基准不一致 | 部署NTP服务,确保时钟同步 |
| 时间戳回绕 | 时间戳突然变小 | 32位RTP时间戳溢出 | 使用无符号32位比较,或扩展为64位 |
特别提醒:我曾经遇到过一个案例,接收端在计算音画同步时,直接拿音频和视频的RTP时间戳做减法。结果当然是一团糟——因为两者的单位不同。音频是样本数,视频是90kHz时钟周期,直接减毫无意义。正确的做法是先把两者都换算到NTP时间,再比较。
3.6 小结
RTP时间戳和NTP时间戳的配合,是WebRTC音画同步的基石。音频用实际采样率,视频用90kHz固定时钟,两者通过RTCP SR中的映射对统一到NTP绝对时间。接收端拿到这个映射关系后,就能算出每一帧的绝对时刻,从而判断音画是否对齐。