三个 exe 压在同一个内核上:ffmpeg 转、ffplay 放、ffprobe 看。 一条命令按「段」读,退出码只有三种结局。
第一章讲了七个库,第二章摸了一遍命令。这一章把三个工具的选项逐个过一遍。 3.2 讲 ffplay,3.3 讲 ffprobe,3.4 起回到 ffmpeg 自己的选项。
一、三个工具,三句话分工
这三个 exe 装在同一目录下,用的是同一批动态库:
工具 一句话职责 输入 输出
ffmpeg 把输入转成输出 文件/流/设备 文件/流 ffplay 把输入放给人看 文件/流/设备 屏幕+声音 ffprobe 把输入的信息打出来 文件/流 文本/JSON
它们认识的格式完全一样——因为格式识别这件事是 libavformat 干的,不是 exe 干的。 所以 ffprobe 能读出来的东西,ffmpeg 一定也能读,区别只在它顺手把东西转了。
反过来说,一个文件 ffprobe 读不出来,ffmpeg 也一定读不出来。 这就是为什么排障时先跑 ffprobe:它连解码都不做,能到的最远处就是格式识别。
二、命令是按段读的
一条 ffmpeg 命令分成三段,边界是两个「地标」:
段 起止地标 常见参数
全局 程序一启动就生效 -y -hide_banner -loglevel 输入 到下一个 -i 为止 -ss -t -r -f -stream_loop 输出 到下一个输出文件为止 -c:v -b:v -s -pix_fmt
段内顺序不影响含义,跨段才改变含义。
所以 -ss 写两遍可以,写一遍就得挑对位置——这是 2.1 讲过的那条规则。
这里要补一句 2.1 没说的:全局段在 Windows 上有个特殊写法。
-y 这类全局参数如果写在 -i 后面,某些版本会报「Unrecognized option」。
所以养成习惯:全局参数一律写在最前面,不要夹在中间。
三、退出码只有三种结局
脚本里判断成败只能看退出码,不能看日志里有没有 error 字样:
退出码 含义 典型触发
0 全部成功 正常转完 1 出错退出 文件不存在、参数不合法 其他 被信号打断 Ctrl+C、被系统杀掉
为什么不能看日志?因为 ffmpeg 遇到「可恢复的小问题」也会打 error。 比如某个元数据字段解析不了,它会打一行 error,然后照样把片子转完,退出 0。
反过来也有陷阱:-loglevel quiet 时一行日志都不打,但退出码照样是 0 或 1。
所以「静默 + 看退出码」是最稳的组合,也是所有自动化流水线的标准做法。
四、动手 Demo:三个外壳压在同一个内核上
沙箱里没有这三个 exe,但有它们链接的同一批库。 这一段用库把「三个工具各自的能力边界」和「这套构建的规模」量出来。
import av
# 三个工具共用这三个计数器
print(u"格式 %d 个" % len(av.formats_available))
print(u"编解码器 %d 个" % len(av.codecs_available))
print(u"滤镜 %d 个" % len(av.filter.filters_available))
# 七个库的版本,证明是同一份内核
for k, v in sorted(av.library_versions.items()):
print(u"%-14s %s" % (k, v))
实跑输出:
--------------------------------------------------------------------
ffmpeg 工具简介——三个外壳压在同一个内核上
--------------------------------------------------------------------
**① 三个 exe,分工就三句话**
工具 一句话职责 输入 输出
--------------------------------------------------------------------
ffmpeg 把输入转成输出 文件/流/设备 文件/流
ffplay 把输入放给人看 文件/流/设备 屏幕+声音
ffprobe 把输入的信息打出来 文件/流 文本/JSON
**这三个工具都链同一批 libav* 动态库,所以它们认识的格式一个不差。**
**ffprobe 能读出来的东西,ffmpeg 一定也能读,区别只在它顺手把东西转了。**
**② 一条命令是按「段」读的,不是按「参数顺序」读的**
段 起止地标 常见参数
---------------------------------------------------------------
全局 程序一启动就生效 -y -hide_banner -loglevel
输入 到下一个 -i 为止 -ss -t -r -f -stream_loop
输出 到下一个输出文件为止 -c:v -b:v -s -pix_fmt
**同一段里参数的先后不影响含义,跨段才会改变含义。**
**这就是为什么 `-ss` 写两遍可以,写一遍就得挑对位置。**
**③ 退出码只有三种结局**
退出码 含义 典型触发
-------------------------------------------------------
0 全部成功 正常转完
1 出错退出 文件不存在、参数不合法
非 0 其他 被信号打断 Ctrl+C、被系统杀掉
**脚本里判断成败只能看退出码,不能看日志里有没有 error 字样。**
**因为 ffmpeg 遇到可恢复的小问题也会打 error,但最后照样退出 0。**
**④ 本机这套库的规模,用一行代码就能数出来**
库 暴露出来的条目 对应命令
-----------------------------------------------
libavformat 414 个格式 -formats
libavcodec 557 个编解码器 -codecs
libavfilter 468 个滤镜 -filters
**这三个数就是 `ffmpeg -formats`、`-codecs`、`-filters` 的行数。**
**它们不是「功能清单」,是「编译进这份二进制的东西」——换一份构建,三个数全变。**
这三个计数器就是 ffmpeg -formats、-codecs、-filters 的行数。
它们不是「功能清单」,是「编译进这份二进制的东西」——换一份构建三个数全变。
七个库的版本号也要看一眼:它们并不齐步走。 codec 和 format 都到了 62,可 filter 只有 11、swscale 只有 9—— 每个库自己记自己的接口变更次数,谁改得多谁号大。
「三个外壳同一个内核」这句话的实际意义在这里: 你在 ffprobe 里看到的时间基、帧率、像素格式,和 ffmpeg 编码时用的是同一套值。 所以 3.3 那几节读出来的数,可以直接拿来算 3.5 的参数。
30 秒实验
- 数一次:
ffmpeg -version最后那段 configuration 里全是--enable-,数一遍就知道这份构建开了哪些开关。 - 比一次:
ffmpeg -formats和ffprobe -show_format对同一个文件,看两边的说法是否一致。 - 试一次错:故意给一个不存在的文件,
echo $?看退出码是不是 1。
常见误区
- 以为三个 exe 各有一套格式支持。它们链接同一批库,格式识别能力完全相同。区别只在「拿识别结果做什么」。
- 用日志里有没有 error 判断成败。可恢复的小问题也会打 error,但退出码仍是 0。只认退出码。
- 把全局参数写在中间。
-y、-loglevel这类必须写在最前面,夹在-i之间某些版本会报错。
自测三问(点开对答案)
1. 为什么 ffprobe 读不出来的文件,ffmpeg 也一定读不出来? —— 因为格式识别是 libavformat 干的,两个工具调的是同一份代码。读不出来说明这一层就没过,后面无从谈起。 2. 退出码为 0 是否代表完全没问题? —— 代表「程序认为任务完成了」。它可能带着警告完成,也可能只转出了一半就被容错机制收了尾。要更严就开 verbose 看日志。 3. 三个工具里哪个最安全地用来「检查一个文件」? —— ffprobe。它只读不写,跑一百遍也不会改坏文件;ffmpeg 一旦参数写错就可能覆盖掉东西。- ☐ (本节完,下一节 3.2 ffplay 工具简介)