同一份信息五种写法,选哪个完全看「谁来读」。 程序读用 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 秒实验
- 给人看:
ffprobe -show_streams 片子.mp4,默认格式,缩进清楚。 - 给程序用:
ffprobe -v error -print_format json -show_streams 片子.mp4,再| python -m json.tool美化。 - 给 shell 用:
ffprobe -v error -print_format flat -show_streams 片子.mp4 | grep codec_name。
常见误区
- 自动化脚本不加
-v error。警告混进 JSON,解析直接失败,报错信息还误导人。 - 用 default 格式做字符串切割。缩进和字段顺序都可能变,一升级就崩。
- 长期脚本里用 flat。数组下标是写死的,换个文件就要改。flat 只适合临时看一眼。
自测三问(点开对答案)
1. `-of` 和 `-print_format` 是什么关系? —— 完全等价,`-of` 是简写。写哪个都行,效果一样。 2. 为什么 flat 特别适合 grep? —— 因为它把完整路径写在一行里(如 `streams.streams[0].codec_name=h264`),不用管嵌套层级。 3. JSON 输出解析失败,最可能是什么原因? —— 没加 `-v error`,警告也打到了标准输出,混进了 JSON 里。加这个开关就干净了。- ☐ (本节完,下一节 3.3.6 select_streams)