采样率与时间戳关系:音频采样率与视频帧率
好,咱们进入第二章。这一章要聊的是采样率、帧率跟时间戳之间的那点事儿。说白了,就是搞清楚「一个时间戳增量到底对应多少物理时间」。这个问题搞不明白,后面做音画同步就是空中楼阁。
我记得刚入行那会儿,有个老同事跟我说过一句话,我一直记着:「时间戳就是音视频的脉搏」。你想想看,脉搏乱了,人就不行了。时间戳乱了,流就废了。
2.1 音频采样率:时间戳的「心跳」
音频采样率,单位是 Hz,意思就是每秒钟采多少个样点。常见的就这么几个:
| 采样率 | 每秒样点数 | 常见场景 |
|---|---|---|
| 8 kHz | 8000 | VoIP、电话语音 |
| 16 kHz | 16000 | 宽带语音、部分会议系统 |
| 44.1 kHz | 44100 | CD 音质、音乐流媒体 |
| 48 kHz | 48000 | 影视制作、专业音频、AC-3/DTS |
这里有个关键点:RTP 时间戳的单位不是「秒」,而是「采样周期」。什么意思?
对于音频来说,时间戳每增加 1,就代表过去了 1 个采样点的时间。那 1 个采样点的时间是多少?
1 个采样点的时间 = 1 / 采样率(秒)
举例:
8 kHz → 1 个样点 = 125 µs
48 kHz → 1 个样点 ≈ 20.83 µs
所以,如果你一帧音频有 480 个样点,采样率是 48 kHz,那这一帧的时间戳增量就是 480。物理时长呢?480 / 48000 = 10 ms。
核心公式:
时间戳增量 = 每帧样点数
物理时长(秒)= 每帧样点数 / 采样率
我在项目中遇到过有人把 8 kHz 音频的时间戳增量算成了 480,结果播放器疯狂快进。为什么?480 / 8000 = 60 ms,但接收端按 10 ms 去等,当然对不上。
2.2 视频帧率:时间戳的「节拍」
视频这边稍微绕一点。视频的 RTP 时间戳单位也是「采样周期」,但视频的采样率是固定的 90 kHz。这是历史原因——当年 MPEG 标准定的,90 kHz 能整除 24、25、30、50、60 这些常见帧率,算起来方便。
常见的帧率对应的时间戳增量:
| 帧率(fps) | 每帧时长 | 时间戳增量(90 kHz 时钟) |
|---|---|---|
| 24 | 41.666... ms | 3750 |
| 30 | 33.333... ms | 3000 |
| 60 | 16.666... ms | 1500 |
怎么算的?很简单:
时间戳增量 = 90000 / 帧率
24 fps → 90000 / 24 = 3750
30 fps → 90000 / 30 = 3000
60 fps → 90000 / 60 = 1500
嗯,这里要注意:视频的时间戳增量必须是整数。如果除不尽怎么办?比如 29.97 fps(NTSC 制式),90000 / 29.97 不是整数。实际做法是取整,然后用帧率补偿机制去微调。这个后面章节会细讲。
我曾经踩过的坑:
有一次做视频会议网关,需要把 30 fps 的视频转成 24 fps。我直接改了帧率字段,没重新计算时间戳。结果接收端看到时间戳增量从 3000 变成了 3750,以为发生了丢包,疯狂做丢包重传。网络直接炸了。
教训:转帧率必须重新生成时间戳序列,不能只改头字段。
2.3 时间戳增量计算:一张图说清楚
下面这张图把音频和视频的时间戳计算逻辑放在一起对比,你看完应该就全明白了。
2.4 实战中的增量计算
光讲理论不行,咱们来点实际的。假设你正在写一个 RTP 打包器,音频是 16 kHz、每帧 320 个样点,视频是 24 fps。时间戳应该怎么给?
// 音频:16 kHz, 320 样点/帧
audio_ts_increment = 320; // 时间戳增量
audio_frame_duration = 320.0 / 16000 * 1000; // = 20 ms
// 视频:24 fps
video_ts_increment = 90000 / 24; // = 3750
video_frame_duration = 3750.0 / 90000 * 1000; // ≈ 41.67 ms
然后发送的时候,每一帧的时间戳就是累加:
// 第一帧
audio_ts = random_initial_value;
video_ts = random_initial_value;
// 第二帧
audio_ts += 320;
video_ts += 3750;
// 第三帧
audio_ts += 320;
video_ts += 3750;
// ... 以此类推
我个人习惯:
初始时间戳不要从 0 开始。RTP 规范建议随机初始化,防止多个流的时间戳冲突。我一般用 rand() 生成一个 32 位随机数作为起点。
2.5 常见误区与避坑
做音视频开发这么多年,我见过太多人在时间戳增量上翻车。列几个最常见的:
- 误区一:音频时间戳增量用「毫秒」算
有人觉得 20 ms 的音频帧,时间戳增量就是 20。错!音频时间戳的单位是样点,不是毫秒。20 ms 在 48 kHz 下是 960,在 8 kHz 下是 160。 - 误区二:视频时间戳增量用帧率直接除 1000
30 fps 的帧间隔是 33.33 ms,有人直接写 33 作为增量。但视频时钟是 90 kHz,正确值是 3000。差了两个数量级。 - 误区三:音频和视频用同一个时间戳时钟
音频的时钟是采样率,视频的时钟是 90 kHz。两者不能混用。混用的后果就是音画不同步,而且偏差会越来越大。
我曾经踩过的坑:
有一次做多路混音,我把两路 48 kHz 和 44.1 kHz 的音频流直接按同样的时间戳增量发送。结果混音器那边发现时间戳跳跃不一致,直接丢弃了其中一路。后来我学乖了:不同采样率的流,必须各自维护独立的时间戳计数器。
2.6 小结
这一章的核心就三句话:
- 音频时间戳增量 = 每帧样点数,物理时长 = 样点数 / 采样率
- 视频时间戳增量 = 90000 / 帧率,物理时长 = 增量 / 90000
- 音频和视频的时间戳时钟不同,不能混用