何为AVOption?AVClass? 及其关联的若干个Context
---------------------------------------------------------------------
author:hjjdebug
date: 2022年 09月 30日 星期五 20:17:50 CST
---------------------------------------------------------------------
AVOption 是一个结构,它的每一个实例,用来描述AVContext 中一个成员变量.
这一句话挺抽象的,包含了不少信息.
AVContext 当然也是一个结构, 是用户自定义的一个结构。 c语言也就只有结构了.
我们随便定义这个结构,但是要求它的第一个成员是AVClass 指针.
又是挺关键的一句话.
举个例子吧:
typedef struct TestContext {
const AVClass *obj;
char *name; //用AVOption opt_name 来描述, 也可以叫opt[1]或其它名字
int telenum; //用AVOption opt_tele 来描述, 也可以叫opt[2]或其它名字
int flags; //用AVOption opt_flags 来描述, 也可以叫opt[3]或其它名字
}
opt_name 可能需要如下信息来表达:
ctx中有一个成员,它的名字叫"name",它的默认值是"张三",它在ctx偏移是offset(ctx,name),它是个char*型变量, 表示人员的名称
opt_telenum 可能需要如下信息来表达:
ctx中有一个成员,它的名字叫telenum,它的默认值是12345678,它在ctx偏移是offset(num,telenum),它是个整型变量, 代表电话号码
opt_flags 可能需要如下信息来表达:
ctx中有一个成员,它的名字叫flags,它的默认值是0x5,它在ctx偏移是offset(num,flag),它是个int型变量,具体意义参考flags比特位定义
有了这个认识,我们来定义一下AVOpton 结构,直接看一下ffmpeg 中的定义吧。 这下就看懂了.
type = struct AVOption {
const char *name; // 定义一个结构变量名称
const char *help; // 对这个名称可以进一步解释,这是给人看的
int offset; // 计算机找成员不看文字,它要找偏移
enum AVOptionType type; // 类型说明,说明是整形,实型,布尔型及其它型请看代码定义
union { // 数据类型的一个联合,储存默认的数值,其类型有 type 决定
int64_t i64;
double dbl;
const char *str;
AVRational q;
} default_val;
double min; //数据有效值的最大值,最小值范围
double max;
int flags; //定义了是否是编码参数,解码参数,音频参数,视频参数等信息,可以认为是使用类型,具体请看代码定义
const char *unit; //属于那个单元(部门)
}
下面我们用c代码定义3个AVOption对象
static const AVOption test_class_options[]= {
{"name", "set person name", OFFSET(person), AV_OPT_TYPE_STRING, { .str = "张三" }, CHAR_MIN, CHAR_MAX, 1 },
{"telenum", "set telephone num", OFFSET(telenum), AV_OPT_TYPE_INT, { .i64 = 12345678 }, INT_MIN, INT_MAX, 1, NULL},
{"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, { .i64 = 1 }, 0, INT_MAX, 1, "flags" },
}
将以上 test_class_options 选项用AVClass 对象来包含.
static const AVClass obj1 = {
.class_name = "TestContext",
.option = test_class_options,
... 其它项还有其它功能,例如层级划分等,这里都不考虑,给默认值了.
};
使用举例:
TestContext test_ctx = { 0 }; //声明一个ctx 对象, 它包含一个AVClass 指针, 这个ctx会有很多成员变量,每个成员变量都会有一个AVOption 项与之对应
test_ctx.obj = &obj1; // 为这个AVClass 指针赋值,这个AVClass对象会包含一个AVOption 指针,AVOption 中定义了所有默认变量数值。
av_opt_set_defaults(&test_ctx); //然后可以为这个ctx所有其它变量付初始值, 初始值就是AVOption 项中定义的, 一个函数调用就全部赋值完成了,很强大!
//然后你可以用一般的方法printf, 打印出ctx中各成员变量的值
//当然也可以用它写的专有函数,打印内部信息,例如: av_opt_show2(&test_ctx, NULL, -1, 0);
//发现还是其内部写的函数,打印的漂亮!打印了选项的名称,数值类型,说明及它属于什么使用类型
自然,为了灵活的操控这些成员变量,av_opt还定义有其它函数, av_opt_get, av_opt_set, av_opt_find... 等
阅读一下libavutil/opt.c 会让你受益匪浅,尤其是代码跟入之后可了解其全部细节.
它演示了全部用字符串来修改成员的值及程序判错功能. 只所以它能统一用字符串输入,是因为它内部有一个系列分别对应不同的类型解析.
也演示了对象的序列化输入,输出. 就是用一个长字符串来操作对象(get,set),这就是字符串切分和合并的问题了.
这样你可以了解AVOption 结构,并大致了解AVClass 结构及如何客户化一些Context结构.
重点:
AVOption是用来描述Context成员的结构
AVClass是用来描述Context结构的结构
ffmpeg 有400多个AVClass 对象, 意味着有400多种结构.
参考: 1. libavutil/tests/opt.c 代码
参考: 2. ffmpeg 相关代码
实例胜千言,放上我自己的测试代码:
#include <stdio.h>
extern "C"
{
#include <libavutil/opt.h>
#include <libavutil/log.h>
};
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
//第一个成员是AVClass 指针的类叫context 类,
//换句话说, AVClass 是描述任意context类的结构, 它要放在context类的第一个成员位置.
typedef struct stTestContext {
const AVClass *obj;
char *name; //用AVOption opt_person 来描述, 也可以叫opt[1]或其它名字
int telenum; //用AVOption opt_tele 来描述, 也可以叫opt[2]或其它名字
int flags; //用AVOption opt_flags 来描述, 也可以叫opt[3]或其它名字
}CPerson;
//由于定义了类型, 表格中可以使用不同的数据类型, 用表格为成员赋初值.
#define OFFSET(x) offsetof(CPerson, x)
static const AVOption person_options[]= {
{"name", "set person name", OFFSET(name), AV_OPT_TYPE_STRING, { .str = "张三" }, CHAR_MIN, CHAR_MAX, 1 },
{"telenum", "set telephone num", OFFSET(telenum), AV_OPT_TYPE_INT, { .i64 = 12345678 }, INT_MIN, INT_MAX, 1, NULL},
{"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, { .i64 = 1 }, 0, INT_MAX, 1, "flags" },
};
static const AVClass obj1 = {
.class_name = "CPerson",
.option = person_options,
};
CPerson aPerson;
int main()
{
aPerson.obj = &obj1;
av_opt_set_defaults(&aPerson); // 定义了表格,一个函数调用就完成了所有变量的赋值!,就是为了爽这一下!
printf("name:%s\n",aPerson.name);
printf("telenum:%d\n",aPerson.telenum);
printf("flags:%d\n",aPerson.flags);
return 0;
}
将代码存为main.cpp, 编译通过后(指明头文件路径和连接ffmpeg库), 执行结果如下:
./context
name:张三
telenum:12345678
flags:1