十二个样本格式:六种精度乘两种存法。
带 p 的是 planar,不带的是 packed——这个后缀到处通用。
10.1 讲的是「PCM 是什么」,这一节讲 FFmpeg 给它起了哪些名字。 10.2.2 专门讲两种存法的差别。
一、十二个名字
六种精度,两种存法,乘起来十二个。 名字 位数 每样本字节 存法
u8 8 1 packed s16 16 2 packed s32/flt 32 4 packed s64/dbl 64 8 packed 上面五个的 p 版 同左 同左 planar
十二个名字全都能构造出来,一个不缺。
u 是无符号整数,s 是有符号整数,flt/dbl 是浮点。
u8 是唯一常见的无符号格式,多用于老式声卡与某些 WAV。
它有 128 的偏置:静音不是 0,是 128。这一条经常被人忽略。
二、属性直接问对象
别背表,问对象。 属性 含义
bits 每个样本多少位 bytes 每个样本多少字节 is_packed 是不是交错存 is_planar 是不是分平面存
s16 与 s16p 的 bits 与 bytes 完全一样,只差存法。
所以「换格式」有两种完全不同的意思:换精度,或换存法。
这两种换法在代码里长得一样(都是传一个字符串给重采样器),但代价完全不同:换精度要舍入,
换存法只是搬家。
三、解码器默认给哪一种
不是 s16,这一点很多人猜错。 来源 解码后的格式
AAC fltp(32 位浮点、planar) MP3 fltp 或 s16p WAV(s16) s16(packed)
AAC 解出来是 fltp。 因为压缩算法内部就是浮点算的,
解码器直接把中间结果给你,省一次转换。
所以「拿到帧先看格式」不是小心眼,是必需动作。
默认按 s16 去读 fltp 的数据,读出来全是垃圾。
四、名字写错会怎样
报错信息还算清楚,但容易看漏。 写法 结果
s16 能构造 s16le 构造不出来,那是「编码名」不是「格式名」 S16 大小写敏感,失败
s16 是「样本格式」,pcm_s16le 是「编解码器名」,
两套命名体系不能混用。 装裸 PCM 时用后者,处理帧时用前者。
这条区分在第六章也出现过:格式名(容器/设备)与编解码器名是两个命名空间。
FFmpeg 里到处都有这种「两层名字」,混了就报「找不到」。
五、怎么在前两者之间选
一句话:中间处理用浮点,存盘交付用整数。 场合 选什么 为什么
内部处理 fltp 不丢精度,算得快 存盘交付 s16 体积小一半,兼容性好 送硬件 看设备要求 多数要 s16 packed
浮点的好处是「怎么算都不溢出」:整数相乘很容易超出范围, 要时时想着限幅;浮点只用管 −1.0 到 1.0。 代价是体积翻倍、转回整数时要舍入。 所以「进来就转浮点、出去再转整数」是很常见的写法。
六、动手 Demo:FFmpeg 认得哪些样本格式
把十二个名字逐个构造出来,看位数、字节数与存法;再查真实音轨用的是哪个。
import av
names = ["u8", "s16", "s32", "flt", "dbl", "s64",
"u8p", "s16p", "s32p", "fltp", "dblp", "s64p"]
for n in names:
f = av.audio.format.AudioFormat(n)
print(n, f.bits, f.bytes, f.is_packed, f.is_planar)
c = av.open("tiny.mp4")
print(c.streams.audio[0].codec_context.format.name)
c.close()
# 名字混用会怎样:s16 是样本格式,pcm_s16le 是编解码器名
try:
av.audio.format.AudioFormat("s16le")
except Exception as e:
print("s16le 构造失败", type(e).__name__)
实跑输出:
--------------------------------------------------------------------
FFmpeg 认得哪些样本格式
--------------------------------------------------------------------
**① 六种「精度」乘两种「存法」,一共十二个名字**
名字 位数 每样本字节 存法
-------------------------------------------
u8 8 1 packed
s16 16 2 packed
s32 32 4 packed
flt 32 4 packed
dbl 64 8 packed
s64 64 8 packed
u8p 8 1 planar
s16p 16 2 planar
s32p 32 4 planar
fltp 32 4 planar
dblp 64 8 planar
s64p 64 8 planar
**十二个名字全都能构造出来,一个不缺。** 带 `p` 后缀的是 planar,不带的是 packed。
**这个后缀写法在 FFmpeg 里到处通用。**
**② 属性直接问对象,不用背**
属性 s16 s16p
---------------------------------
bits 16 16
bytes 2 2
is_packed True False
is_planar False True
**`s16` 与 `s16p` 的位数、字节数完全一样,只差存法。**
**所以「换格式」有两种完全不同的意思**:换精度(s16→flt),或换存法(s16→s16p)。
**③ 真实音轨用的是哪一种**
项 值
---------------------------------------
编码 aac
解码后的样本格式 fltp
是多少位 32 位浮点
是 packed 还是 planar planar
**AAC 解出来是 `fltp`(32 位浮点、planar)。** 记住这个事实:**解码器给你的默认不是 s16**,
想用整数得自己转。
十二个都能构造,带 p 的是 planar,is_packed 与 is_planar 直接读。
AAC 解出来是 fltp,不是 s16——默认按整数读会读错。
30 秒实验
- 把
s16le换成pcm_s16le再试一次,看能不能构造(提示:还是不行,它是编解码器名)。 - 打印
av.audio.format.AudioFormat("u8").bits,想一想静音应该是多少。 - 用
dbl建一帧再to_ndarray(),看数组的 dtype 是什么。
常见误区
- 把
s16与pcm_s16le当成同一个名字。 一个是样本格式,一个是编解码器。 - 以为解码出来一定是 s16。 AAC 给的是 fltp。
- 忽略
u8的 128 偏置。 静音不是 0,按有符号读会读到噪声。
自测三问(点开对答案)
1. 样本格式名字里带 `p` 是什么意思? —— planar,按声道分平面存,对应的是 packed。 2. `s16` 与 `pcm_s16le` 有什么区别? —— 前者是样本格式名,后者是编解码器名,两套命名空间。 3. AAC 解码出来的样本格式是什么? —— fltp,32 位浮点、planar。- ☐ (本节完,下一节 10.2.2 Packed和Planar的区别)