🎬 第三章 FFmpeg三大常用工具及应用选项详解
📄 一句话概括

采样格式的命名只有两个变量:位深加是否平面。 末尾那个 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 秒实验

  1. 列格式:ffmpeg -sample_fmts,注意 u8/s16 这类前缀的含义。
  2. 查编码器要什么:ffmpeg -h encoder=aac,找 Supported sample formats。
  3. 显式指定:ffmpeg -i 片子.mp4 -c:a aac -sample_fmt fltp 出.mp4,省掉内部转换。

常见误区

  1. 以为 s16 和 s16p 是两种精度。位深一样,区别只在按声道分开存还是一路交错。
  2. 以为自动转换是免费的。它是一次全量重排,批量处理时开销很可观。
  3. 拿 av.AudioFormat 找声道数。它没有这个属性,声道数要问 AudioLayout。
自测三问(点开对答案) 1. `fltp` 这个名字能读出哪两个信息? —— 32 位浮点(flt),并且是平面存放(末尾的 p),也就是按声道分开存。 2. 打包转平面会改变样本的值吗? —— 不会。它只是把交错存放重排成按声道分块,样本值一个都不变。 3. 为什么给 AAC 编码器 s16 数据时中间会有一次转换? —— 因为 AAC 只接受 fltp 一种采样格式,s16 必须先重采样成 fltp 才能编。

相关笔记