FFmpeg 与 x264.exe 是两套名字,落到同一组参数上。 最容易踩的坑是码率单位:FFmpeg 用 bit,x264 用 kbit,差一千倍。
上一节用的 preset 这些名字其实是 x264 的,FFmpeg 只是转发。 这一节把两套名字对上,并验证它们确实等价。
一、两套名字对照
同一组参数,两种写法:
FFmpeg 里写 x264 里写 含义
crf 23 crf 23 质量档位 preset slow preset slow 速度与效率 g 10 keyint 10 关键帧间隔 b:v 2M bitrate 2000 目标码率 profile:v high profile high 档次 bf 3 bframes 3 连续 B 帧上限
前两行的名字一模一样,因为 FFmpeg 就是原样转发。
后四行才有改名:g 变 keyint、bf 变 bframes。
第四行是唯一真正容易踩的坑:FFmpeg 的码率单位是 bit,x264 的是 kbit。
写成 2M 是 2000000 bit,写成 2000 在 x264 里才是 2000 kbit——同一个意思。
二、透传:两套名字确实等价
用 x264-params 透传,与分开写对比:
写法 字节 关键帧数
crf=23 加 g=10 6245 5 x264-params crf=23:keyint=10 6245 5
两行字节完全一样、关键帧数也一样——说明这两条路最终都落到同一组 x264 参数上。 差 0 字节这件事比「差不多」有说服力得多。
x264-params 的意义就在这儿:FFmpeg 没给命令行开关的 x264 选项,
都能通过它透传进去,用冒号分隔、不用加两个减号。
三、什么时候必须用透传
三类需求只能走透传:
需求 为什么得透传
调 psy 相关的几个参数 FFmpeg 没有对应开关 指定 slice 数量 同上 关掉某个具体工具 如 no-scenecut
代价是可读性变差、也没人帮你检查拼写。 写错一个参数名,x264 只会在日志里抱怨一句,编码照样跑完——结果和你以为的完全不同。
所以透传的正确用法是:先用分开的写法验证参数有效果,再合并成透传字符串。 直接一上手就写一长串,出错时几乎没法定位是哪个名字写错了。
四、动手 Demo:分开写与透传写,比字节与关键帧数
同一组参数(CRF 23、关键帧间隔 10)用两种写法各编一遍。 然后比字节与关键帧数——如果两套名字真的等价,两行就该一模一样。
import av, os
def enc(dst, opts):
c1 = av.open(dst, u"w")
s1 = c1.add_stream(u"libx264", rate=25)
s1.width, s1.height, s1.pix_fmt = 160, 90, u"yuv420p"
s1.options = opts
c0 = av.open(u"s5.mp4")
for f in c0.decode(video=0):
for o in s1.encode(f.reformat(width=160, height=90, format=u"yuv420p")):
c1.mux(o)
for o in s1.encode(None):
c1.mux(o)
c1.close()
c0.close()
c = av.open(dst)
k = sum(1 for pk in c.demux(video=0) if pk.size and pk.is_keyframe)
c.close()
return os.path.getsize(dst), k
a = enc(u"x_sep.mp4", {u"crf": u"23", u"g": u"10"})
b = enc(u"x_params.mp4", {u"x264-params": u"crf=23:keyint=10"})
print(u"分开写 %d 字节 关键帧 %d" % a)
print(u"x264-params %d 字节 关键帧 %d" % b)
print(u"两行字节差 %d" % (a[0] - b[0]))
实跑输出:
--------------------------------------------------------------------
5.7.2 x264.exe 中的选项名与选项值——两套名字一件事
--------------------------------------------------------------------
**① 同一组参数,两种写法对照**
FFmpeg 里写 x264 里写 含义
----------------------------------------------------------------
-crf 23 --crf 23 质量档位
-preset slow --preset slow 速度与效率的取舍
-g 10 --keyint 10 关键帧最大间隔
-b:v 2M --bitrate 2000 目标码率(x264 用 kbps)
-profile:v high --profile high 档次
-bf 3 --bframes 3 连续 B 帧上限
**第四行是唯一真正容易踩的坑:FFmpeg 的码率单位是 bit,x264 的是 kbit。**
**写成 `-b:v 2000` 会被当成 2 kbps,编码器直接给你一片马赛克。**
**② 用 x264-params 透传,验证两套名字等价**
写法 字节 关键帧数
---------------------------------------------------
crf=23 加 g=10 6245 5
x264-params crf=23:keyint=10 6245 5
**两行字节完全一样、关键帧数也一样——说明这两条路最终都落到同一组 x264 参数上。**
**`x264-params` 的意义就在这儿:FFmpeg 没给命令行开关的 x264 选项,**
**都能通过它透传进去,用冒号分隔、不用加两个减号。**
**③ 什么时候必须用透传**
需求 为什么得透传
----------------------------------------------
调 psy 相关的几个参数 FFmpeg 没有对应开关
指定 slice 数量 同上
关掉某个具体工具 如 no-scenecut
**代价是可读性变差、也没人帮你检查拼写。写错一个参数名,x264 只会在日志里抱怨一句,**
**编码照样跑完——结果和你以为的完全不同。**
x264-params 的值是一个冒号分隔的字符串,参数名用 x264 那一套。
所以里面写 keyint 而不是 g,写成 g 反而不行。
两行字节完全相同,是因为它们最终生成的是同一条 x264 命令行。
FFmpeg 的 g 只是被翻译成 keyint 而已。
想验证透传有没有生效,最直接的办法就是比字节与关键帧数。 参数写错时这两列都不会变,正好当探测器用。
30 秒实验
- 命令行对照:
ffmpeg -i 源.mp4 -c:v libx264 -x264-params crf=23:keyint=10 出.mp4。 - 关掉场景切换补帧:透传
scenecut=0,再数关键帧,应当正好等于设置值。 - 故意写错名字:透传
keyintt=10,看日志怎么抱怨、编码是否照跑。
常见误区
- 把 x264 的
bitrate当 bit 用。它是 kbit,差一千倍,画面会糊一片。 - 在
x264-params里写 FFmpeg 的短名。里面得用 x264 的参数名。 - 以为透传的参数写错会报错。多数情况只写一行日志,编码照跑。
自测三问(点开对答案)
1. FFmpeg 的 `-g` 对应 x264 的哪个参数? —— `keyint`。FFmpeg 只是把它翻译过去,落到的还是同一个 x264 参数。 2. 为什么 `-b:v 2M` 与 `--bitrate 2000` 是一个意思? —— FFmpeg 用 bit 为单位,x264 用 kbit,2000000 bit 就是 2000 kbit。 3. 怎么验证透传参数生效了? —— 比字节与关键帧数。参数没生效时这两列不会有变化。- ☐ (本节完,下一节 5.8 libx265 的常用编码选项及应用案例)