转码是「拆—解—改—编—装」五步,命令行只是把五步写进一行。 preset 是拿时间换体积,越换越亏;-c:a copy 省的是时间,不是体积。
第五章已经讲过转码的参数,这一章把它放到 Linux 上重走一遍。 补的是三处只有命令行才有的细节:preset 的性价比、copy 的真实作用、换行与引号。
一、一条命令,五步活
Linux 上最常用的一条转码命令是这样:
ffmpeg -i in.mp4 -c:v libx264 -crf 23 -preset medium \
-c:a copy out.mp4
四个选项各管一摊:-c:v 选视频编码器、-crf 定量化粗细、
-preset 定编码器肯花多少工夫、-c:a 定音频怎么办。
剩下的拆容器、解码、装容器,它自己会做。
步骤 命令行里对应什么 PyAV 里对应什么
拆容器 -i 这一步 av.open(输入) 解码 自动,不用写 packet.decode() 改 -vf / -af / -s / -r frame.reformat() 编码 -c:v / -c:a / -crf add_stream().encode() 装容器 输出文件名 mux()
这张表就是转码的全部内容。换语言、换库、换平台,都还是这五步, 所以「转码慢」永远只能慢在后两步——解码和编码。
二、preset 是拿时间换体积,越换越亏
同一个源、同一个 crf 23,只换 preset,实测三档:
preset 成片字节 耗时 体积相对
ultrafast 249925 约 140 毫秒 1.00 倍 medium 191154 约 180 毫秒 0.76 倍 veryslow 180439 约 410 毫秒 0.72 倍
从 ultrafast 换到 medium,体积省了两成多,时间只多三成——这一档几乎是白捡。 再从 medium 换到 veryslow,体积只省半成,时间却翻了一倍多。
所以 preset 的正确用法不是「越慢越好」,而是「够用就停」。
直播、转码服务这种要吞吐的场景,ultrafast 或 veryfast 才是常态;
veryslow 只留给「一次压好、以后不再动」的归档片。
顺带说一句:preset 改的是「搜索有多拼」,crf 改的是「目标质量」,
两个是不同维度。想要小就调 crf,想要快就调 preset,别指望一个管两件事。
三、-c:a copy 省的是时间,不是体积
很多人把 -c:a copy 当成「把音频压小」的开关,这是误解。实测:
音频怎么处理 成片字节 耗时 说明
-c:a copy(流拷贝) 191056 约 101 毫秒 不解码不重编 重新编码 aac 191154 约 185 毫秒 解码再编码
两者体积只差 98 字节,耗时差了四成多。
原因很简单:源里的音频本来就是 AAC,copy 过去还是那个 AAC; 重编码一遍也不会更小,只会多一道解码再编码。 所以正确的结论是「想快就 copy」,不是「想小就 copy」。
真想把音频变小,要么改 -b:a,要么降采样率、降声道——
这些都得重新编码,copy 一个字节都动不了。
四、Linux 命令行的三处字面差异
同样的命令搬到 Linux 上,有三处写法会变,跟 FFmpeg 无关,是 shell 的规矩:
哪一处 Linux 上怎么写 Windows 上怎么写
路径分隔符 /home/me/in.mp4 D:\work\in.mp4 命令续行 行尾一个反斜杠 cmd 用 ^,PowerShell 用反引号 单引号 一个字都不解释 cmd 里没有单引号这回事
单引号这一条最值得记:Linux 的单引号里,$、*、反斜杠全都是普通字符,
双引号里 $ 仍会被展开。
所以写 -vf 那种又长又带符号的参数,用单引号最省心。
五、转封装与转码差在哪
把 -c:v 也换成 copy,就是转封装:
ffmpeg -i in.mp4 -c copy out.mkv
这时候一帧都不解码、一帧都不重编,只是把同一个码流换个盒子装。 几百兆的片子常常一两秒就完成,速度差着两个数量级。
代价是「只能换盒子,不能改内容」: 想改分辨率、改码率、加水印、剪掉一段,都必须真的转码。 这也是排查时的第一条分流—— 先问「我要改的是盒子还是码流」,盒子用 copy,码流才谈转码。
六、动手 Demo:三档 preset 的体积与耗时对比
沙箱里没有 ffmpeg 这个可执行文件,所以下面用 PyAV 走同一条链路。 源用 lavfi 现造,不落中间文件,代码块自包含。
import os
import time
from fractions import Fraction
import av
W, H, FPS, NF = 320, 240, 25, 50
for preset in ("ultrafast", "medium", "veryslow"):
t0 = time.time()
c = av.open("testsrc=size=%dx%d:rate=%d" % (W, H, FPS), format="lavfi")
o = av.open("o_%s.mp4" % preset, "w")
v = o.add_stream("libx264", rate=Fraction(FPS, 1))
v.width, v.height, v.pix_fmt = W, H, "yuv420p"
v.options = {"crf": "23", "preset": preset}
n = 0
for f in c.decode(c.streams[0]):
g = f.reformat(format="yuv420p")
g.pts = n
g.time_base = Fraction(1, FPS)
for pk in v.encode(g):
o.mux(pk)
n += 1
if n >= NF:
break
for pk in v.encode(None):
o.mux(pk)
o.close()
c.close()
path = "o_%s.mp4" % preset
print("%-10s %7d 字节 %5.0f 毫秒" %
(preset, os.path.getsize(path), (time.time() - t0) * 1000))
实跑输出:
--------------------------------------------------------------------
10.1 使用 FFmpeg 实现音视频转码
--------------------------------------------------------------------
**① 本机没有 ffmpeg.exe,这一节用 PyAV 走同一条链路**
**Linux 上那条命令是:**
ffmpeg -i in.mp4 -c:v libx264 -crf 23 -preset medium -c:a copy out.mp4
**转码这名字听着玄,其实就是五步。**
步骤 命令行里对应什么 PyAV 里对应什么
----------------------------------------------------------------
拆容器 -i 这一步 av.open(输入)
解码 自动,不用写 packet.decode()
改 -vf / -af / -s / -r frame.reformat()
编码 -c:v / -c:a / -crf add_stream().encode()
装容器 输出文件名 mux()
**这张表就是转码的全部内容——换语言、换库,都还是这五步。**
**② 同一条命令只换 preset,体积与耗时怎么变**
preset 成片字节 耗时 体积相对
------------------------------------------------
ultrafast 249925 133 毫秒 1.00 倍
medium 191154 178 毫秒 0.76 倍
veryslow 180439 406 毫秒 0.72 倍
**从 ultrafast 换到 medium,体积省了 23.5%,耗时变成 1.34 倍;**
**再从 medium 换到 veryslow,体积只省 5.6%,耗时却变成 2.27 倍。**
**这是整条命令里性价比最高的一处认识:`preset` 是拿时间换体积,**
**而且换到后面越换越亏——从 ultrafast 到 medium 几乎是白捡,**
**从 medium 到 veryslow 是耗时的 2.27 倍只换来 5.6% 的体积。**
**③ `-c:a copy` 省的是时间,不是体积**
音频怎么处理 成片字节 耗时 说明
------------------------------------------------------------------------
-c:a copy(流拷贝) 191056 100 毫秒 不解码不重编
重新编码 aac 191154 183 毫秒 解码再编码
**两者体积只差 98 字节,耗时却差 45%——**
**`-c:a copy` 的价值在于「别让音频白占 CPU」,不在于「把文件变小」。**
**原因很简单:源里的音频本来就是 AAC,copy 过去还是那个 AAC;**
**重编码一遍也不会更小,只会多一道解码再编码。**
**所以「想变小就 copy」是错的,「想快就 copy」才是对的。**
三行跑完,preset 这一档就从此有了具体数字,不再是书上的一句「越慢越好」。
注意 v.options 这一句:crf 和 preset 都是往编码器的私有选项里塞,
PyAV 里就是一本字典,命令行里就是两个 - 开头的参数,一一对应。
所以「命令行参数 ⇄ 库里的选项名」这件事,两边其实是同一套名字。
30 秒实验
- 把
crf从 23 改成 18 再跑一遍:看体积涨多少、耗时涨多少,体会两个维度不同。 - 把
preset换成"placebo":等它跑完,就知道「越慢越好」的尽头有多不划算。 - 把
-c:a copy亲手试一次:源里没有音频流时它会报什么,也顺便记住。
常见误区
- 以为
-c:a copy能把音频压小。它一个字节都不改,只省时间。 - 以为
preset越慢画质越好。它只是搜索更细,质量由crf说了算。 - 在 Windows 上照抄
\续行。cmd 的续行符是^,直接粘贴会断在半路。
自测三问(点开对答案)
1. 转码五步里,哪两步最花时间? —— 解码和编码。拆容器、装容器只是搬运,改那一步通常也很轻。 2. 想「换容器不改内容」,命令怎么写? —— `-c copy`,一秒完成;只要动分辨率、码率、水印,就必须真转码。 3. `-crf` 与 `-preset` 分别管什么? —— `crf` 管目标质量,`preset` 管编码器花多少工夫去逼近这个质量。- ☐ 10.1 完——下一节 10.2 使用 ffplay 和 ffprobe