热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ffmpeg中AVOption的实现分析

[时间:2017-10][状态:Open][关键词:ffmpeg,avutil,AVOption]0引言AVO

[时间:2017-10] [状态:Open]
[关键词:ffmpeg,avutil,AVOption]

0 引言

AVOptions提供了一种通用的options机制,可以用于任意特定结构的对象。
本文将先介绍下AVOption的实现,然后整理下如何使用AVOption。(我使用的版本是ffmpeg3.4)

1 AVOption实现细节

这部分可能会很枯燥,多数是ffmpeg源码的粘贴和整理,如果读者对此不感兴趣可以调到下一部分
AVOption的定义如下:

enum AVOptionType{AV_OPT_TYPE_FLAGS,AV_OPT_TYPE_INT,AV_OPT_TYPE_INT64,AV_OPT_TYPE_DOUBLE,AV_OPT_TYPE_FLOAT,AV_OPT_TYPE_STRING,AV_OPT_TYPE_RATIONAL,AV_OPT_TYPE_BINARY, ///};typedef struct AVOption {const char *name;/* short English help text */const char *help;/* option对应的偏移地址和类型 */int offset;enum AVOptionType type;/* 共用体,实际存储数据 */union {int64_t i64;double dbl;const char *str;/* TODO those are unused now */AVRational q;} default_val;double min; ///} AVOption;

初始化函数av_opt_set_defaultsav_opt_set_defaults2

void av_opt_set_defaults(void *s)
{av_opt_set_defaults2(s, 0, 0);
}
// 这里的s是包含AVOptions成员的结构体指针
void av_opt_set_defaults2(void *s, int mask, int flags)
{const AVOption *opt = NULL;// 遍历AVOption中所有数据while ((opt = av_opt_next(s, opt))) {void *dst = ((uint8_t*)s) + opt->offset;if ((opt->flags & mask) != flags)continue;if (opt->flags & AV_OPT_FLAG_READONLY)continue;// 根据类型赋值switch (opt->type) {case AV_OPT_TYPE_CONST:/* Nothing to be done here */break;case AV_OPT_TYPE_BOOL:case AV_OPT_TYPE_FLAGS:case AV_OPT_TYPE_INT:case AV_OPT_TYPE_INT64:case AV_OPT_TYPE_UINT64:case AV_OPT_TYPE_DURATION:case AV_OPT_TYPE_CHANNEL_LAYOUT:case AV_OPT_TYPE_PIXEL_FMT:case AV_OPT_TYPE_SAMPLE_FMT:write_number(s, opt, dst, 1, 1, opt->default_val.i64);break;case AV_OPT_TYPE_DOUBLE:case AV_OPT_TYPE_FLOAT: {double val;val = opt->default_val.dbl;write_number(s, opt, dst, val, 1, 1);}break;case AV_OPT_TYPE_RATIONAL: {AVRational val;val = av_d2q(opt->default_val.dbl, INT_MAX);write_number(s, opt, dst, 1, val.den, val.num);}break;case AV_OPT_TYPE_COLOR:set_string_color(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_STRING:set_string(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_IMAGE_SIZE:set_string_image_size(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_VIDEO_RATE:set_string_video_rate(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_BINARY:set_string_binary(s, opt, opt->default_val.str, dst);break;case AV_OPT_TYPE_DICT:/* Cannot set defaults for these types */break;default:av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n",opt->type, opt->name);}}
}

初始化的基本逻辑是扫描AVOptions数组的所有成员,然后按照类型依次初始化。其中最主要的是av_opt_next函数。其实现如下:

// 该函数通过直接在AVOptions数组上顺序+1实现遍历
const AVOption *av_opt_next(const void *obj, const AVOption *last)
{const AVClass *class;if (!obj)return NULL;class = *(const AVClass**)obj;if (!last && class && class->option && class->option[0].name)return class->option;if (last && last[1].name)return ++last;return NULL;
}

AVOptions数组必须以NULL结束,否则这个函数可能就出现死循环了。
接下来是保存数据的函数,write_number,其实现的代码如下:

static int write_number(void *obj, const AVOption *o, void *dst, double num, int den, int64_t intnum)
{switch (o->type) {case AV_OPT_TYPE_PIXEL_FMT:*(enum AVPixelFormat *)dst &#61; llrint(num / den) * intnum;break;case AV_OPT_TYPE_SAMPLE_FMT:*(enum AVSampleFormat *)dst &#61; llrint(num / den) * intnum;break;case AV_OPT_TYPE_BOOL:case AV_OPT_TYPE_FLAGS:case AV_OPT_TYPE_INT:*(int *)dst &#61; llrint(num / den) * intnum;break;case AV_OPT_TYPE_DURATION:case AV_OPT_TYPE_CHANNEL_LAYOUT:case AV_OPT_TYPE_INT64:{double d &#61; num / den;if (intnum &#61;&#61; 1 && d &#61;&#61; (double)INT64_MAX) {*(int64_t *)dst &#61; INT64_MAX;} else*(int64_t *)dst &#61; llrint(d) * intnum;break;}case AV_OPT_TYPE_UINT64:{double d &#61; num / den;// We must special case uint64_t here as llrint() does not support values// outside the int64_t range and there is no portable function which does// "INT64_MAX &#43; 1ULL" is used as it is representable exactly as IEEE double// while INT64_MAX is notif (intnum &#61;&#61; 1 && d &#61;&#61; (double)UINT64_MAX) {*(uint64_t *)dst &#61; UINT64_MAX;} else if (d > INT64_MAX &#43; 1ULL) {*(uint64_t *)dst &#61; (llrint(d - (INT64_MAX &#43; 1ULL)) &#43; (INT64_MAX &#43; 1ULL))*intnum;} else {*(uint64_t *)dst &#61; llrint(d) * intnum;}break;}case AV_OPT_TYPE_FLOAT:*(float *)dst &#61; num * intnum / den;break;case AV_OPT_TYPE_DOUBLE:*(double *)dst &#61; num * intnum / den;break;case AV_OPT_TYPE_RATIONAL:case AV_OPT_TYPE_VIDEO_RATE:if ((int) num &#61;&#61; num)*(AVRational *)dst &#61; (AVRational) { num *intnum, den };else*(AVRational *)dst &#61; av_d2q(num * intnum / den, 1 <<24);break;default:return AVERROR(EINVAL);}return 0;
}

write_number仅支持数字形式的参数。其他格式需要单独处理。下面是其他格式的保存函数&#xff1a;

// AV_OPT_TYPE_COLOR:
static int set_string_color(void *obj, const AVOption *o, const char *val, uint8_t *dst)
{int ret;if (!val) {return 0;} else {ret &#61; av_parse_color(dst, val, -1, obj);if (ret <0)av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as color\n", val);return ret;}return 0;
}
// AV_OPT_TYPE_STRING:
static int set_string(void *obj, const AVOption *o, const char *val, uint8_t **dst)
{av_freep(dst);*dst &#61; av_strdup(val);return *dst ? 0 : AVERROR(ENOMEM);
}
// AV_OPT_TYPE_IMAGE_SIZE:
static int set_string_image_size(void *obj, const AVOption *o, const char *val, int *dst)
{int ret;if (!val || !strcmp(val, "none")) {dst[0] &#61;dst[1] &#61; 0;return 0;}ret &#61; av_parse_video_size(dst, dst &#43; 1, val);if (ret <0)av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as image size\n", val);return ret;
}
// AV_OPT_TYPE_VIDEO_RATE:
static int set_string_video_rate(void *obj, const AVOption *o, const char *val, AVRational *dst)
{int ret;if (!val) {ret &#61; AVERROR(EINVAL);} else {ret &#61; av_parse_video_rate(dst, val);}if (ret <0)av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as video rate\n", val);return ret;
}
// AV_OPT_TYPE_BINARY:
static int set_string_binary(void *obj, const AVOption *o, const char *val, uint8_t **dst)
{int *lendst &#61; (int *)(dst &#43; 1);uint8_t *bin, *ptr;int len;av_freep(dst);*lendst &#61; 0;if (!val || !(len &#61; strlen(val)))return 0;if (len & 1)return AVERROR(EINVAL);len /&#61; 2;ptr &#61; bin &#61; av_malloc(len);if (!ptr)return AVERROR(ENOMEM);while (*val) {int a &#61; hexchar2int(*val&#43;&#43;);int b &#61; hexchar2int(*val&#43;&#43;);if (a <0 || b <0) {av_free(bin);return AVERROR(EINVAL);}*ptr&#43;&#43; &#61; (a <<4) | b;}*dst &#61; bin;*lendst &#61; len;return 0;
}

当然&#xff0c;AVOptions还有两个构造函数&#xff0c;有兴趣的可以看看ffmpeg中的源代码&#xff0c;其声明如下&#xff1a;

int av_set_options_string(void *ctx, const char *opts,const char *key_val_sep, const char *pairs_sep);
int av_opt_set_from_string(void *ctx, const char *opts,const char *const *shorthand,const char *key_val_sep, const char *pairs_sep);

销毁函数av_opt_free

该函数的实现主要依赖于av_opt_next&#xff0c;遍历所有AVOptions&#xff0c;释放初始化时申请的内存。

void av_opt_free(void *obj)
{const AVOption *o &#61; NULL;while ((o &#61; av_opt_next(obj, o))) {switch (o->type) {case AV_OPT_TYPE_STRING:case AV_OPT_TYPE_BINARY:av_freep((uint8_t *)obj &#43; o->offset);break;case AV_OPT_TYPE_DICT:av_dict_free((AVDictionary **)(((uint8_t *)obj) &#43; o->offset));break;default:break;}}
}

属性查找av_opt_findav_opt_find2

属性查找依赖于遍历的接口函数&#xff0c;其具体实现如下&#xff1a;

const AVOption *av_opt_find(void *obj, const char *name, const char *unit,int opt_flags, int search_flags)
{return av_opt_find2(obj, name, unit, opt_flags, search_flags, NULL);
}const AVOption *av_opt_find2(void *obj, const char *name, const char *unit,int opt_flags, int search_flags, void **target_obj)
{const AVClass *c;const AVOption *o &#61; NULL;if(!obj)return NULL;c&#61; *(AVClass**)obj;if (!c)return NULL;// 判断是否需要检查子对象属性 if (search_flags & AV_OPT_SEARCH_CHILDREN) {if (search_flags & AV_OPT_SEARCH_FAKE_OBJ) { // 假对象const AVClass *child &#61; NULL;while (child &#61; av_opt_child_class_next(c, child))if (o &#61; av_opt_find2(&child, name, unit, opt_flags, search_flags, NULL))return o;} else {void *child &#61; NULL;while (child &#61; av_opt_child_next(obj, child))if (o &#61; av_opt_find2(child, name, unit, opt_flags, search_flags, target_obj))return o;}}// 遍历当前AVClass的所有属性&#xff08;这里的主要区别是遍历函数不同&#xff09;while (o &#61; av_opt_next(obj, o)) {if (!strcmp(o->name, name) && (o->flags & opt_flags) &#61;&#61; opt_flags &&((!unit && o->type !&#61; AV_OPT_TYPE_CONST) ||(unit && o->type &#61;&#61; AV_OPT_TYPE_CONST && o->unit && !strcmp(o->unit, unit)))) {if (target_obj) {if (!(search_flags & AV_OPT_SEARCH_FAKE_OBJ))*target_obj &#61; obj;else*target_obj &#61; NULL;}return o;}}return NULL;
}

AVOptions遍历

av_opt_next在前面的初始化函数中介绍过。这里重点关注后边两个&#xff1a;av_opt_child_next和av_opt_child_class_next。
对应的实现代码如下&#xff1a;

// 该函数直接通过AVClass->child_next函数指针访问子对象
void *av_opt_child_next(void *obj, void *prev)
{const AVClass *c &#61; *(AVClass **)obj;if (c->child_next)return c->child_next(obj, prev);return NULL;
}
// 该函数直接通过AVClass->child_class_next函数指针访问子类
const AVClass *av_opt_child_class_next(const AVClass *parent, const AVClass *prev)
{if (parent->child_class_next)return parent->child_class_next(prev);return NULL;
}

属性设置

属性设置函数比较多&#xff0c;这里依次查看该实现&#xff1a;

int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
{int ret &#61; 0;void *dst, *target_obj;// 根据name查找对应的AVOptionconst AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (!val && (o->type !&#61; AV_OPT_TYPE_STRING &&o->type !&#61; AV_OPT_TYPE_PIXEL_FMT && o->type !&#61; AV_OPT_TYPE_SAMPLE_FMT &&o->type !&#61; AV_OPT_TYPE_IMAGE_SIZE && o->type !&#61; AV_OPT_TYPE_VIDEO_RATE &&o->type !&#61; AV_OPT_TYPE_DURATION && o->type !&#61; AV_OPT_TYPE_COLOR &&o->type !&#61; AV_OPT_TYPE_CHANNEL_LAYOUT && o->type !&#61; AV_OPT_TYPE_BOOL))return AVERROR(EINVAL);if (o->flags & AV_OPT_FLAG_READONLY)return AVERROR(EINVAL);dst &#61; ((uint8_t *)target_obj) &#43; o->offset;switch (o->type) { // 根据AVOPtion的类型设置该参数case AV_OPT_TYPE_BOOL:return set_string_bool(obj, o, val, dst);//这个在初始化函数中有介绍case AV_OPT_TYPE_STRING:return set_string(obj, o, val, dst); //这个在初始化函数中有介绍case AV_OPT_TYPE_BINARY:return set_string_binary(obj, o, val, dst);//这个在初始化函数中有介绍case AV_OPT_TYPE_FLAGS:case AV_OPT_TYPE_INT:case AV_OPT_TYPE_INT64:case AV_OPT_TYPE_UINT64:case AV_OPT_TYPE_FLOAT:case AV_OPT_TYPE_DOUBLE:case AV_OPT_TYPE_RATIONAL:return set_string_number(obj, target_obj, o, val, dst);case AV_OPT_TYPE_IMAGE_SIZE:return set_string_image_size(obj, o, val, dst);case AV_OPT_TYPE_VIDEO_RATE: {AVRational tmp;ret &#61; set_string_video_rate(obj, o, val, &tmp);if (ret <0)return ret;return write_number(obj, o, dst, 1, tmp.den, tmp.num);}case AV_OPT_TYPE_PIXEL_FMT:return set_string_pixel_fmt(obj, o, val, dst);case AV_OPT_TYPE_SAMPLE_FMT:return set_string_sample_fmt(obj, o, val, dst);case AV_OPT_TYPE_DURATION:if (!val) {*(int64_t *)dst &#61; 0;return 0;} else {if ((ret &#61; av_parse_time(dst, val, 1)) <0)av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as duration\n", val);return ret;}break;case AV_OPT_TYPE_COLOR:return set_string_color(obj, o, val, dst);case AV_OPT_TYPE_CHANNEL_LAYOUT:if (!val || !strcmp(val, "none")) {*(int64_t *)dst &#61; 0;} else {int64_t cl &#61; av_get_channel_layout(val);if (!cl) {av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as channel layout\n", val);ret &#61; AVERROR(EINVAL);}*(int64_t *)dst &#61; cl;return ret;}break;}av_log(obj, AV_LOG_ERROR, "Invalid option type.\n");return AVERROR(EINVAL);
}

下面看一下其中具体调用的函数实现细节&#xff08;其中多数函数在初始化一节中一节介绍了&#xff0c;这里不做重复&#xff09;

static int read_number(const AVOption *o, const void *dst, double *num, int *den, int64_t *intnum)
{switch (o->type) {case AV_OPT_TYPE_FLAGS:*intnum &#61; *(unsigned int*)dst;return 0;case AV_OPT_TYPE_PIXEL_FMT:*intnum &#61; *(enum AVPixelFormat *)dst;return 0;case AV_OPT_TYPE_SAMPLE_FMT:*intnum &#61; *(enum AVSampleFormat *)dst;return 0;case AV_OPT_TYPE_BOOL:case AV_OPT_TYPE_INT:*intnum &#61; *(int *)dst;return 0;case AV_OPT_TYPE_CHANNEL_LAYOUT:case AV_OPT_TYPE_DURATION:case AV_OPT_TYPE_INT64:case AV_OPT_TYPE_UINT64:*intnum &#61; *(int64_t *)dst;return 0;case AV_OPT_TYPE_FLOAT:*num &#61; *(float *)dst;return 0;case AV_OPT_TYPE_DOUBLE:*num &#61; *(double *)dst;return 0;case AV_OPT_TYPE_RATIONAL:*intnum &#61; ((AVRational *)dst)->num;*den &#61; ((AVRational *)dst)->den;return 0;case AV_OPT_TYPE_CONST:*num &#61; o->default_val.dbl;return 0;}return AVERROR(EINVAL);
}#define DEFAULT_NUMVAL(opt) ((opt->type &#61;&#61; AV_OPT_TYPE_INT64 || \opt->type &#61;&#61; AV_OPT_TYPE_UINT64 || \opt->type &#61;&#61; AV_OPT_TYPE_CONST || \opt->type &#61;&#61; AV_OPT_TYPE_FLAGS || \opt->type &#61;&#61; AV_OPT_TYPE_INT) \? opt->default_val.i64 \: opt->default_val.dbl)
static int set_string_number(void *obj, void *target_obj, const AVOption *o, const char *val, void *dst)
{int ret &#61; 0;int num, den;char c;if (sscanf(val, "%d%*1[:/]%d%c", &num, &den, &c) &#61;&#61; 2) {if ((ret &#61; write_number(obj, o, dst, 1, den, num)) >&#61; 0)return ret;ret &#61; 0;}// 下面支持表达式的处理逻辑for (;;) {int i &#61; 0;char buf[256];int cmd &#61; 0;double d;int64_t intnum &#61; 1;if (o->type &#61;&#61; AV_OPT_TYPE_FLAGS) {if (*val &#61;&#61; &#39;&#43;&#39; || *val &#61;&#61; &#39;-&#39;)cmd &#61; *(val&#43;&#43;);for (; i unit, 0, 0);int res;int ci &#61; 0;double const_values[64];const char * const_names[64];if (o_named && o_named->type &#61;&#61; AV_OPT_TYPE_CONST)d &#61; DEFAULT_NUMVAL(o_named);else {if (o->unit) {for (o_named &#61; NULL; o_named &#61; av_opt_next(target_obj, o_named); ) {if (o_named->type &#61;&#61; AV_OPT_TYPE_CONST &&o_named->unit &&!strcmp(o_named->unit, o->unit)) {if (ci &#43; 6 >&#61; FF_ARRAY_ELEMS(const_values)) {av_log(obj, AV_LOG_ERROR, "const_values array too small for %s\n", o->unit);return AVERROR_PATCHWELCOME;}const_names [ci ] &#61; o_named->name;const_values[ci&#43;&#43;] &#61; DEFAULT_NUMVAL(o_named);}}}const_names [ci ] &#61; "default";const_values[ci&#43;&#43;] &#61; DEFAULT_NUMVAL(o);const_names [ci ] &#61; "max";const_values[ci&#43;&#43;] &#61; o->max;const_names [ci ] &#61; "min";const_values[ci&#43;&#43;] &#61; o->min;const_names [ci ] &#61; "none";const_values[ci&#43;&#43;] &#61; 0;const_names [ci ] &#61; "all";const_values[ci&#43;&#43;] &#61; ~0;const_names [ci] &#61; NULL;const_values[ci] &#61; 0;res &#61; av_expr_parse_and_eval(&d, i ? buf : val, const_names,const_values, NULL, NULL, NULL, NULL, NULL, 0, obj);if (res <0) {av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\"\n", val);return res;}}}if (o->type &#61;&#61; AV_OPT_TYPE_FLAGS) {read_number(o, dst, NULL, NULL, &intnum);if (cmd &#61;&#61; &#39;&#43;&#39;)d &#61; intnum | (int64_t)d;else if (cmd &#61;&#61; &#39;-&#39;)d &#61; intnum &~(int64_t)d;}if ((ret &#61; write_number(obj, o, dst, d, 1, 1)) <0)return ret;val &#43;&#61; i;if (!i || !*val)return 0;}
}

从上面一部分代码可以看出av_opt_set函数仅仅是根据类型进行数据区域赋值。下面的函数是针对特定类型的赋值&#xff1a;

// int
int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
{return set_number(obj, name, 1, 1, val, search_flags);
}
// double
int av_opt_set_double(void *obj, const char *name, double val, int search_flags)
{return set_number(obj, name, val, 1, 1, search_flags);
}
// AVRational
int av_opt_set_q(void *obj, const char *name, AVRational val, int search_flags)
{return set_number(obj, name, val.num, val.den, 1, search_flags);
}
// binary
int av_opt_set_bin(void *obj, const char *name, const uint8_t *val, int len, int search_flags)
{void *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);uint8_t *ptr;uint8_t **dst;int *lendst;if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; AV_OPT_TYPE_BINARY || o->flags & AV_OPT_FLAG_READONLY)return AVERROR(EINVAL);ptr &#61; len ? av_malloc(len) : NULL;if (len && !ptr)return AVERROR(ENOMEM);dst &#61; (uint8_t **)(((uint8_t *)target_obj) &#43; o->offset);lendst &#61; (int *)(dst &#43; 1);av_free(*dst);*dst &#61; ptr;*lendst &#61; len;if (len)memcpy(ptr, val, len);return 0;
}
// image_size&#xff0c;字符格式"1024x768"
int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags)
{void *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; AV_OPT_TYPE_IMAGE_SIZE) {av_log(obj, AV_LOG_ERROR,"The value set by option &#39;%s&#39; is not an image size.\n", o->name);return AVERROR(EINVAL);}if (w<0 || h<0) {av_log(obj, AV_LOG_ERROR,"Invalid negative size value %dx%d for size &#39;%s&#39;\n", w, h, o->name);return AVERROR(EINVAL);}*(int *)(((uint8_t *)target_obj) &#43; o->offset) &#61; w;*(int *)(((uint8_t *)target_obj&#43;sizeof(int)) &#43; o->offset) &#61; h;return 0;
}
// 视频码率
int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags)
{void *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; AV_OPT_TYPE_VIDEO_RATE) {av_log(obj, AV_LOG_ERROR,"The value set by option &#39;%s&#39; is not a video rate.\n", o->name);return AVERROR(EINVAL);}if (val.num <&#61; 0 || val.den <&#61; 0)return AVERROR(EINVAL);return set_number(obj, name, val.num, val.den, 1, search_flags);
}
// 图像采样格式
int av_opt_set_pixel_fmt(void *obj, const char *name, enum AVPixelFormat fmt, int search_flags)
{return set_format(obj, name, fmt, search_flags, AV_OPT_TYPE_PIXEL_FMT, "pixel", AV_PIX_FMT_NB);
}
// 音频采样格式
int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags)
{return set_format(obj, name, fmt, search_flags, AV_OPT_TYPE_SAMPLE_FMT, "sample", AV_SAMPLE_FMT_NB);
}
// channel_layout
int av_opt_set_channel_layout(void *obj, const char *name, int64_t cl, int search_flags)
{void *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; AV_OPT_TYPE_CHANNEL_LAYOUT) {av_log(obj, AV_LOG_ERROR,"The value set by option &#39;%s&#39; is not a channel layout.\n", o->name);return AVERROR(EINVAL);}*(int64_t *)(((uint8_t *)target_obj) &#43; o->offset) &#61; cl;return 0;
}
// 设置为AVDictionary
int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val,int search_flags)
{void *target_obj;AVDictionary **dst;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->flags & AV_OPT_FLAG_READONLY)return AVERROR(EINVAL);dst &#61; (AVDictionary **)(((uint8_t *)target_obj) &#43; o->offset);av_dict_free(dst);av_dict_copy(dst, val, 0);return 0;
}

属性获取

属性获取和属性设置是对应的&#xff0c;对应函数一般是成对存在&#xff0c;比如av_opt_get/av_opt_set。下面先查看av_opt_get代码&#xff1a;

int av_opt_get(void *obj, const char *name, int search_flags, uint8_t **out_val)
{void *dst, *target_obj;// 通过name查找对应的AVOptionconst AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);uint8_t *bin, buf[128];int len, i, ret;int64_t i64;if (!o || !target_obj || (o->offset<&#61;0 && o->type !&#61; AV_OPT_TYPE_CONST))return AVERROR_OPTION_NOT_FOUND;dst &#61; (uint8_t *)target_obj &#43; o->offset;buf[0] &#61; 0;switch (o->type) { // 根据实际类型返回参数// 对于整型参数&#xff0c;可以直接通过snprintf格式化之case AV_OPT_TYPE_BOOL:ret &#61; snprintf(buf, sizeof(buf), "%s", (char *)av_x_if_null(get_bool_name(*(int *)dst), "invalid"));break;case AV_OPT_TYPE_FLAGS:ret &#61; snprintf(buf, sizeof(buf), "0x%08X", *(int *)dst);break;case AV_OPT_TYPE_INT:ret &#61; snprintf(buf, sizeof(buf), "%d", *(int *)dst);break;case AV_OPT_TYPE_INT64:ret &#61; snprintf(buf, sizeof(buf), "%"PRId64, *(int64_t *)dst);break;case AV_OPT_TYPE_UINT64:ret &#61; snprintf(buf, sizeof(buf), "%"PRIu64, *(uint64_t *)dst);break;case AV_OPT_TYPE_FLOAT:ret &#61; snprintf(buf, sizeof(buf), "%f", *(float *)dst);break;case AV_OPT_TYPE_DOUBLE:ret &#61; snprintf(buf, sizeof(buf), "%f", *(double *)dst);break;case AV_OPT_TYPE_VIDEO_RATE:case AV_OPT_TYPE_RATIONAL:ret &#61; snprintf(buf, sizeof(buf), "%d/%d", ((AVRational *)dst)->num, ((AVRational *)dst)->den);break;case AV_OPT_TYPE_CONST:ret &#61; snprintf(buf, sizeof(buf), "%f", o->default_val.dbl);break;case AV_OPT_TYPE_STRING: // 字符串可以直接复制if (*(uint8_t **)dst) {*out_val &#61; av_strdup(*(uint8_t **)dst);} else if (search_flags & AV_OPT_ALLOW_NULL) {*out_val &#61; NULL;return 0;} else {*out_val &#61; av_strdup("");}return *out_val ? 0 : AVERROR(ENOMEM);case AV_OPT_TYPE_BINARY: // 二进制文件需要特殊处理if (!*(uint8_t **)dst && (search_flags & AV_OPT_ALLOW_NULL)) {*out_val &#61; NULL;return 0;}len &#61; *(int *)(((uint8_t *)dst) &#43; sizeof(uint8_t *));if ((uint64_t)len * 2 &#43; 1 > INT_MAX)return AVERROR(EINVAL);if (!(*out_val &#61; av_malloc(len * 2 &#43; 1)))return AVERROR(ENOMEM);if (!len) {*out_val[0] &#61; &#39;\0&#39;;return 0;}bin &#61; *(uint8_t **)dst;for (i &#61; 0; i &#61; sizeof(buf))return AVERROR(EINVAL);*out_val &#61; av_strdup(buf);return *out_val ? 0 : AVERROR(ENOMEM);
}

上面只有一个函数没有实现format_duration&#xff0c;其他代码如下&#xff1a;

static void format_duration(char *buf, size_t size, int64_t d)
{char *e;// 主要是为了时长的特殊的格式化输出av_assert0(size >&#61; 25);if (d <0 && d !&#61; INT64_MIN) {*(buf&#43;&#43;) &#61; &#39;-&#39;;size--;d &#61; -d;}if (d &#61;&#61; INT64_MAX)snprintf(buf, size, "INT64_MAX");else if (d &#61;&#61; INT64_MIN)snprintf(buf, size, "INT64_MIN");else if (d > (int64_t)3600*1000000)snprintf(buf, size, "%"PRId64":%02d:%02d.%06d", d / 3600000000,(int)((d / 60000000) % 60),(int)((d / 1000000) % 60),(int)(d % 1000000));else if (d > 60*1000000)snprintf(buf, size, "%d:%02d.%06d",(int)(d / 60000000),(int)((d / 1000000) % 60),(int)(d % 1000000));elsesnprintf(buf, size, "%d.%06d",(int)(d / 1000000),(int)(d % 1000000));e &#61; buf &#43; strlen(buf);while (e > buf && e[-1] &#61;&#61; &#39;0&#39;)*(--e) &#61; 0;if (e > buf && e[-1] &#61;&#61; &#39;.&#39;)*(--e) &#61; 0;
}

接下来是对应的不同类型的AVOption的获取&#xff0c;其基本逻辑是对av_opt_get的拆分&#xff0c;代码如下&#xff1a;

// 查找数字格式
static int get_number(void *obj, const char *name, const AVOption **o_out, double *num, int *den, int64_t *intnum,int search_flags)
{void *dst, *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)goto error;dst &#61; ((uint8_t *)target_obj) &#43; o->offset;if (o_out) *o_out&#61; o;return read_number(o, dst, num, den, intnum);error:*den &#61;*intnum &#61; 0;return -1;
}int av_opt_get_int(void *obj, const char *name, int search_flags, int64_t *out_val)
{int64_t intnum &#61; 1;double num &#61; 1;int ret, den &#61; 1;if ((ret &#61; get_number(obj, name, NULL, &num, &den, &intnum, search_flags)) <0)return ret;*out_val &#61; num * intnum / den;return 0;
}int av_opt_get_double(void *obj, const char *name, int search_flags, double *out_val)
{int64_t intnum &#61; 1;double num &#61; 1;int ret, den &#61; 1;if ((ret &#61; get_number(obj, name, NULL, &num, &den, &intnum, search_flags)) <0)return ret;*out_val &#61; num * intnum / den;return 0;
}int av_opt_get_q(void *obj, const char *name, int search_flags, AVRational *out_val)
{int64_t intnum &#61; 1;double num &#61; 1;int ret, den &#61; 1;if ((ret &#61; get_number(obj, name, NULL, &num, &den, &intnum, search_flags)) <0)return ret;if (num &#61;&#61; 1.0 && (int)intnum &#61;&#61; intnum)*out_val &#61; (AVRational){intnum, den};else*out_val &#61; av_d2q(num*intnum/den, 1<<24);return 0;
}int av_opt_get_image_size(void *obj, const char *name, int search_flags, int *w_out, int *h_out)
{void *dst, *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; AV_OPT_TYPE_IMAGE_SIZE) {av_log(obj, AV_LOG_ERROR,"The value for option &#39;%s&#39; is not an image size.\n", name);return AVERROR(EINVAL);}dst &#61; ((uint8_t*)target_obj) &#43; o->offset;if (w_out) *w_out &#61; *(int *)dst;if (h_out) *h_out &#61; *((int *)dst&#43;1);return 0;
}int av_opt_get_video_rate(void *obj, const char *name, int search_flags, AVRational *out_val)
{int64_t intnum &#61; 1;double num &#61; 1;int ret, den &#61; 1;if ((ret &#61; get_number(obj, name, NULL, &num, &den, &intnum, search_flags)) <0)return ret;if (num &#61;&#61; 1.0 && (int)intnum &#61;&#61; intnum)*out_val &#61; (AVRational) { intnum, den };else*out_val &#61; av_d2q(num * intnum / den, 1 <<24);return 0;
}static int get_format(void *obj, const char *name, int search_flags, int *out_fmt,enum AVOptionType type, const char *desc)
{void *dst, *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; type) {av_log(obj, AV_LOG_ERROR,"The value for option &#39;%s&#39; is not a %s format.\n", desc, name);return AVERROR(EINVAL);}dst &#61; ((uint8_t*)target_obj) &#43; o->offset;*out_fmt &#61; *(int *)dst;return 0;
}int av_opt_get_pixel_fmt(void *obj, const char *name, int search_flags, enum AVPixelFormat *out_fmt)
{return get_format(obj, name, search_flags, out_fmt, AV_OPT_TYPE_PIXEL_FMT, "pixel");
}int av_opt_get_sample_fmt(void *obj, const char *name, int search_flags, enum AVSampleFormat *out_fmt)
{return get_format(obj, name, search_flags, out_fmt, AV_OPT_TYPE_SAMPLE_FMT, "sample");
}int av_opt_get_channel_layout(void *obj, const char *name, int search_flags, int64_t *cl)
{void *dst, *target_obj;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; AV_OPT_TYPE_CHANNEL_LAYOUT) {av_log(obj, AV_LOG_ERROR,"The value for option &#39;%s&#39; is not a channel layout.\n", name);return AVERROR(EINVAL);}dst &#61; ((uint8_t*)target_obj) &#43; o->offset;*cl &#61; *(int64_t *)dst;return 0;
}int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val)
{void *target_obj;AVDictionary *src;const AVOption *o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;if (o->type !&#61; AV_OPT_TYPE_DICT)return AVERROR(EINVAL);src &#61; *(AVDictionary **)(((uint8_t *)target_obj) &#43; o->offset);av_dict_copy(out_val, src, 0);return 0;
}

这几个函数逻辑个对应的av_opt_set_xxx类似&#xff0c;从代码可以直接看出来。

// 该函数返回AVOption的地址
void *av_opt_ptr(const AVClass *class, void *obj, const char *name)
{const AVOption *opt&#61; av_opt_find2(&class, name, NULL, 0, AV_OPT_SEARCH_FAKE_OBJ, NULL);if(!opt)return NULL;return (uint8_t*)obj &#43; opt->offset;
}

杂项

此类函数可能就是部分工具函数。我们逐个分析下&#xff1a;

int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name)
{// 依次查找各部分&#xff0c;然后确定是否已经设置该flagconst AVOption *field &#61; av_opt_find(obj, field_name, NULL, 0, 0);const AVOption *flag &#61; av_opt_find(obj, flag_name,field ? field->unit : NULL, 0, 0);int64_t res;if (!field || !flag || flag->type !&#61; AV_OPT_TYPE_CONST ||av_opt_get_int(obj, field_name, 0, &res) <0)return 0;return res & flag->default_val.i64;
}

下面几个函数是使用宏统一定义的&#xff1a;

int av_opt_eval_flags (void *obj, const AVOption *o, const char *val, int *flags_out);
int av_opt_eval_int (void *obj, const AVOption *o, const char *val, int *int_out);
int av_opt_eval_int64 (void *obj, const AVOption *o, const char *val, int64_t *int64_out);
int av_opt_eval_float (void *obj, const AVOption *o, const char *val, float *float_out);
int av_opt_eval_double(void *obj, const AVOption *o, const char *val, double *double_out);
int av_opt_eval_q (void *obj, const AVOption *o, const char *val, AVRational *q_out);#define OPT_EVAL_NUMBER(name, opttype, vartype) \
int av_opt_eval_ ## name(void *obj, const AVOption *o, \const char *val, vartype *name ## _out) \
{ \if (!o || o->type !&#61; opttype || o->flags & AV_OPT_FLAG_READONLY) \return AVERROR(EINVAL); \return set_string_number(obj, obj, o, val, name ## _out); \
}OPT_EVAL_NUMBER(flags, AV_OPT_TYPE_FLAGS, int)
OPT_EVAL_NUMBER(int, AV_OPT_TYPE_INT, int)
OPT_EVAL_NUMBER(int64, AV_OPT_TYPE_INT64, int64_t)
OPT_EVAL_NUMBER(float, AV_OPT_TYPE_FLOAT, float)
OPT_EVAL_NUMBER(double, AV_OPT_TYPE_DOUBLE, double)
OPT_EVAL_NUMBER(q, AV_OPT_TYPE_RATIONAL, AVRational)
// 最后直接调用set_string_number函数&#xff0c;这个函数在上文中有提及

av_opt_copy函数完成AVOption的复制工作。下面看看代码&#xff0c;原则上应该跟初始化函数类似。

int av_opt_copy(void *dst, const void *src)
{const AVOption *o &#61; NULL;const AVClass *c;int ret &#61; 0;if (!src)return AVERROR(EINVAL);c &#61; *(AVClass **)src;if (!c || c !&#61; *(AVClass **)dst)return AVERROR(EINVAL);// 遍历&#43;按照类型复制while ((o &#61; av_opt_next(src, o))) {void *field_dst &#61; (uint8_t *)dst &#43; o->offset;void *field_src &#61; (uint8_t *)src &#43; o->offset;uint8_t **field_dst8 &#61; (uint8_t **)field_dst;uint8_t **field_src8 &#61; (uint8_t **)field_src;if (o->type &#61;&#61; AV_OPT_TYPE_STRING) {if (*field_dst8 !&#61; *field_src8)av_freep(field_dst8);*field_dst8 &#61; av_strdup(*field_src8);if (*field_src8 && !*field_dst8)ret &#61; AVERROR(ENOMEM);} else if (o->type &#61;&#61; AV_OPT_TYPE_BINARY) {int len &#61; *(int *)(field_src8 &#43; 1);if (*field_dst8 !&#61; *field_src8)av_freep(field_dst8);*field_dst8 &#61; av_memdup(*field_src8, len);if (len && !*field_dst8) {ret &#61; AVERROR(ENOMEM);len &#61; 0;}*(int *)(field_dst8 &#43; 1) &#61; len;} else if (o->type &#61;&#61; AV_OPT_TYPE_CONST) {// do nothing} else if (o->type &#61;&#61; AV_OPT_TYPE_DICT) {AVDictionary **sdict &#61; (AVDictionary **) field_src;AVDictionary **ddict &#61; (AVDictionary **) field_dst;if (*sdict !&#61; *ddict)av_dict_free(ddict);*ddict &#61; NULL;av_dict_copy(ddict, *sdict, 0);if (av_dict_count(*sdict) !&#61; av_dict_count(*ddict))ret &#61; AVERROR(ENOMEM);} else {int size &#61; opt_size(o->type); // 返回特定类型的长度if (size <0)ret &#61; size;elsememcpy(field_dst, field_src, size);}}return ret;
}
static int opt_size(enum AVOptionType type)
{switch(type) {case AV_OPT_TYPE_BOOL:case AV_OPT_TYPE_INT:case AV_OPT_TYPE_FLAGS:return sizeof(int);case AV_OPT_TYPE_DURATION:case AV_OPT_TYPE_CHANNEL_LAYOUT:case AV_OPT_TYPE_INT64:case AV_OPT_TYPE_UINT64:return sizeof(int64_t);case AV_OPT_TYPE_DOUBLE:return sizeof(double);case AV_OPT_TYPE_FLOAT:return sizeof(float);case AV_OPT_TYPE_STRING:return sizeof(uint8_t*);case AV_OPT_TYPE_VIDEO_RATE:case AV_OPT_TYPE_RATIONAL:return sizeof(AVRational);case AV_OPT_TYPE_BINARY:return sizeof(uint8_t*) &#43; sizeof(int);case AV_OPT_TYPE_IMAGE_SIZE:return sizeof(int[2]);case AV_OPT_TYPE_PIXEL_FMT:return sizeof(enum AVPixelFormat);case AV_OPT_TYPE_SAMPLE_FMT:return sizeof(enum AVSampleFormat);case AV_OPT_TYPE_COLOR:return 4;}return AVERROR(EINVAL);
}

最后三个值是判断是否是默认值、以及序列化输出。

int av_opt_is_set_to_default(void *obj, const AVOption *o)
{int64_t i64;double d, d2;float f;AVRational q;int ret, w, h;char *str;void *dst;if (!o || !obj)return AVERROR(EINVAL);dst &#61; ((uint8_t*)obj) &#43; o->offset;// 基本思路是根据类型&#xff0c;判断当前属性值和默认值是否一致。switch (o->type) {case AV_OPT_TYPE_CONST:return 1;case AV_OPT_TYPE_BOOL:case AV_OPT_TYPE_FLAGS:case AV_OPT_TYPE_PIXEL_FMT:case AV_OPT_TYPE_SAMPLE_FMT:case AV_OPT_TYPE_INT:case AV_OPT_TYPE_CHANNEL_LAYOUT:case AV_OPT_TYPE_DURATION:case AV_OPT_TYPE_INT64:case AV_OPT_TYPE_UINT64:read_number(o, dst, NULL, NULL, &i64); // 数值类型直接读取return o->default_val.i64 &#61;&#61; i64;// 判断默认值是否一致case AV_OPT_TYPE_STRING:str &#61; *(char **)dst;if (str &#61;&#61; o->default_val.str) //2 NULLsreturn 1;if (!str || !o->default_val.str) //1 NULLreturn 0;return !strcmp(str, o->default_val.str);case AV_OPT_TYPE_DOUBLE:read_number(o, dst, &d, NULL, NULL);return o->default_val.dbl &#61;&#61; d;case AV_OPT_TYPE_FLOAT:read_number(o, dst, &d, NULL, NULL);f &#61; o->default_val.dbl;d2 &#61; f;return d2 &#61;&#61; d;case AV_OPT_TYPE_RATIONAL:q &#61; av_d2q(o->default_val.dbl, INT_MAX);return !av_cmp_q(*(AVRational*)dst, q);case AV_OPT_TYPE_BINARY: {struct {uint8_t *data;int size;} tmp &#61; {0};int opt_size &#61; *(int *)((void **)dst &#43; 1);void *opt_ptr &#61; *(void **)dst;if (!opt_size && (!o->default_val.str || !strlen(o->default_val.str)))return 1;if (!opt_size || !o->default_val.str || !strlen(o->default_val.str ))return 0;if (opt_size !&#61; strlen(o->default_val.str) / 2)return 0;ret &#61; set_string_binary(NULL, NULL, o->default_val.str, &tmp.data);if (!ret)ret &#61; !memcmp(opt_ptr, tmp.data, tmp.size);av_free(tmp.data);return ret;}case AV_OPT_TYPE_DICT:/* Binary and dict have not default support yet. Any pointer is not default. */return !!(*(void **)dst);case AV_OPT_TYPE_IMAGE_SIZE:if (!o->default_val.str || !strcmp(o->default_val.str, "none"))w &#61; h &#61; 0;else if ((ret &#61; av_parse_video_size(&w, &h, o->default_val.str)) <0)return ret;return (w &#61;&#61; *(int *)dst) && (h &#61;&#61; *((int *)dst&#43;1));case AV_OPT_TYPE_VIDEO_RATE:q &#61; (AVRational){0, 0};if (o->default_val.str) {if ((ret &#61; av_parse_video_rate(&q, o->default_val.str)) <0)return ret;}return !av_cmp_q(*(AVRational*)dst, q);case AV_OPT_TYPE_COLOR: {uint8_t color[4] &#61; {0, 0, 0, 0};if (o->default_val.str) {if ((ret &#61; av_parse_color(color, o->default_val.str, -1, NULL)) <0)return ret;}return !memcmp(color, dst, sizeof(color));}default:av_log(obj, AV_LOG_WARNING, "Not supported option type: %d, option name: %s\n", o->type, o->name);break;}return AVERROR_PATCHWELCOME;
}int av_opt_is_set_to_default_by_name(void *obj, const char *name, int search_flags)
{const AVOption *o;void *target;if (!obj)return AVERROR(EINVAL);// 先查找&#xff0c;然后对比是否是默认值o &#61; av_opt_find2(obj, name, NULL, 0, search_flags, &target);if (!o)return AVERROR_OPTION_NOT_FOUND;return av_opt_is_set_to_default(target, o);
}int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer,const char key_val_sep, const char pairs_sep)
{const AVOption *o &#61; NULL;uint8_t *buf;AVBPrint bprint;int ret, cnt &#61; 0;const char special_chars[] &#61; {pairs_sep, key_val_sep, &#39;\0&#39;};if (pairs_sep &#61;&#61; &#39;\0&#39; || key_val_sep &#61;&#61; &#39;\0&#39; || pairs_sep &#61;&#61; key_val_sep ||pairs_sep &#61;&#61; &#39;\\&#39; || key_val_sep &#61;&#61; &#39;\\&#39;) {av_log(obj, AV_LOG_ERROR, "Invalid separator(s) found.");return AVERROR(EINVAL);}if (!obj || !buffer)return AVERROR(EINVAL);*buffer &#61; NULL;av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);// 序列化的基本思路是遍历 while (o &#61; av_opt_next(obj, o)) {if (o->type &#61;&#61; AV_OPT_TYPE_CONST)continue;if ((flags & AV_OPT_SERIALIZE_OPT_FLAGS_EXACT) && o->flags !&#61; opt_flags)continue;else if (((o->flags & opt_flags) !&#61; opt_flags))continue;if (flags & AV_OPT_SERIALIZE_SKIP_DEFAULTS && av_opt_is_set_to_default(obj, o) > 0)continue;if ((ret &#61; av_opt_get(obj, o->name, 0, &buf)) <0) {av_bprint_finalize(&bprint, NULL);return ret;}if (buf) {if (cnt&#43;&#43;)av_bprint_append_data(&bprint, &pairs_sep, 1);av_bprint_escape(&bprint, o->name, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);av_bprint_append_data(&bprint, &key_val_sep, 1);av_bprint_escape(&bprint, buf, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);av_freep(&buf);}}av_bprint_finalize(&bprint, buffer);return 0;
}

2 AVOption的用法

只要是支持AVOption的对象都可以使用上面提供的接口。支持AVOption的结构体或对象的第一个数据域必须是一个指向AVClass对象的指针&#xff0c;其中该AVClass的对象的option域必须设置为一个以NULL结束的AVOption数组。

通常可以使用av_opt_next()编译一个对象的全部选项&#xff0c;也可以使用av_opt_find()通过指定选项名称查找特定选项。
也可以通过av_opt_child_class_next()av_opt_child_next()两种不同的遍历子对象属性的方式。

对于读取和设置AVOption可以通过av_opt_set()av_opt_get&#xff0c;也可以通过更详细的区分的av_opt_set_xxx和av_opt_get_xxx。

3 如何实现支持AVOption的对象

按照之前的AVOption的接口要求&#xff0c;下面给出一个简单的支持AVOption属性的结构体&#xff0c;首先给出一个示例结构体的定义&#xff0c;代码如下&#xff1a;

struct zyj_object {const AVClass *class;int int_opt;char *str_opt;uint8_t *bin_opt;int bin_len;struct child_object * child;
};
static const AVOption zyj_object_options[] &#61; {{ "example_int", "This is a test option of int type.", offsetof(zyj_object, int_opt),AV_OPT_TYPE_INT, { .i64 &#61; -1 }, INT_MIN, INT_MAX },{ "example_str", "This is a test option of string type.", offsetof(zyj_object, str_opt),AV_OPT_TYPE_STRING },{ "example_bin", "This is a test option of binary type.", offsetof(zyj_object, bin_opt),AV_OPT_TYPE_BINARY },{ NULL },
};
static const AVClass zyj_object_class &#61; {.class_name &#61; "zyj class",.item_name &#61; av_default_item_name,.option &#61; zyj_object_options,.version &#61; LIBAVUTIL_VERSION_INT,
};

这样一个结构体可以使用av_opt_set_defaults()将AVOption所有成员初始化为默认值。下面我们看一下如何创建和销毁zyj_object&#xff0c;代码如下&#xff1a;

zyj_object *alloc_zyj_object(void)
{zyj_object *ret &#61; av_mallocz(sizeof(*ret));ret->class &#61; &zyj_object_class;av_opt_set_defaults(ret);return ret;
}
void free_zyj_object(zyj_object **foo)
{av_opt_free(*foo);av_freep(foo);
}

特例一&#xff1a;嵌套AVOption实现

像AVCodecContext一样&#xff0c;其中包含了libavcodec导出的通用选项&#xff0c;同时它的priv_data成员包含了codec相关的选项。在这种情况下&#xff0c;需要设置父结构体来导出子选项。也就是实现在父结构体的AVClass中实现AVClass.child_next()和AVClass.child_class_next()。这里给出一个简单的嵌套示例&#xff1a;

typedef struct child_object {AVClass *class;int flags_opt;
} child_struct;
static const AVOption child_opts[] &#61; {{ "test_flags", "This is a test option of flags type.",offsetof(child_struct, flags_opt), AV_OPT_TYPE_FLAGS, { .i64 &#61; 0 }, INT_MIN, INT_MAX },{ NULL },
};
static const AVClass child_class &#61; {.class_name &#61; "child class",.item_name &#61; av_default_item_name,.option &#61; child_opts,.version &#61; LIBAVUTIL_VERSION_INT,
};
void *child_next(void *obj, void *prev)
{zyj_object *t &#61; obj;if (!prev && t->child_struct)return t->child_struct;return NULL
}
const AVClass child_class_next(const AVClass *prev)
{return prev ? NULL : &child_class;
}// 这个是更新
static const AVClass zyj_object_class &#61; {.class_name &#61; "zyj class",.item_name &#61; av_default_item_name,.option &#61; zyj_object_options,.child_next &#61; child_next,.child_class_next &#61; child_class_next,.version &#61; LIBAVUTIL_VERSION_INT,
};

命名常量

AVOption支持命名常量。使用AV_OPT_TYPE_CONST可以声明常量&#xff0c;但是必须给定默认的初始值。示例代码如下&#xff1a;

{ "flag1", "This is a flag with value 16", 0, AV_OPT_TYPE_CONST, { .i64 &#61; 16 }, 0, 0, "test_unit" },

4 小结

本文可能涉及比较多的源代码&#xff0c;是对libavutil/opt.c的整理及摘取&#xff0c;通过梳理相关实现代码&#xff0c;让我基本理解AVOption的实现逻辑&#xff0c;值得后续更深入的使用作为参考。

转:https://www.cnblogs.com/tocy/p/ffmpeg-libavutil-avoption.html



推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
author-avatar
想零的时候
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有