x264.exe --crf 23 和 av_opt_set(ctx, "crf", "23") 不是一套名字。
最坑的不是名字,是单位:x264 用 kbps,FFmpeg 用 bps,差 1000 倍。
上一节讲了 preset 的效果,这一节讲「怎么写对名字」。 这一节的核心方法只有一个:改完看结果变不变,不看返回值。
一、名字对照表
| x264.exe 写法 | FFmpeg/PyAV 写法 | 作用 |
|---|---|---|
--crf 23 |
crf=23 |
质量(越小越好) |
--keyint 30 |
gop_size=30 |
关键帧间隔 |
--bframes 3 |
max_b_frames=3 |
B 帧数量 |
--preset medium |
preset="medium" |
速度与体积 |
--tune zerolatency |
tune="zerolatency" |
低延迟调优 |
--profile high |
profile="high" |
编码档位 |
--vbv-maxrate 2000 |
maxrate=2000000 |
最大码率 |
--vbv-bufsize 4000 |
bufsize=4000000 |
缓冲大小 |
最后两行要注意单位:x264.exe 用 kbps,FFmpeg 的 maxrate/bufsize 用 bps。
照着文档填 2000,实际变成 2 kbps,画面糊成马赛克——而且不报错,只是效果离谱。
这个单位差是我见过最多的错,比名字写错还常见,因为名字写错起码不生效,
单位写错是「生效了但错得离谱」。
二、怎么验证「真生效了」
这是本节最值钱的一段。FFmpeg 设了不存在的选项时,
av_opt_set 找不到就当没看见——不报错、不警告。
所以「设了没生效」和「名字写错了」在返回值上完全一样。
唯一可靠的办法:改完看结果变不变。
| 选项 | 文件大小 | I 帧 | B 帧 | 真生效吗 |
|---|---|---|---|---|
| (基线) | 10.4 KB | 1 | 21 | — |
crf=40 |
5.5 KB | 1 | 21 | 是(大小变了) |
crf=15 |
13.9 KB | 1 | 21 | 是(大小变了) |
gop_size=5 |
33.9 KB | 6 | 18 | 是(I 帧变多) |
max_b_frames=0 |
10.8 KB | 1 | 0 | 是(B 帧没了) |
preset=ultrafast |
19.2 KB | 1 | 0 | 是(大小变了) |
nonexistent=1 |
10.4 KB | 1 | 21 | 否(被忽略) |
最后一行是关键:文件大小和基线一模一样,但它不报错、不警告。 这就是 FFmpeg 的坑:你以为配置没生效,其实是名字写错了。
三、两个意外发现
意外一:preset=ultrafast 把 B 帧变成 0 了。
这就是 preset 的真实含义:它不是开关,是一整套参数预设值,
会顺带改掉你没注意的东西。所以「只调 preset」时,别以为其它参数还是原来的。
意外二:gop_size=5 同时把 B 帧从 21 降到 18。
因为 GOP 只有 5 帧,能塞的 B 帧本来就不多——这不是副作用,是参数之间本来就有耦合。
结论:报告任何一次「调参效果」时,必须把同时变化的其它指标一起写出来, 否则读者会以为是这个参数单独起作用。
四、x264-params 后门
所有 x264 私有参数都能从这里传,写法是「键=值、冒号分隔」:
ffmpeg -i in.yuv -c:v libx264 \
-x264-params "ref=3:me=umh:subme=7" out.h264
这是最后一招:某个选项在 FFmpeg 里找不到对应名字,
先去 x264 --fullhelp 里找原名,再从这儿塞进去。
代价是拼错不报错,所以更要按上面那个「改完看结果」的办法验。
五、动手 Demo:命令行选项名 vs 库选项名
Demo 先把对照表列出来,再逐个改一项,看结果变不变。
nonexistent=1 那一行是故意写的,用来证明「不报错不等于生效」。
CASES = [("基线", {}), ("crf=40", {"crf": "40"}),
("crf=15", {"crf": "15"}),
("gop_size=5", {"gop_size": 5}),
("max_b_frames=0", {"max_b_frames": 0}),
("preset=ultrafast", {"preset": "ultrafast"}),
("nonexistent=1", {"nonexistent": "1"})]
for name, opt in CASES:
c, s = open_enc("case.h264")
s.options = opt
... # 编完统计大小 / I 帧 / B 帧
实跑输出:
--------------------------------------------------------------------
命令行选项名 vs 库选项名
--------------------------------------------------------------------
**`x264.exe --crf 23` 和 `av_opt_set(ctx, "crf", "23")` 用的不是一套名字。**
**这一节把它们对齐,以后查文档不用来回猜。**
x264.exe 写法 FFmpeg/PyAV 写法 作用
----------------------------------------------------------------------
`--crf 23` `crf=23` 质量(越小越好)
`--keyint 30` `gop_size=30` 关键帧间隔
`--bframes 3` `max_b_frames=3` B 帧数量
`--preset medium` `preset="medium"` 速度与体积
`--tune zerolatency` `tune="zerolatency"` 低延迟调优
`--profile high` `profile="high"` 编码档位
`--vbv-maxrate 2000` `maxrate=2000000` 最大码率
`--vbv-bufsize 4000` `bufsize=4000000` 缓冲大小
**最后两行要注意单位**:x264.exe 用 kbps,**FFmpeg 的 `maxrate`/`bufsize` 用 bps**,
差 1000 倍。
**这个单位差是最常见的错**:照着文档填 `2000`,实际变成了 2 kbps,画面糊成马赛克。
基线:crf 默认、GOP 默认、B 帧默认 → **10.4 KB,1 个 I 帧,21 个 B 帧**
**然后逐个改一项,看「真生效了没有」**——不是看报不报错,是看结果变不变:
选项 文件大小 I 帧 B 帧 真生效吗
----------------------------------------------------------------------
crf=40 5.5 KB 1 21 是(大小变了)
crf=15 13.9 KB 1 21 是(大小变了)
gop_size=5 33.9 KB 6 18 是(I 帧变多)
max_b_frames=0 10.8 KB 1 0 是(B 帧没了)
preset=ultrafast 19.2 KB 1 0 是(大小变了)
nonexistent=1 10.4 KB 1 21 否(被忽略)
**`nonexistent=1` 那行是关键**:文件大小和基线一模一样,**但它不报错、不警告。**
**这就是 FFmpeg 的坑**:设了不存在的选项,
`av_opt_set` 找不到就当没看见——**你以为是配置没生效,其实是名字写错了。**
**验证办法只有一条:改完看结果变不变**,不看返回值。
**`preset=ultrafast` 那一行还有个意外**:B 帧变成 0 了,因为这一档默认就不开 B 帧。
**这就是 preset 的真实含义**:它不是一个开关,**是一整套参数的预设值**,
会顺带改掉你没注意的东西。
**所以「只调 preset」时,别以为其它参数还是原来的。**
**`x264-params` 是后门**:所有 x264 私有参数都能从这里传,写法是 `ref=3:me=umh` 这种「键=值、
冒号分隔」。
**查不到的选项,先去 x264 的 `--fullhelp` 里找名字,再从这儿塞。**
这个 Demo 的方法比结论重要:任何编码参数的效果验证,都该这么写——基线跑一遍,改一项跑一遍,
比的是结果,不是返回值。
nonexistent=1 那行不能省:它证明「不报错」这件事本身不可信,
这是全节最有说服力的一格数据。
30 秒实验
- 把
maxrate按 x264 的习惯填2000(不乘 1000),看码率掉到多少。 - 用
x264-params "ref=1"试一次,看文件大小变不变。 - 故意把
crf拼成crff,确认它静默失败——并记下你应该怎么发现它。
常见误区
- 以为设了选项就一定生效。 名字错、单位错都静默失败,必须改完看结果。
- 以为 preset 只改速度。 它是一整套预设,会顺带把 B 帧、参考帧一起改掉。
- 以为
maxrate 2000就是 2000 kbps。 FFmpeg 里是 bps,要写 2000000。
自测三问(点开对答案)
1. 为什么设了不存在的选项不报错? —— `av_opt_set` 在选项表里查不到名字就当没这一项,返回码不区分「设成功」和「没找到」。 **所以只能靠结果验证。** 2. `preset=ultrafast` 为什么 B 帧变 0? —— 这一档的预设值里 B 帧数就是 0。**preset 是一整套参数,不是单一开关。** 3. `--vbv-maxrate 2000` 对应 FFmpeg 里怎么写? —— `maxrate=2000000`。**x264 用 kbps,FFmpeg 用 bps,差 1000 倍。**下一节换 H.265:多花几倍时间去换体积,这笔账在监控里划不划算。
- ☐ (本节完,下一节 04.03 libx265的常用编码选项简介)