🎬 第十章 SWResample音频重采样理论及案例实战
📄 一句话概括

时长 = 样本数 ÷ 采样率,就这一条,没有第二个公式。 音频的 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 秒实验

  1. 算一下 90112 个样本按 44100 是多少秒,再按 48000 算一遍。
  2. 把 pts 换成「帧号 × 1024」,与真实 pts 对比,看前 88 帧有没有差别。
  3. 打印最后一帧的 pts 加它的样本数,看是不是接近总样本数。

常见误区

  1. 用帧号乘固定帧长算 pts。 帧长可变时会越算越偏。
  2. 以为裸 PCM 自己带时长信息。 全靠参数。
  3. 把时长取整成毫秒再累加。 误差会累积,最后音画不同步。
自测三问(点开对答案) 1. 音频时长怎么算? —— 样本数除以采样率,没有第二个公式。 2. 音频的 pts 含义是什么? —— 从开头数到第几个样本,乘 time_base 就是秒。 3. 为什么 pts 要累加而不是用帧号乘? —— 帧长可能变化,乘法会累积误差。

相关笔记