所有和AVOption有关的数据都存储在AVClass结构体中。如果一个结构体(例如AVFormatContext或者AVCodecContext)想要支持AVOption的话,它的第一个成员变量必须是一个指向AVClass结构体的指针。该AVClass中的成员变量option必须指向一个AVOption类型的静态数组。
何为AVOption?
AVOption是用来设置FFmpeg中变量值的结构体。特点就在于它赋值的灵活性。AVOption可以使用字符串为任何类型的变量赋值。统一使用字符串赋值。例如给int型变量qp设定值为20,通过AVOption需要传递进去一个内容为“20”的字符串。
此外,AVOption中变量的名称也使用字符串来表示。传递两个字符串(一个是变量的名称,一个是变量的值)就可以改变系统中变量的值。
对于从外部系统中调用FFmpeg的人来说,作用就很大了:从外部系统中只可以传递字符串给内部系统。比如说对于直接调用ffmpeg.exe的人来说,他们是无法修改FFmpeg内部各个变量的数值的,这种情况下只能通过输入“名称”和“值”这样的字符串,通过AVOption改变FFmpeg内部变量的值。由此可见,使用AVOption可以使FFmpeg更加适应多种多样的外部系统。如
互联网上只可以传输字符串。其实除了可以对FFmpeg常用结构体AVFormatContext,AVCodecContext等进行赋值之外,还可以对它们的私有数据priv_data进行赋值。例如使用libx264进行编码的时候,通过AVCodecContext的priv_data字段可以对X264Context结构体中的变量进行赋值,设置preset,profile等。使用libx265进行编码的时候,通过AVCodecContext的priv_data字段可以对libx265Context结构体中的变量进行赋值,设置preset,tune等。
何为AVClass?
AVClass最主要的作用就是给结构体(例如AVFormatContext等)增加AVOption功能的支持。AVClass就是AVOption和目标结构体之间的“桥梁”。AVClass要求必须声明为目标结构体的第一个变量。
AVClass中有一个option数组用于存储目标结构体的所有的AVOption。举个例子,AVFormatContext结构体,AVClass和AVOption之间的关系如下图所示。
![]()
图中AVFormatContext结构体的第一个变量为AVClass类型的指针av_class,它在AVFormatContext结构体初始化的时候,被赋值指向了全局静态变量av_format_context_class结构体(定义位于libavformat\options.c)。而AVClass类型的av_format_context_class结构体中的option变量指向了全局静态数组avformat_options(定义位于libavformat\options_table.h)。
static const AVClass av_format_context_class = {
.class_name = "AVFormatContext",
.item_name = format_to_name,
.option = avformat_options,
.version = LIBAVUTIL_VERSION_INT,
.child_next = format_child_next,
.child_class_next = format_child_class_next,
.category = AV_CLASS_CATEGORY_MUXER,
.get_category = get_category,
};
static const AVOption avformat_options[] = {
{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"},
{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"},
{"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT64, {.i64 = 5000000 }, 32, INT64_MAX, D},
AVOption
- /**
- * AVOption
- */
- typedef struct AVOption {
- const char *name; 名称。
-
- /**
- * short English help text
- * @todo What about other languages?
- */
- const char *help; 简短的帮助。
-
- /**
- * The offset relative to the context structure where the option
- * value is stored. It should be 0 for named constants.
- */
- int offset; 选项相对结构体首部地址的偏移量(这个很重要)。
- enum AVOptionType type; 选项的类型。
-
- /**
- * the default value for scalar options
- */
- union {
- int64_t i64;
- double dbl;
- const char *str;
- /* TODO those are unused now */
- AVRational q;
- } default_val; 选项的默认值。
- double min; ///< minimum valid value for the option 选项的最小值。
- double max; ///< maximum valid value for the option 选项的最大值。
-
- int flags; 一些标记。
-
- /**
- * The logical unit to which the option belongs. Non-constant
- * options and corresponding named constants share the same
- * unit. May be NULL.
- */
- const char *unit; 该选项所属的逻辑单元,可以为空。
- } AVOption;
其中,default_val是一个union类型的变量,可以根据选项数据类型的不同,取int,double,char*,AVRational(表示分数)几种类型。
AVClass
AVClass中存储了AVOption类型的数组option,用于存储选项信息。AVClass有一个特点就是它必须位于其支持的结构体的第一个位置。
- /**
- * Describe the class of an AVClass context structure. That is an
- * arbitrary struct of which the first field is a pointer to an
- * AVClass struct (e.g. AVCodecContext, AVFormatContext etc.).
- */
- typedef struct AVClass {
- /**
- * The name of the class; usually it is the same name as the
- * context structure type to which the AVClass is associated.
- */
- const char* class_name; AVClass名称。
-
- /**
- * A pointer to a function which returns the name of a context
- * instance ctx associated with the class.
- */
- const char* (*item_name)(void* ctx);函数,获取与AVClass相关联的结构体实例的名称。
-
- /**
- * a pointer to the first option specified in the class if any or NULL
- *
- * @see av_set_default_options()
- */
- const struct AVOption *option; AVOption类型的数组(最重要)。
-
- /**
- * LIBAVUTIL_VERSION with which this structure was created.
- * This is used to allow fields to be added without requiring major
- * version bumps everywhere.
- */
-
- int version; 完成该AVClass的时候的LIBAVUTIL_VERSION。
-
- /**
- * Offset in the structure where log_level_offset is stored.
- * 0 means there is no such variable
- */
- int log_level_offset_offset;
-
- /**
- * Offset in the structure where a pointer to the parent context for
- * logging is stored. For example a decoder could pass its AVCodecContext
- * to eval as such a parent context, which an av_log() implementation
- * could then leverage to display the parent context.
- * The offset can be NULL.
- */
- int parent_log_context_offset;
-
- /**
- * Return next AVOptions-enabled child or NULL
- */
- void* (*child_next)(void *obj, void *prev);
-
- /**
- * Return an AVClass corresponding to the next potential
- * AVOptions-enabled child.
- *
- * The difference between child_next and this is that
- * child_next iterates over _already existing_ objects, while
- * child_class_next iterates over _all possible_ children.
- */
- const struct AVClass* (*child_class_next)(const struct AVClass *prev);
-
- /**
- * Category used for visualization (like color)
- * This is only set if the category is equal for all objects using this class.
- * available since version (51 << 16 | 56 << 8 | 100)
- */
- AVClassCategory category; AVClass的类型,是一个类型为AVClassCategory的枚举型变量。
-
- /**
- * Callback to return the category.
- * available since version (51 << 16 | 59 << 8 | 100)
- */
- AVClassCategory (*get_category)(void* ctx);
-
- /**
- * Callback to return the supported/allowed ranges.
- * available since version (52.12)
- */
- int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
- } AVClass;
下面通过具体的例子看一下AVClass这个结构体。我们看几个具体的例子:
- AVFormatContext中的AVClass
- AVCodecContext中的AVClass
- AVFrame中的AVClass
- 各种组件(libRTMP,libx264,libx265)里面特有的AVClass。
AVFormatContext
AVFormatContext 中的AVClass定义位于libavformat\options.c中,是一个名称为av_format_context_class的静态结构体。如下所示。
- static const AVClass av_format_context_class = {
- .class_name = "AVFormatContext",
- .item_name = format_to_name,
- .option = avformat_options,
- .version = LIBAVUTIL_VERSION_INT,
- .child_next = format_child_next,
- .child_class_next = format_child_class_next,
- .category = AV_CLASS_CATEGORY_MUXER,
- .get_category = get_category,
- };
从源代码可以看出以下几点
(1)class_name: 该AVClass名称是“AVFormatContext”。
(2)item_nameitem_name指向一个函数format_to_name(),该函数定义如下所示。
- static const char* format_to_name(void* ptr)
- {
- AVFormatContext* fc = (AVFormatContext*) ptr;
- if(fc->iformat) return fc->iformat->name;
- else if(fc->oformat) return fc->oformat->name;
- else return "NULL";
- }
从函数的定义可以看出,如果AVFormatContext结构体中的AVInputFormat结构体不为空,则返回AVInputFormat的name,然后尝试返回AVOutputFormat的name,如果AVOutputFormat也为空,则返回“NULL”。
(3)optionoption字段则指向一个元素个数很多的静态数组avformat_options。该数组单独定义于libavformat\options_table.h中。其中包含了AVFormatContext支持的所有的AVOption
AVCodecContext
位于libavcodec\options.c中,是一个名称为av_codec_context_class的静态结构体。如下所示。
- static const AVClass av_codec_context_class = {
- .class_name = "AVCodecContext",
- .item_name = context_to_name,
- .option = avcodec_options,
- .version = LIBAVUTIL_VERSION_INT,
- .log_level_offset_offset = offsetof(AVCodecContext, log_level_offset),
- .child_next = codec_child_next,
- .child_class_next = codec_child_class_next,
- .category = AV_CLASS_CATEGORY_ENCODER,
- .get_category = get_category,
- };
(1)class_name:该AVClass名称是“AVCodecContext”。
(2)item_nameitem_name指向一个函数context_to_name (),该函数定义如下所示。
- static const char* context_to_name(void* ptr) {
- AVCodecContext *avc= ptr;
-
- if(avc && avc->codec && avc->codec->name)
- return avc->codec->name;
- else
- return "NULL";
- }
从函数的定义可以看出,如果AVCodecContext中的Codec结构体不为空,则返回Codec(AVCodec类型的)的name,否则返回“NULL”。
(3)optionoption字段则指向一个元素个数极多的静态数组avcodec_options。该数组单独定义于libavcodec\options_table.h中。其中包含了AVCodecContext支持的所有的AVOption
AVFrame
位于libavcodec\options.c中,是一个名称为av_frame_class的静态结构体。如下所示。
- static const AVClass av_frame_class = {
- .class_name = "AVFrame",
- .item_name = NULL,
- .option = frame_options,
- .version = LIBAVUTIL_VERSION_INT,
- };
option字段则指向静态数组frame_options。frame_options定义如下所示。
- static const AVOption frame_options[]={
- {"best_effort_timestamp", "", FOFFSET(best_effort_timestamp), AV_OPT_TYPE_INT64, {.i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, 0},
- {"pkt_pos", "", FOFFSET(pkt_pos), AV_OPT_TYPE_INT64, {.i64 = -1 }, INT64_MIN, INT64_MAX, 0},
- {"pkt_size", "", FOFFSET(pkt_size), AV_OPT_TYPE_INT64, {.i64 = -1 }, INT64_MIN, INT64_MAX, 0},
- {"sample_aspect_ratio", "", FOFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0 }, 0, INT_MAX, 0},
- {"width", "", FOFFSET(width), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0},
- {"height", "", FOFFSET(height), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0},
- {"format", "", FOFFSET(format), AV_OPT_TYPE_INT, {.i64 = -1 }, 0, INT_MAX, 0},
- {"channel_layout", "", FOFFSET(channel_layout), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, 0},
- {"sample_rate", "", FOFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0},
- {NULL},
- };
可以看出AVFrame的选项数组中包含了“width”,“height”这类用于视频帧的选项,以及“channel_layout”,“sample_rate”这类用于音频帧的选项。
各种组件特有的AVClass
除了FFmpeg中通用的AVFormatContext,AVCodecContext,AVFrame这类的结构体之外,每种特定的组件也包含自己的AVClass。
LibRTMP
libRTMP中根据协议类型的不同定义了多种的AVClass。由于这些AVClass除了名字不一样之外,其他的字段一模一样,所以AVClass的声明写成了一个名为RTMP_CLASS的宏。