时长 = 样本数 ÷ 采样率,就这一条,没有第二个公式。 音频的 pts 就是「第几个样本」,所以时间戳直接读得出秒数。
10.2.2 讲样本怎么排,这一节讲怎么换算成时间。 视频那边有三把尺子,音频只有一把——这是它省心的地方。
一、唯一公式
两个数就能算出时间。 样本数 采样率 时长
8000 8000 1.000000 秒 44100 44100 1.000000 秒 1024 8000 0.128000 秒 1000 48000 0.020833 秒
对比视频那边要乘 time_base、要管帧率、要管三把尺子,音频这边一个除法就完了。 原因是音频的时间刻度天然就是「一个样本」, 而视频的「一个单位」是什么,得看那一路流的 time_base。
二、帧长不是固定的
同一路音频,每帧样本数可能一样,也可能不一样。 编码 一帧多少样本 是否固定
AAC 1024 固定 MP3 1152 大体固定 Opus 不定(常见 960) 可变
素材这条音轨是 AAC,88 帧、每帧 1024 个样本。 所以它的样本数永远是 1024 的整数倍。 别把这个当普遍规律:换成 Opus 或某些编码器,每帧样本数会变,算时长就只能累加、不能乘。
三、pts 就是第几个样本
音频的 pts 特别好懂:它就是「从开头数到第几个样本」。 第几帧 样本数 pts 乘 time_base
0 1024 0 0.000000 秒 1 1024 1024 0.023220 秒 2 1024 2048 0.046440 秒
pts 每次加 1024,正好是一帧的样本数。 而 time_base 是 1/44100,乘一下就是秒数。 所以音频的「时间戳」其实是「样本序号」, 这也是为什么音频重采样之后 pts 必须重新算——样本数变了。 这一条在 10.3.2 会变成一个具体的坑:转完采样率如果不重算 pts,时长就会错。
四、裸 PCM 的时长全靠参数
同一个文件,两种读法,两个时长。 读法 样本数 时长
按 8000 读 8000 1.0000 秒 按 44100 读(默认) 8000 0.1814 秒
文件一个字都没改,时长差了五倍多。 这就是「裸 PCM 没有头」的代价。 实践里的判法:先拿文件字节数除以(声道 × 每样本字节)得样本数, 再猜采样率,试几个常见的(8000/16000/44100/48000),听哪个不变调就是哪个。
五、边界与取整
算时长时,取整方向要小心。 场合 怎么取整 为什么
算时长 不取整,用浮点 1 个样本也要算进去 算样本数 四舍五入 少一个就短了点 算 pts 累加,不要乘 避免误差累积
第三条最实用:一帧一帧往下走时, pts 用「上一帧 pts + 这一帧样本数」累加,别用「帧号 × 1024」。 因为帧长不一定恒定,乘法在变长帧上会越算越偏。这是音频时间戳最容易错的地方。
六、动手 Demo:音频的时间是怎么算出来的
先算几组样本数对应的时长,再把素材的帧长、pts、时间基全打出来核对。
import av
for n, r in ((8000, 8000), (44100, 44100), (1024, 8000), (1000, 48000)):
print(n, r, n / float(r))
c = av.open("tiny.mp4")
st = c.streams.audio[0]
sizes = []
pts = []
for pk in c.demux(st):
if pk.size == 0:
break
for fr in st.codec_context.decode(pk):
sizes.append(fr.samples)
if len(pts) < 3:
pts.append((fr.pts, float(fr.pts * fr.time_base)))
print(len(sizes), sorted(set(sizes)), sum(sizes))
print(pts)
print(st.time_base)
c.close()
实跑输出:
--------------------------------------------------------------------
音频的时间是怎么算出来的
--------------------------------------------------------------------
**① 唯一公式:样本数除以采样率**
样本数 采样率 时长
-------------------------------------
8000 8000 1.000000 秒
44100 44100 1.000000 秒
1024 8000 0.128000 秒
1000 48000 0.020833 秒
**没有第二个公式。** 视频那边还有帧率、时间基一堆事,
**音频这边只要两个数**——这就是音频时间好算的原因。
**② 帧长不是固定的,但 AAC 是**
项 值
-----------------------------
帧数 88
每帧样本数 [1024]
是否固定 是
合计 90112 样本
**AAC 一帧固定 1024 个样本**,所以 88 帧的样本数永远是 1024 的整数倍。
**换成 MP3 就不一定了**,MP3 一帧 1152 个,但编码器可能补零。
**③ pts 与 time_base**
第几帧 样本数 pts pts 乘 time_base
---------------------------------------------------
0 1024 0 0.000000 秒
1 1024 1024 0.023220 秒
2 1024 2048 0.046440 秒
**音频的 pts 就是「从这个位置开始的第几个样本」**,time_base 是 1/采样率,
所以 pts 直接就读得出时间。**这是音频比视频省心的地方。**
**④ 帧 pts 与实际时间对得上吗**
第 2 帧 pts=2048,按 1/44100 换算 0.046440 秒;而它前面已经过了 2048 个样本、也就是 0.046440 秒。
**两个数一样,说明 pts 就是累计样本数。**
每帧都是 1024 个样本,所以 pts 每次加 1024,乘 1/44100 就是秒。 换成变长帧(如 Opus)就不能这么算,得逐帧累加。
30 秒实验
- 算一下 90112 个样本按 44100 是多少秒,再按 48000 算一遍。
- 把 pts 换成「帧号 × 1024」,与真实 pts 对比,看前 88 帧有没有差别。
- 打印最后一帧的 pts 加它的样本数,看是不是接近总样本数。
常见误区
- 用帧号乘固定帧长算 pts。 帧长可变时会越算越偏。
- 以为裸 PCM 自己带时长信息。 全靠参数。
- 把时长取整成毫秒再累加。 误差会累积,最后音画不同步。
自测三问(点开对答案)
1. 音频时长怎么算? —— 样本数除以采样率,没有第二个公式。 2. 音频的 pts 含义是什么? —— 从开头数到第几个样本,乘 time_base 就是秒。 3. 为什么 pts 要累加而不是用帧号乘? —— 帧长可能变化,乘法会累积误差。- ☐ (本节完,下一节 10.2.4 C语言分离左右声道)