libavutil 是底座:内存、字典、日志、枚举,全是工具,不管业务。 排错时唯一的信息源是它的日志系统,级别调到 VERBOSE 才看得见细节。
2.1 给了全景,这一篇拆最底下那层。 它没有「上下文」概念,谁都能调,调完不留痕迹。
一、它管四件事
类 代表 谁在用
内存 av_malloc、AVBuffer 所有库 字典 AVDictionary 传选项、设参数 日志 av_log 排错 枚举量 像素格式、采样格式 swscale 与 swresample
四件事里最有用的是字典与日志。 字典让你在不知道参数名有几个的情况下把选项传给库; 日志让你在不知道内部发生了什么时候看见它的抱怨。 内存与枚举量属于「知道它在」就够的那一类,平时不用直接调。
二、日志级别:排错的第一把钥匙
库不抛异常,它写日志。 你不开日志,就只能看见一个负数返回码。 级别 什么时候看
PANIC 库自己都快挂了 FATAL 这次调用注定失败 ERROR 出错,程序还能跑 WARNING 能跑但有隐患,最该看的一级 INFO 常规信息,默认 VERBOSE 细节,排格式问题开它 DEBUG 开发时用,量很大
命令行里的 -loglevel verbose 就是这张表的第五行。
它跟程序里 av_log_set_level 是同一个东西。
一条经验:报错但看不出原因,先开到 VERBOSE 重跑一遍。 多数「打不开」「不支持」的真实理由,
在 INFO 以下是看不见的。
三、AVDictionary:参数名的搬运工
很多接口的最后一个参数是 AVDictionary 的二级指针,这就是它出场的地方。 为什么不用函数参数?因为选项太多了。 打开一个 RTMP 连接能设几十个参数,写死成函数参数,接口就没法维护了。 操作 函数
设一个 av_dict_set 取一个 av_dict_get 遍历 av_dict_get 配合标志位 释放 av_dict_free
用完必须释放。 这类「传进去的空字典会被填满」的接口,是内存泄漏的高发区——库填进去的东西, 得你自己清。
四、它没有状态,所以谁都能用
libavutil 里没有上下文结构,全是纯函数与枚举。 这条设计带来两个好处:一是任何线程都能随便调,不用管生命周期; 二是它可以被别的库无条件依赖,不会形成环。 代价是它不含业务语义。 想「解一个 mp4」,它一个函数都帮不上; 但你想算个时间基、转个像素格式名,全在这儿。 本机像素格式 267 个,这个枚举就是它定义的。看起来数量夸张,常用的就十来个。
五、动手 Demo:libavutil——所有库脚下的那一层
这一段列出日志级别、数一遍像素格式,说明「工具层」的样子。 它没有上下文,所以拿不出「实例」来给你看。
import av
print("像素格式", len(av.video.format.names))
for lv in ("PANIC", "FATAL", "ERROR", "WARNING", "INFO", "VERBOSE"):
print(lv)
实跑输出:
--------------------------------------------------------------------
libavutil——所有库脚下的那一层
--------------------------------------------------------------------
**① 它管的四件事**
类 东西 谁在用
------------------------------------------------------------
内存 av_malloc、AVBuffer 所有库
字典 AVDictionary 传选项、给参数
日志 AVLog、av_log 排错时唯一的信息源
枚举量 像素格式、采样格式 swscale 与 swresample
**② 日志级别是本课最常用的一个枚举**
级别 什么时候看
--------------------------------------------
PANIC 库自己都快挂了
FATAL 这次调用注定失败
ERROR 出错,但程序还能跑
WARNING 能跑,但有隐患,最该看的一级
INFO 常规信息,默认级别
VERBOSE 细节,排格式问题时开
DEBUG 开发时用,量大
**命令行里 `-loglevel verbose` 对应的就是这张表。** 调不出来问题时,先把它开到 VERBOSE,
再看库到底说了什么。
**③ 像素格式一共 267 个**
这个数字看着大,常用的就十来个:yuv420p、nv12、rgb24、bgr24、gray。
**剩下的多为硬件格式与高位深格式**,做软件处理时基本碰不到。
**④ 它没有状态**
libavutil 里没有「上下文」这类东西。它全是工具函数与枚举,**谁都能调,调完不留痕迹**。
这也是它排在最底层的原因:不依赖别人,才可能被所有人依赖。
267 个像素格式,全是 libavutil 定义的枚举。
日志级别那七行,在命令行里就是 -loglevel 的取值。
30 秒实验
ffmpeg -loglevel verbose -i 某文件与默认级别各跑一次,diff 两份输出。python -c "import av; print(len(av.video.format.names))"数像素格式。- 在代码里
av_dict_set传一个参数给avformat_open_input,看它认不认。
常见误区
- 只用返回码不看日志。 返回码只告诉你失败,日志告诉你为什么失败。
- 忘了
av_dict_free。 库填进去的字典要自己清,不清就漏。 - 以为像素格式越多越好。 267 个里常用的十来个,选对格式比数量重要。
自测三问(点开对答案)
1. 库报错时第一步做什么? —— 把日志级别开到 VERBOSE 重跑一遍。多数「不支持」的真实原因在默认级别下看不见。 2. 为什么很多接口用 AVDictionary 传参,而不是写函数参数? —— 选项太多,写死会让接口无法维护。字典可以随版本加参数而不改函数签名。 3. libavutil 为什么能被所有库依赖? —— 它不带状态、没有上下文,是纯函数与枚举的集合,依赖它不会形成循环。- ☐ (本节完,下一节 2.3 libavformat 简介)