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 知识体系总览

下面这张图,把这一章的核心知识点串起来了。你可以把它当作一个「地图」,后面每讲一个知识点,都能在这张图上找到位置。

音频同步核心概念关系图 PTS(显示时间戳) DTS(解码时间戳) time_base(时间基) 核心公式: 实际时间(秒) = PTS × time_base 音频场景下,PTS ≈ DTS(解码顺序 = 显示顺序) 常见时间基 1/44100 → 44.1kHz 音频 1/48000 → 48kHz 音频 1/1000 → 毫秒级精度 1/90000 → MPEG-TS 流 ⚠️ 常见陷阱 • 不同时间基的 PTS 不能直接比较 • 转换前先统一时间基 • 音频 PTS 通常单调递增 • 注意封装格式对时间基的影响

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 和时间基这三个概念,是音频同步的基石。你理解了它们,后面讲同步策略、缓冲机制、漂移补偿时,就会轻松很多。