采样格式的命名只有两个变量:位深加是否平面。 末尾那个 p 就是平面,fltp 就是 32 位浮点、按声道分开存。
3.11.9 讲声道怎么摆,这一节讲每个采样点怎么存。 两件事合起来,才是一路音频的完整描述。
一、命名规则:位深加一个 p
看名字就能读出两个信息:
格式 位深 打包还是平面 每样本字节
u8 8 打包 1 s16 16 打包 2 s32 32 打包 4 flt 32 浮点 打包 4 dbl 64 浮点 打包 8 s16p 16 平面 2 fltp 32 浮点 平面 4
末尾那个 p 就是平面(planar):s16p 是 16 位平面,s16 是 16 位打包。
这一条记熟了,看到 fltp 就知道是「32 位浮点、按声道分开存」。
前缀也有一套简写:u 无符号、s 有符号、flt 浮点、dbl 双精度。
这几个前缀与 C 语言里的类型名基本对应,不是 FFmpeg 自己造的。
二、打包与平面的区别
本质是「左右声道怎么摆」,不是「精度高低」:
存法 立体声的内存排列 谁喜欢
打包 左1 右1 左2 右2 … 声卡、老接口 平面 左1 左2 … 右1 右2 … 编码器、滤镜
所以「打包转平面」本质上是一次重排,不改变任何一个样本的值。 它便宜(只是内存搬动),但搬错了就是左右声道互换或者杂音。
编码器和滤镜偏爱平面,是因为按声道整块处理时内存访问更连续。 声卡偏爱打包,是因为它按「一帧一帧」往外送,交错存放正好对得上。
三、编码器往往只吃一种
这就是「为什么非要转格式」的直接原因:
项 值
编码器 aac 接受的采样格式 只有 fltp 接受的采样率个数 13 种
AAC 只吃 fltp 一种格式,所以给它 s16,ffmpeg 要先转一遍。
这个转换是自动的,但它是「有代价的自动」——多了一次全量重排。
批量处理时把解码输出直接设成目标格式,能省掉这一趟。 这类优化单次看不出来,几万个文件跑下来差别就明显了。
四、动手 Demo:七种采样格式的位深与字节数
把常见采样格式逐个构造,读它自己报的位深、是否平面、每样本字节。 最后再看 AAC 编码器接受哪些格式,验证「只吃一种」这句话。
import av
FMT = [u"u8", u"s16", u"s32", u"flt", u"dbl", u"s16p", u"fltp"]
for name in FMT:
try:
f = av.AudioFormat(name)
print(u"%-6s %2d 位 %s %d 字节/样本"
% (name, f.bits, u"平面" if f.is_planar else u"打包", f.bytes))
except Exception as e:
print(u"%-6s 不支持" % name)
c = av.codec.Codec(u"aac", u"w")
fm = c.audio_formats or []
print(u"AAC 接受 %d 种:%s" % (len(fm), u", ".join(x.name for x in fm)))
实跑输出:
--------------------------------------------------------------------
3.11.10 -sample_fmts——采样格式的打包与平面
--------------------------------------------------------------------
**① 采样格式的命名只有两个变量:位深 + 是否平面**
格式 位深 打包还是平面 每样本字节
-------------------------------------------------
u8 8 打包 1
s16 16 打包 2
s32 32 打包 4
flt 32 打包 4
dbl 64 打包 8
s16p 16 平面 2
fltp 32 平面 4
**末尾那个 p 就是平面(planar):`s16p` = 16 位平面,`s16` = 16 位打包。**
**这一条记熟了,看到 `fltp` 就知道是「32 位浮点、按声道分开存」。**
**② 打包与平面的区别,是「左右声道怎么摆」**
存法 立体声的内存排列 谁喜欢
-------------------------------------------------
打包 左1 右1 左2 右2 … 声卡、老接口
平面 左1 左2 … 右1 右2 … 编码器、滤镜
**所以「打包转平面」本质上是一次重排,不改变任何一个样本的值。**
**它便宜(只是内存搬动),但搬错了就是左右声道互换或者杂音。**
**③ 编码器往往只吃一种,这就是要转的原因**
项 值
-----------------------------
编码器 aac
接受的采样格式 fltp
接受的采样率个数 13
**AAC 编码器只吃一种格式(`fltp`),所以你给它 s16,ffmpeg 要先转一遍。**
**这个转换是自动的,但它是「有代价的自动」——多了一次全量重排。**
**批量处理时把输入直接解成目标格式,能省掉这一趟。**
**④ 这一节的十个查询命令,其实是一套固定的排查动作**
想知道 查哪个
----------------------------------------------
这个文件能不能读 -formats / -demuxers
我要的编码在不在 -encoders / -decoders
这个滤镜在不在 -filters
网络推流行不行 -protocols
硬件加速行不行 -hwaccels
声道模式怎么填 -layouts
采样格式怎么填 -sample_fmts
**这七行就是这一章存在的意义:把「猜」换成「查」。**
**遇到任何「参数不认」「找不到编码器」「滤镜不存在」,先回到这张表查一遍,**
**多数情况下答案是「你这份构建里就没有」,而不是「你写错了」。**
第一列到第三列的对应关系,用眼睛过一遍就记住命名规则了。
s16 与 s16p 的位深完全一样,差别只在「按声道分开存还是一路交错」。
最后一行会打出「AAC 接受 1 种:fltp」——这就是那个「只吃一种」的实证。 所以给它 s16 时中间必然有一次重采样,不是可选的。
注意 av.AudioFormat 没有 channels 属性,声道数要问 AudioLayout。
这两件事(采样格式、声道布局)是两个独立属性,本节与 3.11.9 正好各讲一个。
30 秒实验
- 列格式:
ffmpeg -sample_fmts,注意u8/s16这类前缀的含义。 - 查编码器要什么:
ffmpeg -h encoder=aac,找Supported sample formats。 - 显式指定:
ffmpeg -i 片子.mp4 -c:a aac -sample_fmt fltp 出.mp4,省掉内部转换。
常见误区
- 以为
s16和s16p是两种精度。位深一样,区别只在按声道分开存还是一路交错。 - 以为自动转换是免费的。它是一次全量重排,批量处理时开销很可观。
- 拿
av.AudioFormat找声道数。它没有这个属性,声道数要问AudioLayout。
自测三问(点开对答案)
1. `fltp` 这个名字能读出哪两个信息? —— 32 位浮点(flt),并且是平面存放(末尾的 p),也就是按声道分开存。 2. 打包转平面会改变样本的值吗? —— 不会。它只是把交错存放重排成按声道分块,样本值一个都不变。 3. 为什么给 AAC 编码器 s16 数据时中间会有一次转换? —— 因为 AAC 只接受 fltp 一种采样格式,s16 必须先重采样成 fltp 才能编。- ☐ (本节完,下一节 3.11.11 -colors)