一条 ffmpeg 命令的难点不在参数多,而在参数放的位置。 同一个 -ss,放在 -i 前后是两种行为。
7.1.1 认了库,这一节看库的壳怎么用。 7.1.3 讲怎么绕开命令行直接调库。
一、一条命令的六个位置
ffmpeg -i 输入 -ss 定位 -t 时长 -c 编码 -crf 质量 输出
① 工具名:ffmpeg 本体。
② 输入:-i 后面跟文件或地址。
③ 定位与时长:-ss 开始位置、-t 持续多久。
④ 编码器:-c:v libx264 指定视频编码器。
⑤ 质量与开关:-crf 28 控质量、-an 丢掉音频。
⑥ 输出:最后一个参数,没有前缀。
实测一条典型命令有 13 个 token,其中 6 个是参数名、7 个是值。
参数名与值成对出现,只有 -an 这种开关是单独一个。
二、位置决定行为
-ss 放在 -i 前面是「快速定位」:
先跳到最近的关键帧,再从那里开始解,快但不精确。
放在 -i 后面是「解码定位」:
从文件开头逐帧解到那个点,慢但精确到帧。
同一个参数、两个位置、两种行为——这是 ffmpeg 最容易踩的坑。 而且它不报错,只是结果和你想的不一样。
一个实用判据: 切片段、做预览用「快速定位」(放前面), 做精确剪辑用「解码定位」(放后面)。 两者差的时间在大文件上能有几十倍。
三、最常用的十个参数
-i:输入。 -c copy:不重编码,只换容器。最省时间。
-c:v/-c:a:分别指定视频与音频编码器。
-crf:质量,18 到 28 之间最常用,越小越好。
-b:v:指定码率,直播必须用这个而不是 crf。
-r:帧率。 -s:分辨率。
-ss/-t:定位与时长。
-g:GOP 长度,切 HLS 时必须设。
-c copy 是最该记住的一个:
它把「重新编码」变成「重新封装」,一个小时的视频几秒就转完。
代价是编码参数一点都不能改——改分辨率、改码率都必须重编码。
四、动手 Demo:命令行——输入、输出、参数各在哪个位置
拆一条典型命令,看六个位置各是什么。
再用 PyAV 真跑一次「只取前 25 个包」,等价于 -t 1。
cmd = "ffmpeg -i in.mp4 -ss 00:00:10 -t 5 -c:v libx264 -crf 28 -an out.mp4"
toks = cmd.split()
print("共 %d 个 token" % len(toks))
print(" 工具 :", toks[0])
print(" 输入 :", " ".join(toks[1:3]))
print(" 定位 :", " ".join(toks[3:6]))
print(" 编码器 : -c:v libx264")
print(" 开关 : -crf 28 -an")
print(" 输出 :", toks[-1])
print()
import av, numpy as np, tempfile, os
p = os.path.join(tempfile.mkdtemp(), "a.mp4")
c = av.open(p, "w", format="mp4")
st = c.add_stream("libx264", rate=25)
st.width, st.height, st.pix_fmt = 320, 240, "yuv420p"
st.options = {"crf": "30", "preset": "veryfast", "g": "25"}
for i in range(50):
f = av.VideoFrame(320, 240, "yuv420p")
y = np.zeros((240, 320), np.uint8)
y[:, :] = np.linspace(16, 200, 320, dtype=np.uint8)[None, :]
f.planes[0].update(y.tobytes())
for pi in (1, 2):
f.planes[pi].update(bytes([128]) * f.planes[pi].buffer_size)
f.pts = i
for pk in st.encode(f):
c.mux(pk)
for pk in st.encode(None):
c.mux(pk)
c.close()
n = 0
with av.open(p) as c2:
for pk in c2.demux(video=0):
if pk.size == 0:
continue
n += 1
if n >= 25:
break
print("只取前 25 个包 -> %d 个,等价 -t 1" % n)
实跑输出:
--------------------------------------------------------------------
命令行——输入、输出、参数各在哪个位置
--------------------------------------------------------------------
**① 一条典型命令拆开看**
**原命令:**
ffmpeg -i in.mp4 -ss 00:00:10 -t 5 -c:v libx264 -crf 28 -an out.mp4
位置 片段 作用
---------------------------------------------
1 ffmpeg 工具名
2 -i in.mp4 输入
3 -ss 00:00:10 -t 定位与时长
4 -c:v libx264 视频编码器
5 -crf 28 -an 质量与丢音频
6 out.mp4 输出
**② 参数位置错了后果很具体**
**`-ss` 放在 `-i` 前面是「快速定位」:直接跳到关键帧,快但不精确。**
**放在 `-i` 后面是「解码定位」:逐帧解到那个点,慢但精确。**
**同一个参数、两个位置、两种行为——这是 ffmpeg 最容易踩的坑。**
**③ 真跑一次「按时间切一段」**
**原文件 50 帧、4711 字节。**
**帧率 25,时长 2.00 秒**
**只取前 25 个包,实际读到 25 个——就相当于 `-t 1`(1 秒 @25fps)。**
13 个 token 里 6 个是参数名,参数与值成对,只有 -an 单独。
-t 也可以写成 -to(到哪个时间点),两者差一个字的语义。
取前 25 个包就是 1 秒,因为帧率是 25——不是按字节截,是按包截。
30 秒实验
- 把
-ss移到-i前面,对比耗时——快很多,但落点会偏到一个关键帧上。 - 把
-c:v libx264换成-c copy,看还能不能改 crf——不能,copy 模式所有编码参数都被忽略。 - 把
-t 5改成-to 5,想想差在哪——-t是时长 5 秒,-to是到第 5 秒结束。
常见误区
- 「-ss 放哪都一样」——前面是快速跳、后面是逐帧解,差得远。
- 「-c copy 也能改码率」——不能,只是换容器。
- 「参数顺序无所谓」——顺序决定作用对象,是最容易错的地方。
自测三问(点开对答案)
1. `-ss` 放在 `-i` 前后有什么区别? —— **前面是快速定位(跳关键帧),后面是解码定位(逐帧精确)。** 2. `-c copy` 为什么快? —— **它只换容器、不重编码**,所以编码参数一律不能改。 3. 直播为什么用 `-b:v` 而不是 `-crf`? —— **直播要可控码率**,crf 会让码率随画面复杂度上下飘。- ☐ (本节完,下一节 7.1.3 FFmpeg 开发包)