1. 音频同步基础概念:PTS、DTS、时间基(time_base) 核心原理与关系
做音视频开发的朋友,几乎都绕不开「同步」这两个字。尤其是音频同步,搞不好就出现声音超前、画面滞后,或者干脆对不上口型。我刚开始接触 FFmpeg 时,就被 PTS、DTS 这些概念绕晕过。后来踩了不少坑,才慢慢理清楚它们之间的关系。
这一章,我们就从最基础的概念讲起。说白了,PTS、DTS 和时间基就是音视频同步的「三根柱子」。你把这几个东西搞明白了,后面调试同步问题就会顺手很多。
1.1 什么是 PTS 和 DTS?
先看两个缩写:
- PTS(Presentation Time Stamp)——显示时间戳。它告诉解码器:这个音频帧应该在什么时间点被播放出来。
- DTS(Decoding Time Stamp)——解码时间戳。它告诉解码器:这个音频帧应该在什么时间点被解码。
你可能会问:为什么需要两个时间戳?
嗯,这里有个关键点。对于音频来说,大部分情况下 PTS 和 DTS 是相等的。因为音频帧的解码顺序和显示顺序通常一致。但视频就不一样了——B 帧的存在会导致解码顺序和显示顺序不同。不过我们这门课主要讲音频,所以你可以先记住:音频的 PTS 和 DTS 基本一致。
核心记忆点:
- PTS 决定「什么时候播」
- DTS 决定「什么时候解」
- 音频场景下,两者通常相等
1.2 时间基(time_base)是什么?
时间基,英文叫 time_base。它其实就是一个「刻度」的概念。
我举个例子你就明白了。假设你有一把尺子,上面刻的是厘米。那 1 厘米就是这把尺子的「基」。PTS 的值就是在这把尺子上读出来的数字。比如 PTS = 100,就表示 100 厘米。
在 FFmpeg 里,时间基通常用分数表示,比如 1/44100 或 1/1000。这个分数的分母越大,时间精度就越高。
| 时间基 | 含义 | 适用场景 |
|---|---|---|
1/44100 |
每个刻度代表 1/44100 秒 | CD 音质音频(44.1kHz) |
1/48000 |
每个刻度代表 1/48000 秒 | 专业音频(48kHz) |
1/1000 |
每个刻度代表 1 毫秒 | 视频常用时间基 |
1/90000 |
每个刻度代表 1/90000 秒 | MPEG-TS 流 |
我个人习惯把时间基理解成「步长」。步长越小,你能走到的位置就越精确。音频同步的精度,很大程度上就取决于你选的时间基。
1.3 PTS、DTS 和时间基的关系
这三者的关系,可以用一个公式说清楚:
实际时间(秒) = PTS × time_base
举个例子:
- PTS = 44100
- time_base = 1/44100
- 实际时间 = 44100 × (1/44100) = 1 秒
你看,PTS 本身只是一个整数,它本身不代表时间。只有乘上时间基,才能换算成真实的时间。
我在项目中遇到过一个问题:两个音频流的 PTS 看起来差不多,但播放时就是不同步。后来一查,发现它们用的时间基不一样。一个用 1/44100,另一个用 1/48000。PTS 值直接比较是没有意义的,必须先统一时间基。
⚠️ 常见陷阱:
千万不要直接比较两个不同时间基下的 PTS 值。必须先做时间基转换。我曾经因为这个疏忽,浪费了整整一个下午去排查同步问题。
1.4 FFmpeg 中的时间基体系
FFmpeg 里时间基分好几种,新手很容易搞混。我帮你梳理一下:
- AVStream.time_base:流级别的时间基。每个音视频流都有自己的 time_base。
- AVCodecContext.time_base:编解码器的时间基。用于编解码过程中的时间计算。
- AVPacket.pts/dts:数据包的时间戳,单位是流的时间基。
- AVFrame.pts:解码后帧的时间戳,单位也是流的时间基。
你想想看,一个音频流从封装到解码再到播放,中间要经过好几层。每一层都可能涉及时间基的转换。如果转换不对,同步就出问题。
💡 实用技巧:
调试时,我习惯先用 ffprobe 查看流的时间基:
ffprobe -v error -select_streams a:0 -show_entries stream=time_base input.mp3
这样能快速确认时间基是否一致。
1.5 知识体系总览
下面这张图,把这一章的核心知识点串起来了。你可以把它当作一个「地图」,后面每讲一个知识点,都能在这张图上找到位置。
1.6 一个简单的调试案例
最后,我分享一个实际调试中常用的方法。假设你拿到一个音频文件,想确认它的 PTS 是否连续:
ffprobe -v error -select_streams a:0 -show_entries frame=pts,pkt_dts_time input.aac
输出大概长这样:
[FRAME]
pts=0
pkt_dts_time=0.000000
[/FRAME]
[FRAME]
pts=1024
pkt_dts_time=0.021333
[/FRAME]
[FRAME]
pts=2048
pkt_dts_time=0.042667
[/FRAME]
你看,PTS 每次增加 1024。如果时间基是 1/48000,那每帧的时长就是 1024 / 48000 ≈ 0.0213 秒,也就是约 21.3 毫秒。这个值正好对应 48kHz 采样率下每帧的时长。
如果发现 PTS 跳跃不规律,或者出现回退,那基本可以断定同步有问题。
💡 我的习惯:
每次拿到新的音频流,我都会先用 ffprobe 把 PTS 序列导出来,看一眼是不是均匀递增。这一步花不了 10 秒钟,但能帮你省下后面几小时的排查时间。
好了,这一章的内容就到这里。PTS、DTS 和时间基这三个概念,是音频同步的基石。你理解了它们,后面讲同步策略、缓冲机制、漂移补偿时,就会轻松很多。