八级日志,每级差 8;默认 INFO 太安静,排错先开 VERBOSE。 没日志不等于没事,可能压根没进到那层。
5.1 说 libavutil 管日志,这一节把日志用起来。 下一篇 5.3 换一个工具:解析器。
一、八个级别,数值是 C 侧的原样
级别之间有严格的包含关系:设成 VERBOSE,VERBOSE 及更严重的都打。 级别 数值 什么时候看
PANIC 0 进程要死了 FATAL 8 致命错误 ERROR 16 出错了 WARNING 24 有隐患 INFO 32 默认,信息很少 VERBOSE 40 排错第一档 DEBUG 48 看清每步 TRACE 56 内部细节
每级正好差 8,这是 C 侧的原始取值。记住 16、24、32 三个够用。 设置有两个函数:set_level 管 PyAV 自己的日志,set_libav_level 管底层库的。 一般用前者就够。
二、抓日志的三个要素
Capture 抓到的每条是三样东西:级别、来源、正文。 要素 例子 有什么用
级别 48 过滤噪音 来源 AVFormatContext 看是谁在说话 正文 Opening '…' for reading 真正的内容
「来源」这一栏比想象中有用。 写着 AVFormatContext 的是容器层, 写着具体格式名(mov,mp4,m4a…)的是那个解复用器。一眼看出问题出在哪一层。 设成 WARNING 之后,八级的噪音就压掉了六成:本机读这个小文件 DEBUG 有 98 条, 只留 WARNING 以上就只剩几条。排错与日常运行该用不同级别。
三、日志里能看出什么
举三条本机抓到的真话。 日志正文 读出来的意思
Setting default whitelist… 白名单默认就开着 probed with size=2048 and score=100 猜格式猜得很确定 Unknown dref type 0x206c7275 容器里有个不认识的引用
第一条纠正了一个常见误解:以为协议白名单要显式传才生效,其实默认就有一套。 第二条是猜格式的关键线索:score 满分是 100,分数低说明猜得不确定,格式可能认错。 遇上「明明是这个格式却打不开」,先看这一行的分数。 第三条说明「有不认识的东西」不等于「打不开」:这个文件照样读得好好的。 日志里有些是噪音,要学会跳过去。
四、一个反直觉的实测
打开一个不存在的文件,Capture 抓到 0 条日志。 按直觉该有一条 ERROR 说「文件不存在」,实际一条也没有。 因为 PyAV 在 Python 层就把路径挡住了,根本没走到 libavformat。 这条实测值一个结论:没日志不等于没事。 错误可能发生在更上层,那一层不用 FFmpeg 的日志系统。 排错顺序因此该是:先确认错误在哪一层(看异常类型与栈),再决定要不要去调日志级别。 反过来做会白折腾。
五、动手 Demo:打开 DEBUG,看 FFmpeg 自己在说什么
八级各是什么、抓一次真实日志、再制造一次错误看日志是否出现。
import av
import av.logging
av.logging.set_level(av.logging.DEBUG)
with av.logging.Capture() as cap:
c = av.open("_media32/tiny.mp4")
for p in c.demux(c.streams.video[0]):
pass
c.close()
av.logging.set_level(av.logging.WARNING)
print(len(cap))
print(cap[0])
实跑输出:
--------------------------------------------------------------------
打开 DEBUG,看 FFmpeg 自己在说什么
--------------------------------------------------------------------
**① 八个级别,每级差 8**
级别 数值 什么时候看
-----------------------------------------
PANIC 0 进程要死了
FATAL 8 致命错误
ERROR 16 出错了
WARNING 24 有隐患但不致命
INFO 32 默认档,信息很少
VERBOSE 40 排错第一档
DEBUG 48 看清每一步
TRACE 56 连内部细节都打
**每一级正好差 8**,这是 C 侧的原始取值,不是 PyAV 编的。
记住 16 是 ERROR、24 是 WARNING、32 是 INFO 就够用。
**② 抓一次真实的 DEBUG 日志**
打开并读完这个小文件,一共抓了 **98** 条。
**每条是三样东西:级别、来源、正文。** 头四条原文:
[0] 级别 48 / 来源 AVFormatContext
Opening '_media32/tiny.mp4' for reading
[1] 级别 48 / 来源 file
Setting default whitelist 'file,crypto,data'
[2] 级别 48 / 来源 mov,mp4,m4a,3gp,3g2,mj2
Format mov,mp4,m4a,3gp,3g2,mj2 probed with size=2048 and score=100
[3] 级别 48 / 来源 mov,mp4,m4a,3gp,3g2,mj2
ISO: File Type Major Brand: isom
**来源那一栏很有用**:写着 AVFormatContext 的是容器层,写着具体格式名的是那个解复用器。
**一眼看出这话是谁说的。**
**③ 日志里藏着有用的线索**
上面第二条写着 Setting default whitelist 'file,crypto,data'。
这说明**协议白名单默认就开着**,不是只有显式传才生效。
第三条写着 probed with size=2048 and score=100,说的是「拿前 2048 字节猜格式,猜得很确定」。
**猜错格式的时候,这一行会变成低分**,那就是线索。
**④ 一个反直觉的实测:Python 层拦下的错,FFmpeg 根本没说话**
打开不存在的文件,抛的是 **ArgumentError**。
而这次 Capture 抓到 **0** 条日志。
**是 PyAV 在 Python 层就把路径挡住了,没走到 libavformat。**
所以日志是空的。**没日志不等于没事,可能压根没进到那一层。**
**这条实测能省很多冤枉路**:调不出日志时先确认错误发生在哪一层,别一门心思调日志级别。
每条是三元组:级别数字、来源名、正文。 把级别设回 WARNING 很重要,否则后面的运行全是噪音。
30 秒实验
- 把
set_level换成av.logging.INFO,看抓到的条数少多少。 - 打开一个不存在的文件名,看 Capture 抓到几条。
av.logging.get_skip_repeated()看重复日志是否被折叠。
常见误区
- 以为没日志就是没事。 Python 层拦下的错,FFmpeg 不说话。
- 一直开着 TRACE。 性能会明显下降,日志量能把磁盘写满。
- 只看正文不看来源。 来源栏告诉你哪一层在说话,省一半排查时间。
自测三问(点开对答案)
1. 八个级别的数值为什么是 0、8、16…? —— 每级差 8,是 C 侧 AV_LOG_* 的原始取值。设某一级,比它严重的都会打。 2. 打开不存在的文件为什么抓不到日志? —— PyAV 在 Python 层就拦住了,没进到 libavformat,所以库没说话。 3. 日志里 score 那一行有什么用? —— 表示猜格式的确定程度,满分 100。分数低说明格式可能被认错。- ☐ (本节完,下一节 5.3 AVParseUtil 应用案例及剖析)