🎬 第五章 FFmpeg命令行实现音视频转码
📄 一句话概括

AC-3 是恒定码率:每帧 1536 点、字节固定,不够补、多了砍。 它只认 32、44.1、48 kHz 三档采样率,别的不给商量。

📝 本节和前面几节的关系

上一节转 MP3 还算自由,这一节会撞上硬约束。 目标格式的「死规矩」比参数调优重要得多,这是本节的重点。

一、三档码率,三行都贴着目标走

同一段声音,192/384/448 kbps 各转一遍:

目标码率 字节 实测码率


192 kbps 48482 193 kbps 384 kbps 96966 387 kbps 448 kbps 113128 452 kbps

三行都几乎贴着目标,和 5.1.2 里 AAC「用不完」那次完全不同。 因为 AC-3 是恒定码率:每帧字节固定,简单内容也要填满。

恒定码率的好处是播放器不用缓冲、码流长度可预测;坏处是该省的省不下来。 所以它在网络播放上很浪费,同样的听感 AAC 能省一半。

二、采样率不是想给就能给

AC-3 只认三档采样率:

采样率 结果


48000 成功 44100 成功 32000 成功 24000 被拒(returned 22) 22050 被拒(returned 22)

因为码流里采样率的编码位太少,当年只留了三档。这是标准定死的,不是实现偷懒。 连 24000 这种整数都不行,别指望「取个整就过了」。

遇到这种限制,唯一的路是「先用重采样器改到 48 kHz,再交给 AC-3」。 命令行里 ffmpeg 会帮你补这一步:-ar 48000 加上去就行。

三、什么场合才轮到 AC-3

它是为影院与电视定的,不是为网络定的:

场合 为什么用它


蓝光、家庭影院 老功放都认,兼容性最好 数字电视广播 ATSC 标准音轨就是它 网络播放 不该用,AAC 更省

第三行是结论:选音频格式的顺序永远是「先看播放端认什么,再在里面挑最省的」。 不认 AC-3 的播放器一大把,主动选它只会给自己找麻烦。

顺带一提:真正需要 5.1 声道时,AC-3 的优势才明显—— 它本来就是为多声道设计的,立体声只是它的一种简单情形。

四、动手 Demo:三档码率各转一遍,再试一个不给的采样率

输入是 128 kbps 的 AAC,输出 AC-3,试 192、384、448 三档。 最后故意用 22050 Hz 再转一次,看它怎么拒绝。

import av, os

c0 = av.open(u"aac128.m4a")
fr0 = next(c0.decode(audio=0))
c0.close()

for br in (192000, 384000, 448000):
    c0 = av.open(u"aac128.m4a")
    c1 = av.open(u"ac3_%d.ac3" % (br // 1000), u"w")
    s1 = c1.add_stream(u"ac3", rate=44100)
    s1.layout, s1.bit_rate = u"stereo", br
    for f in c0.decode(audio=0):
        for o in s1.encode(f):
            c1.mux(o)
    for o in s1.encode(None):
        c1.mux(o)
    c1.close()
    c0.close()
    n = os.path.getsize(u"ac3_%d.ac3" % (br // 1000))
    print(u"%d kbps -> %d 字节  实测 %d kbps"
          % (br // 1000, n, n * 8 / 2.0 / 1000))

c1 = av.open(u"ac3_22.ac3", u"w")
s1 = c1.add_stream(u"ac3", rate=22050)
print(u"建流这一步没报错")
try:
    for o in s1.encode(fr0):
        c1.mux(o)
    print(u"22050 编成功了")
except Exception as e:
    print(u"22050 被拒:%s" % str(e)[:60])
c1.close()

实跑输出:

--------------------------------------------------------------------
5.3.3 AAC 转码为 AC-3——影院规格的固定码率
--------------------------------------------------------------------

**① 三档码率各转一遍**

  目标码率     字节       实测码率
  ---------------------------------
  192 kbps     48482      193 kbps
  384 kbps     96966      387 kbps
  448 kbps     113128     452 kbps

**三行几乎都贴着目标走,和 AAC 那次的「用不完」完全不同。**
**因为 AC-3 是恒定码率:每帧 1536 点、固定字节数,不够就补、多了就砍。**
**恒定码率的好处是播放器不用缓冲,坏处是该省的省不下来。**

**② 采样率不是想给就能给**

  采样率     结果
  -------------------------------
  44100      成功
  22050      被拒(returned 22)

**22050 直接被拒。AC-3 只认 32 kHz、44.1 kHz、48 kHz 这三档,**
**因为它当初是为影院与电视定的,码流里的采样率只有两位编码,放不下别的值。**
**这就引出转码的第一条常识:目标格式的硬约束要先查,不是所有参数都能商量。**

**③ 什么场合才轮到 AC-3**

  场合               为什么用它
  ------------------------------------------
  蓝光、家庭影院     老功放都认,兼容性最好
  数字电视广播       ATSC 标准音轨就是它
  网络播放           不该用,AAC 更省

**第三行是重点:AC-3 的恒定码率在网络上很浪费,同样的听感 AAC 能省一半。**
**所以选音频格式的顺序应该是:先看播放端认什么,再在里面挑最省的那个。**

注意那个「建流这一步没报错」:add_stream 只是登记参数, 真正的 avcodec_open2 发生在第一次 encode 时。

所以只建流、不喂帧的试探是假成功——必须真编一帧才知道行不行。 这条规律对所有编码器都成立,调试「参数不合法」时尤其有用。

恒定码率下字节可以预估:码率乘时长除 8。 192 kbps 两秒约 48000 字节,实测 48482,多出来的是帧对齐的余量。

写文件用 .ac3 后缀,ffmpeg 能自己认出封装。 如果没有后缀(比如推流),得显式指定格式名。

30 秒实验

  1. 命令行对照:ffmpeg -i 出.m4a -c:a ac3 -b:a 384k -ar 48000 出.ac3。
  2. 故意不给采样率:源是 44100 时也能过,但源是 22050 就必须加 -ar 48000。
  3. 换成 5.1:-ac 6 试试,字节会明显变大,这就是多声道的代价。

常见误区

  1. 以为采样率随便填。AC-3 只有三档,别的一律被拒。
  2. 以为恒定码率更「专业」。网络场景它只会浪费带宽。
  3. 拿 AC-3 给网页用。浏览器普遍不认,得用 AAC。
自测三问(点开对答案) 1. AC-3 每帧多少采样点?为什么字节这么稳定? —— 1536 点,而且是恒定码率:每帧字节固定,简单内容也要填满。 2. 为什么「只建流、不喂帧」试不出 22050 能不能用? —— add_stream 只登记参数,真正的 avcodec_open2 在第一次 encode 时才跑。 3. 为什么 22050 Hz 会被拒? —— AC-3 规范只允许 32、44.1、48 kHz 三档,码流里没有别的编码位。 4. 要给网页准备音轨,该选什么? —— AAC。AC-3 浏览器普遍不支持,而且恒定码率更费带宽。

相关笔记