何为AVOption?AVClass? 及其关联的若干个Context

何为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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值