🎬 第三章 FFmpeg三大常用工具及应用选项详解
📄 一句话概括

同一份信息五种写法,选哪个完全看「谁来读」。 程序读用 json,人看用 default,shell 切用 flat。

📝 本节和前面几节的关系

3.3.4 讲完了「看什么」,这一节讲「怎么看」。 3.3.6 讲怎么在多流文件里挑一路看。

一、五种输出格式

输出内容是同一份数据,只是排版不同:

取值 长什么样 谁用


default 默认的缩进文本 人眼看 json 标准 JSON 程序解析 flat 一行一个键值 shell 里 grep ini 分节的键值 配置文件风格的工具 csv 逗号分隔 导进表格

-of 是 -print_format 的简写,两个完全等价。 所以 -of json 和 -print_format json 是同一条命令的两种写法。

选哪个只看一件事:谁来读。 人看用 default(有缩进好扫),程序读用 json(结构稳), shell 里切用 flat(一行一个,grep 一下就到了)。

二、flat 为什么好 grep

default 的缩进对人友好,对 shell 极不友好:

格式 取一个字段要做什么 稳不稳


default 数缩进层级,再切冒号 层级一变就崩 flat grep 全路径,一行到手 很稳 json 喂给 python -m json.tool 最稳

flat 的关键是「把完整路径写全」:streams.streams[0].codec_name=h264。 所以想取某条流的编码名,grep codec_name 一行就到,不用管嵌套。

但它也有代价:数组下标写死了,streams[0] 换一个文件就要改。 所以 flat 适合「看一眼」,不适合「写进长期维护的脚本」。

三、做自动化必须加的那个开关

这一条坑过很多人,值得单独占一节:

写法 少了什么 后果


ffprobe -print_format json 没加 -v error 警告混进输出,解析失败 ffprobe -v error -print_format json 齐全 输出是纯 JSON

ffprobe 默认会把警告也打到标准输出,于是 JSON 里混了一行英文。 json.loads 直接抛错,而错误信息看起来像是文件损坏—— 其实只是多了一行「某某字段无法识别」。

加 -v error 把日志压到只剩错误,输出就干净了。 这个习惯值得写进所有脚本模板里,因为它几乎零成本。

四、动手 Demo:同一份信息,五种写法

取同一份流信息,分别渲染成 json、flat、ini 三种,比较行数与字节数。 目的是看清「格式不同、内容相同」这件事。

import json
import av

c = av.open(u"av3.mp4")
cc = c.streams.video[0].codec_context
info = {u"codec_name": cc.name, u"width": cc.width,
        u"height": cc.height, u"pix_fmt": cc.pix_fmt}
c.close()

j = json.dumps({u"streams": [info]}, ensure_ascii=False, indent=2)
flat = u"|".join(u"streams.streams[0].%s=%s" % (k, v)
                   for k, v in info.items())
ini = u"[STREAM]\n" + u"\n".join(u"%s=%s" % (k, v)
                                  for k, v in info.items())

for name, txt in ((u"json", j), (u"flat", flat), (u"ini", ini)):
    print(u"%-5s %d 行 %d 字节"
          % (name, len(txt.split(u"\n")), len(txt.encode(u"utf-8"))))

实跑输出:

--------------------------------------------------------------------
ffprobe -print_format——同一份信息,五种写法
--------------------------------------------------------------------

**① 五种输出格式,给四种不同的消费者**

  取值        长什么样           谁用
  -----------------------------------------------------
  default     默认的缩进文本     人眼看
  json        标准 JSON          程序解析
  flat        一行一个键值       shell 里 grep
  ini         分节的键值         配置文件风格的工具
  csv         逗号分隔           导进表格

**五个选项的输出内容是同一份数据,只是排版不同。**
**所以选哪个完全看「谁来读」——人看用 default,程序读用 json。**

**② 同一份流信息,写成这三种,差别有多大**

  格式     行数     字节数
  ----------------------------
  json     10       129
  flat     1        127
  ini      5        60

**flat 只有一行,但它把路径写全了——这就是它「好 grep」的原因。**
**要取某个字段,`grep streams.streams` 一下就出来,不用管缩进层级。**

**③ -print_format 之外还有个 -of**

**`-of` 是 `-print_format` 的简写,两个完全等价。**
**所以 `-of json` 和 `-print_format json` 是同一条命令的两种写法。**

**④ 做自动化时还有一个必加的开关**

  写法                                    少了什么          后果
  ------------------------------------------------------------------------------------
  ffprobe -print_format json              没加 -v error     警告混进输出,解析失败
  ffprobe -v error -print_format json     齐全              输出是纯 JSON

**上面那个差别坑过很多人:ffprobe 会把警告也打到 stdout,**
**于是 json.loads 直接报错,而错误信息看起来像是文件坏了。**
**加 `-v error` 把日志压掉,输出的就是干净 JSON。**

三份输出的信息量完全一样,差别只在线条数和字节数。 json 行数最多是因为它每层都换行缩进,flat 只有一行是因为它全挤在一起。

flat 那一行里的路径是完整的:streams.streams[0].codec_name。 这就是它「好 grep」的全部秘密——路径写全了,就不用关心嵌套层级。

注意 ini 的写法:它靠 [STREAM] 这种分节标记来组织。 所以多流时会出现 [STREAM]、[STREAM] 重复的分节,读的时候要按顺序对应。

这段代码用的是 Python 的 json 模块,不是 ffprobe 本身。 但生成出来的结构与 -print_format json 是一致的—— 因为 ffprobe 输出的 JSON 也是「顶层一个对象、里面套 streams 数组」。

30 秒实验

  1. 给人看:ffprobe -show_streams 片子.mp4,默认格式,缩进清楚。
  2. 给程序用:ffprobe -v error -print_format json -show_streams 片子.mp4,再 | python -m json.tool 美化。
  3. 给 shell 用:ffprobe -v error -print_format flat -show_streams 片子.mp4 | grep codec_name。

常见误区

  1. 自动化脚本不加 -v error。警告混进 JSON,解析直接失败,报错信息还误导人。
  2. 用 default 格式做字符串切割。缩进和字段顺序都可能变,一升级就崩。
  3. 长期脚本里用 flat。数组下标是写死的,换个文件就要改。flat 只适合临时看一眼。
自测三问(点开对答案) 1. `-of` 和 `-print_format` 是什么关系? —— 完全等价,`-of` 是简写。写哪个都行,效果一样。 2. 为什么 flat 特别适合 grep? —— 因为它把完整路径写在一行里(如 `streams.streams[0].codec_name=h264`),不用管嵌套层级。 3. JSON 输出解析失败,最可能是什么原因? —— 没加 `-v error`,警告也打到了标准输出,混进了 JSON 里。加这个开关就干净了。

相关笔记