5.0 上这条转码命令跟第五章一个字都不差,唯一会变的是「你没写出来的那些默认值」。 -b:a 是目标不是保证:要 128k 实测出 122.1k,验收只能要求接近。
第五章把转码参数讲透了,这一节只补一件事:换版本之后哪些东西会悄悄变。 结论会有点反直觉——最不稳的地方,恰好是你最没注意的地方。
一、命令一个字没变
第五章那条:
ffmpeg -i in.mp4 -c:v libx264 -crf 23 -preset medium \
-c:a aac -b:a 128k out.mp4
在 5.0 上还是这条,在 7.x 上还是这条。
-c:v、-crf、-preset、-c:a 这些选项名,从 4.x 到 7.x 一路没动过。
所以「升级 FFmpeg」这件事,对手上这套命令几乎没有影响。
真正会变的,是那些你没写出来的东西。
二、唯一会变的:AAC 的默认值
5.0 的公告里有一句很容易被忽略:「AAC 编码器的默认设置也做了调整,以提升质量」。
这句话的含义是:
同一条不写 -b:a 的命令,4.x 和 5.0 出来的音频码率可能不一样。
实测同一段 2 秒 440 Hz 单声道音频,三种设法的结果:
音频怎么设 成片字节 折算码率 说明
不写码率 18857 75.4 kbps 默认值也是版本的一部分 -b:a 128k 30516 122.1 kbps 写死就不随版本变 -b:a 64k 17705 70.8 kbps 写死就不随版本变
不写码率那一行出来的 75.4 kbps,就是这套版本「自认为合适」的值。 换一个大版本,它可能就变成别的数——而你的命令一个字都没改。
这就是「依赖默认值等于依赖版本」的具体样子:
升级之后觉得音质「好像变了」、文件「好像大了」,原因往往不在命令里,在默认值里。
要对结果有把握,就把 -b:a、-crf、-preset 显式写出来。
顺带一个反直觉的小坑:写 -b:a 128k,实测出来是 122.1 kbps,不是 128。
-b:a 是给编码器的目标,不是保证:
AAC 的帧大小固定,只能取整到最近的那一档;再加上容器的头开销,
最后的总码率跟你要的数总会差一点。
所以对码率做验收,只能要求「接近」,不能要求「等于」。
三、哪些参数跨版本稳
判断标准只有一条:这个选项名归谁定。
选项 名字归谁 跨版本稳不稳
-crf / -preset libx264 自己定 稳,库不换语义就不换 -x264-params libx264 自己定 稳 -b:a / -ar / -ac FFmpeg 通用层 比较稳,语义简单 aac 的默认参数 FFmpeg 自己定 会变,所以别依赖
一句话记法:名字属于外部库的(x264、x265、fdk-aac)就稳, 属于 FFmpeg 自己实现的编码器(aac、mp3)就可能跟着版本变。 因为外部库的选项名是那个库的公开接口,动它要背兼容性包袱; 而「默认开多大码率」是 FFmpeg 自己的内部决策,改起来没负担。
四、跨版本验收该怎么做
如果手上有一个跑了几年的转码脚本,升级 FFmpeg 之后要验三件事:
验什么 怎么验 为什么
命令还认不认 先空跑一遍看有没有报错 选项被删是最早暴露的 体积变没变 同一个源跑两次比字节 默认值变了体积就变 画质变没变 比 SSIM 或直接看片 编码器实现可能微调
这三步里最容易漏第二步。 「能跑」不等于「结果一样」—— 码率、体积这类东西变了不会报错,只会让你月底发现存储涨了。
最省事的办法其实是:升级前先把所有默认值都写死。
把「不写 -b:a」全部改成「写死 -b:a 128k」,
把「不写 -preset」全部改成「写死 -preset medium」,
这样升级之后唯一还可能变的就只剩编码器实现本身了。
五、转码这一节真正要带走的东西
不是新参数,是一条判断习惯: 遇到「升级后行为变了」,先问「我有没有依赖某个默认值」。
这条习惯不止对 FFmpeg 有用。 任何工具,凡是你没显式写的部分,都是那个版本替你做的决定; 版本一换,决定就换。 所以「把关键参数写死」看起来啰唆,实际上是把不确定性关在门外。
六、动手 Demo:AAC 三档码率,看默认值到底是多少
下面这段现造一段 2 秒 440 Hz 单声道音频,编三次:不写码率、128k、64k。 然后按文件大小折算成实际码率——那个「不写」的数,就是默认值。
import os
import av
import numpy as np
SR, NS = 44100, 88200
tt = np.arange(NS) / float(SR)
sig = (0.25 * np.sin(2 * np.pi * 440 * tt)).astype(np.float32)
for label, br in (("不写码率", None), ("128k", 128000), ("64k", 64000)):
path = "a_%s.m4a" % (br or "def")
o = av.open(path, "w")
a = o.add_stream("aac", rate=SR)
a.layout, a.format = "mono", "fltp"
if br:
a.bit_rate = br
fr = av.AudioFrame.from_ndarray(
np.ascontiguousarray(sig.reshape(1, -1)), format="fltp", layout="mono")
fr.sample_rate = SR
fr.pts = 0
for pk in a.encode(fr):
o.mux(pk)
for pk in a.encode(None):
o.mux(pk)
o.close()
sz = os.path.getsize(path)
print("%-8s %6d 字节 %.1f kbps" %
(label, sz, sz * 8.0 / 1000.0 / (NS / float(SR))))
实跑输出:
--------------------------------------------------------------------
11.2 使用 FFmpeg 5.0 实现音视频转码
--------------------------------------------------------------------
**① 命令没变,唯一会变的是「默认值」**
**5.0 公告里那句「AAC 编码器默认参数调整」,是命令行用户唯一会碰到的一处。**
**它意味着:同样一条不写 `-b:a` 的命令,4.x 和 5.0 出来的音频码率可能不一样。**
音频怎么设 成片字节 折算码率 说明
----------------------------------------------------------------------
不写码率 18857 75.4 kbps 默认值也是版本的一部分
-b:a 128k 30516 122.1 kbps 写死就不随版本变
-b:a 64k 17705 70.8 kbps 写死就不随版本变
**同样的 2 秒单声道音频,不写码率出来的那个数,就是这个版本「自认为合适」的值。**
**换一个大版本,它可能就变了——而你的命令一个字都没改。**
**这就是「依赖默认值等于依赖版本」的具体样子:**
**升级之后音质「好像变了」、文件「好像大了」,原因往往不在命令里。**
**要对结果有把握,就把 `-b:a`、`-crf`、`-preset` 显式写出来——**
**工程上这叫「别让默认值替你做决定」。**
**还有个小坑值得顺手记下:写 `-b:a 128k`,实测出来的是 122.1 kbps,不是 128。**
**`-b:a` 是给编码器的目标,不是保证——**
**AAC 的帧大小是固定的,只能取整到最近的那一档;再加上容器的头开销,**
**最后的总码率跟你要的那个数总会差一点。**
**所以对码率做验收时,只能要求「接近」,不能要求「等于」。**
**② 视频那边为什么不受影响**
**因为 `-crf`、`-preset` 是编码器私有选项,名字由 libx264 自己定,**
**FFmpeg 只是原样转发。libx264 没换,参数语义就不会变。**
**而 aac 是 FFmpeg 自带的编码器,默认值写在 FFmpeg 源码里,跟着版本走。**
**所以「哪些参数跨版本稳」有一条简单的判断:**
**选项名属于外部库的(x264、x265、fdk-aac)就稳,属于 FFmpeg 自己的(aac、mp3)就可能变。**
三行数字里,第一行才是「这一版自己觉得合适」的值。 后两行是你说了算——只要写死,换版本也不会变。
注意 128k 那行折算出来是 122.1,不是 128。 这不是算错,是编码器的目标码率跟最终文件大小之间本来就有差: AAC 帧大小固定、容器还有头,差个几个百分点是常态。
30 秒实验
- 把采样率从 44100 改成 8000 再跑:看默认码率跟着降到多少。
- 把时长从 2 秒拉到 20 秒:看折算码率是否稳定,理解「头开销占比」。
- 拿一段真实音乐跑一遍:跟 440 Hz 正弦比,默认码率往往不一样。
常见误区
- 以为不写码率就等于「无损」。它等于「让这个版本替你选」。
- 要求
-b:a 128k出来的文件精确等于 128 kbps。它是目标,不是保证。 - 升级后只验「能跑」。体积和质量变了不会报错。
自测三问(点开对答案)
1. 换大版本之后,哪一类参数最容易变? —— FFmpeg 自己实现的编码器的默认参数,比如 aac 的默认码率。 2. `-b:a 128k` 为什么出不来 128 kbps? —— AAC 帧大小固定只能取整,加上容器头开销,总会差一点。 3. 升级前最省事的自保办法是什么? —— 把所有默认值显式写死,让结果不再依赖版本。- ☐ 11.2 完——下一节 11.3 使用 FFmpeg 5.0 实现文字跑马灯