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

五个数决定一路音频:编码器、码率、采样率、声道、采样格式。 只有码率直接决定大小,其余四个是「精度」不是「体积」。

📝 本节和前面几节的关系

3.5 讲了视频那一组,这一节讲音频的对应一组。 3.7 讲字幕,那一组和前两组差别很大。

一、五个数各管什么

音频参数看起来多,其实只有五个维度:

选项 管什么 典型值


-c:a 用哪个编码器 aac/libmp3lame/pcm_s16le -b:a 码率 128k/192k -ar 采样率 44100/48000/8000 -ac 声道数 1/2/6 -sample_fmt 内部采样格式 s16/fltp

第二行是唯一直接决定文件大小的。 其余四个改的是「精度」,压缩后的大小由编码器根据码率决定。

所以「想把音频压小」的正确做法是调 -b:a,不是降采样率。 降采样率确实也变小,但它同时丢了高频——用画质换体积,不划算。

二、采样率降了,字节数不一定跟着降

这是本节最反直觉的一处:

降采样率 字节数会怎样 为什么


44100 → 22050 小幅下降 码率下限撑住了 44100 → 8000 明显下降但不线性 编码器开销成了主导

原因是 AAC 这类编码器有个「最低码率」的概念。 当目标码率低于它能有效工作的下限时,省下来的空间被编码器自身的开销吃掉了。

所以「采样率减半 = 文件减半」这个直觉是错的。 真想让文件减半,得同时调 -b:a——那才是直接控制大小的旋钮。

三、哪些操作是不可逆的

这一条比参数表重要得多:

操作 可逆吗 为什么


降到 pcm_s16le 基本可逆 无损编码,只占空间 转 aac 再转回 不可逆 有损,转一次掉一次 降采样率再升回 不可逆 高频已经丢了 混成单声道再拆开 不可逆 两个声道的信息已经混在一起

第二行是最常见的错误:为了「统一格式」把无损转成有损,之后又转回来。 两次损失叠加,而原始数据已经找不回来了。

正确做法是「一次有损,永久保留有损那一份」,或者「一直留一份无损母版」。 后者占空间但安全,前者省空间但要接受「以后只能从这一份转」。

四、动手 Demo:五个数决定一路音频

造一段 1 秒 440 Hz 立体声,分别改采样率和声道数,量输出字节。 重点是验证:字节数不会跟着参数成比例变化。

import os
import av
import numpy as np

SR = 44100
t = np.arange(SR) / float(SR)
sig = (0.4 * np.sin(2 * np.pi * 440 * t) * 32767).astype(np.int16)
st = np.stack([sig, sig], axis=1).reshape(-1)

c = av.open(u"a.m4a", u"w")
s = c.add_stream(u"aac", rate=SR)
s.layout = u"stereo"
fr = av.AudioFrame(format=u"s16", layout=u"stereo", samples=SR)
fr.sample_rate = SR
fr.planes[0].update(st.tobytes())
for pk in s.encode(fr):
    c.mux(pk)
for pk in s.encode(None):
    c.mux(pk)
c.close()
base = os.path.getsize(u"a.m4a")
print(u"44100 立体声 %d 字节" % base)

# 降到 8000
c0 = av.open(u"a.m4a")
cc = c0.streams.audio[0].codec_context
c1 = av.open(u"a8k.m4a", u"w")
s1 = c1.add_stream(u"aac", rate=8000)
s1.layout = u"stereo"
rs = av.AudioResampler(format=u"s16", layout=u"stereo", rate=8000)
for pk in c0.demux(audio=0):
    if not pk.size:
        continue
    for f in cc.decode(pk):
        for rf in rs.resample(f):
            for o in s1.encode(rf):
                c1.mux(o)
for o in s1.encode(None):
    c1.mux(o)
c1.close()
c0.close()
n = os.path.getsize(u"a8k.m4a")
print(u"8000 立体声 %d 字节,是原来的 %.2f 倍" % (n, n / float(base)))

实跑输出:

--------------------------------------------------------------------
3.6 音频选项——五个数决定一路音频
--------------------------------------------------------------------

**① 编码、码率、采样率、声道、采样格式**

  选项            管什么           典型值
  -----------------------------------------------------------------
  -c:a            用哪个编码器     aac / libmp3lame / pcm_s16le
  -b:a            码率             128k / 192k
  -ar             采样率           44100 / 48000 / 8000
  -ac             声道数           1 / 2 / 6
  -sample_fmt     内部采样格式     s16 / fltp

**最后一行又是「中间精度」不是「文件大小」——和 3.5 里 -pix_fmt 的位置一样。**
**真正决定大小的是第二行 `-b:a`,前四个里面只有它直接见效。**

**② 采样率降一半,字节数会不会降一半**

  采样率     输出字节     路线
  ----------------------------------
  44100      17345        原样
  22050      15372        重采样
  8000       6815         重采样

**编码器是 aac、时长 1.0 秒、原文件 17345 字节。**
**注意采样率降到 1/5 时字节数没降到 1/5——因为 AAC 的码率下限撑住了。**
**这一点很反直觉:码率低到一定程度,编码器的开销成了主导,再降采样率也省不下来。**

**③ 声道数的影响更直接**

  声道            输出字节     相对立体声
  -------------------------------------------
  2(立体声)     17345        1.00×
  1(单声道)     10312        0.59×

**没有降到一半,原因同上:码率有下限,容器的头也不随声道数变。**
**但听感上单声道是实打实少了一个维度——两个声道的信息被混成一个,回不去了。**

**④ 音频选项里唯一「无损可逆」的一类**

  情况               可逆吗       为什么
  ------------------------------------------------------
  降到 pcm_s16le     基本可逆     无损编码,只占空间
  转 aac 再转回      不可逆       有损,转一次掉一次
  降采样率再升回     不可逆       高频已经丢了

**第二行是转码最常见的错误:为了「统一格式」把无损转成有损,之后再转回来,**
**两次损失叠加,而原始数据已经找不回来了。素材一旦有损,就该留一份无损母版。**

重采样要用 AudioResampler,不能直接改 frame.sample_rate。 改属性只是改了个标签,样本数没变,编码器拿到的是错的数据。

采样率从 44100 降到 8000(约 1/5),字节数并没有降到 1/5。 AAC 有最低码率的概念,省下来的空间被编码器开销吃掉了。

同样的道理,声道从 2 降到 1 也不会降到一半。 所以「降参数来省空间」这件事,对音频来说收益比想象中小。

真正想省空间就调 -b:a(比如从 128k 降到 64k),那是直接旋钮。 代价是音质下降,而且这一降是不可逆的。

30 秒实验

  1. 降码率:ffmpeg -i 音频.wav -c:a aac -b:a 64k 出.m4a,和 128k 的对比大小与听感。
  2. 降采样率:ffmpeg -i 音频.wav -ar 22050 出.wav,看大小变化,注意高频损失。
  3. 转单声道:ffmpeg -i 音频.wav -ac 1 出.wav,听立体声信息是不是彻底没了。

常见误区

  1. 以为采样率减半文件就减半。编码器有最低码率,字节数不会线性降。
  2. 为了统一格式把无损转有损。转一次掉一次,原始数据再也拿不回来。留一份无损母版。
  3. 直接改 frame.sample_rate 做重采样。那只是改标签,样本数没变,编码器拿到错数据。
自测三问(点开对答案) 1. 五个音频参数里,哪个直接决定文件大小? —— `-b:a` 码率。其余四个改的是精度(采样率、声道、采样格式、编码器),不直接控制大小。 2. 采样率从 44100 降到 8000,文件大小为什么没降到 1/5? —— 因为编码器有最低码率的概念,省下的空间被它自身的开销吃掉了。想省空间要调 `-b:a`。 3. 哪些音频操作是不可逆的? —— 转有损、降采样率、混单声道。无损转 pcm 是基本可逆的,只是占空间。

相关笔记