全球观察:ffmpeg源码分析:结构体成员管理系统-AVOption
AVOption用于在FFmpeg中描述结构体中的成员变量。一个AVOption可以包含名称,简短的帮助信息,取值等等。 下面开始从代码的角度记录AVOption。AVOption结构体的定义如下所示。
libavutil\Opt.h/** * 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;#define AV_OPT_FLAG_ENCODING_PARAM 1 ///< a generic parameter which can be set by the user for muxing or encoding#define AV_OPT_FLAG_DECODING_PARAM 2 ///< a generic parameter which can be set by the user for demuxing or decoding#define AV_OPT_FLAG_AUDIO_PARAM 8#define AV_OPT_FLAG_VIDEO_PARAM 16#define AV_OPT_FLAG_SUBTITLE_PARAM 32/** * The option is intended for exporting values to the caller. */#define AV_OPT_FLAG_EXPORT 64/** * The option may not be set through the AVOptions API, only read. * This flag only makes sense when AV_OPT_FLAG_EXPORT is also set. */#define AV_OPT_FLAG_READONLY 128#define AV_OPT_FLAG_BSF_PARAM (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering#define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering#define AV_OPT_FLAG_DEPRECATED (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information//FIXME think about enc-audio, ... style 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;
(资料图片仅供参考)
下面简单解释一下AVOption的几个成员变量: @ name:名称。 @ help:简短的帮助。 @ offset:选项相对结构体首部地址的偏移量(这个很重要)。 @ type:选项的类型。 @ default_val:选项的默认值。 @ min:选项的最小值。 @ max:选项的最大值。 @ flags:一些标记。 @ unit:该选项所属的逻辑单元,可以为空。 其中,default_val是一个union类型的变量,可以根据选项数据类型的不同,取int,double,char*,AVRational(表示分数)几种类型。type是一个AVOptionType类型的变量。AVOptionType是一个枚举类型,定义如下。
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, ///< offset must point to a pointer immediately followed by an int for the length AV_OPT_TYPE_DICT, AV_OPT_TYPE_UINT64, AV_OPT_TYPE_CONST, AV_OPT_TYPE_IMAGE_SIZE, ///< offset must point to two consecutive integers AV_OPT_TYPE_PIXEL_FMT, AV_OPT_TYPE_SAMPLE_FMT, AV_OPT_TYPE_VIDEO_RATE, ///< offset must point to AVRational AV_OPT_TYPE_DURATION, AV_OPT_TYPE_COLOR, AV_OPT_TYPE_CHANNEL_LAYOUT, AV_OPT_TYPE_BOOL,};
AVOption常用的API可以分成两类:用于设置参数的API和用于读取参数的API。其中最有代表性的用于设置参数的API就是av_opt_set();而最有代表性的用于读取参数的API就是av_opt_get()。除了记录以上两个函数之外,本文再记录一个在FFmpeg的结构体初始化代码中最常用的用于设置默认值的函数av_opt_set_defaults()。
一、av_opt_set()
通过AVOption设置参数最常用的函数就是av_opt_set()了。该函数通过字符串的方式(传入的参数是变量名称的字符串和变量值的字符串)设置一个AVOption的值。此外,还包含了它的一系列“兄弟”函数av_opt_set_XXX(),其中“XXX”代表了int,double这些数据类型。使用这些函数的时候,可以指定int,double这些类型的变量(而不是字符串)作为输入,设定相应的AVOption的值。
/** * @defgroup opt_set_funcs Option setting functions * @{ * Those functions set the field of obj with the given name to value. * * @param[in] obj A struct whose first element is a pointer to an AVClass. * @param[in] name the name of the field to set * @param[in] val The value to set. In case of av_opt_set() if the field is not * of a string type, then the given string is parsed. * SI postfixes and some named scalars are supported. * If the field is of a numeric type, it has to be a numeric or named * scalar. Behavior with more than one scalar and +- infix operators * is undefined. * If the field is of a flags type, it has to be a sequence of numeric * scalars or named flags separated by "+" or "-". Prefixing a flag * with "+" causes it to be set without affecting the other flags; * similarly, "-" unsets a flag. * @param search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN * is passed here, then the option may be set on a child of obj. * * @return 0 if the value has been set, or an AVERROR code in case of * error: * AVERROR_OPTION_NOT_FOUND if no matching option exists * AVERROR(ERANGE) if the value is out of range * AVERROR(EINVAL) if the value is not valid */int av_opt_set (void *obj, const char *name, const char *val, int search_flags);int av_opt_set_int (void *obj, const char *name, int64_t val, int search_flags);int av_opt_set_double (void *obj, const char *name, double val, int search_flags);int av_opt_set_q (void *obj, const char *name, AVRational val, int search_flags);int av_opt_set_bin (void *obj, const char *name, const uint8_t *val, int size, int search_flags);int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags);int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, int search_flags);int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags);int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags);int av_opt_set_channel_layout(void *obj, const char *name, int64_t ch_layout, int search_flags);
有关av_opt_set_XXX()函数的定义不再详细分析,在这里详细看一下av_opt_set()的源代码。av_opt_set()的定义位于libavutil\opt.c,如下所示。
int av_opt_set(void *obj, const char *name, const char *val, int search_flags){ int ret = 0; void *dst, *target_obj; //查找 const AVOption *o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj); if (!o || !target_obj) return AVERROR_OPTION_NOT_FOUND; if (!val && (o->type != AV_OPT_TYPE_STRING && o->type != AV_OPT_TYPE_PIXEL_FMT && o->type != AV_OPT_TYPE_SAMPLE_FMT && o->type != AV_OPT_TYPE_IMAGE_SIZE && o->type != AV_OPT_TYPE_VIDEO_RATE && o->type != AV_OPT_TYPE_DURATION && o->type != AV_OPT_TYPE_COLOR && o->type != AV_OPT_TYPE_CHANNEL_LAYOUT && o->type != AV_OPT_TYPE_BOOL)) return AVERROR(EINVAL); if (o->flags & AV_OPT_FLAG_READONLY) return AVERROR(EINVAL); if (o->flags & AV_OPT_FLAG_DEPRECATED) av_log(obj, AV_LOG_WARNING, "The \"%s\" option is deprecated: %s\n", name, o->help); //dst指向具体的变量 //注意:offset的作用 dst = ((uint8_t *)target_obj) + o->offset; //根据AVOption不同的类型,调用不同的设置函数 switch (o->type) { 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 = 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: { int64_t usecs = 0; if (val) { if ((ret = av_parse_time(&usecs, val, 1)) < 0) { av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as duration\n", val); return ret; } } if (usecs < o->min || usecs > o->max) { av_log(obj, AV_LOG_ERROR, "Value %f for parameter "%s" out of range [%g - %g]\n", usecs / 1000000.0, o->name, o->min / 1000000.0, o->max / 1000000.0); return AVERROR(ERANGE); } *(int64_t *)dst = usecs; return 0; } 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 = 0; } else { int64_t cl = 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 = AVERROR(EINVAL); } *(int64_t *)dst = cl; return ret; } break; } av_log(obj, AV_LOG_ERROR, "Invalid option type.\n"); return AVERROR(EINVAL);}
从源代码可以看出,av_opt_set()首先调用av_opt_find2()查找AVOption。如果找到了,则根据AVOption的type,调用不同的函数(set_string(),set_string_number(),set_string_image_size()等等)将输入的字符串转化为相应type的数据并对该AVOption进行赋值。如果没有找到,则立即返回“没有找到AVOption”的错误。
1.1 av_opt_find2() / av_opt_find()
av_opt_find2()本身也是一个API函数,用于查找AVOption。它的声明位于libavutil\opt.h中,如下所示。
/** * Look for an option in an object. Consider only options which * have all the specified flags set. * * @param[in] obj A pointer to a struct whose first element is a * pointer to an AVClass. * Alternatively a double pointer to an AVClass, if * AV_OPT_SEARCH_FAKE_OBJ search flag is set. * @param[in] name The name of the option to look for. * @param[in] unit When searching for named constants, name of the unit * it belongs to. * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG). * @param search_flags A combination of AV_OPT_SEARCH_*. * * @return A pointer to the option found, or NULL if no option * was found. * * @note Options found with AV_OPT_SEARCH_CHILDREN flag may not be settable * directly with av_opt_set(). Use special calls which take an options * AVDictionary (e.g. avformat_open_input()) to set options found with this * flag. */const AVOption *av_opt_find(void *obj, const char *name, const char *unit, int opt_flags, int search_flags);/** * Look for an option in an object. Consider only options which * have all the specified flags set. * * @param[in] obj A pointer to a struct whose first element is a * pointer to an AVClass. * Alternatively a double pointer to an AVClass, if * AV_OPT_SEARCH_FAKE_OBJ search flag is set. * @param[in] name The name of the option to look for. * @param[in] unit When searching for named constants, name of the unit * it belongs to. * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG). * @param search_flags A combination of AV_OPT_SEARCH_*. * @param[out] target_obj if non-NULL, an object to which the option belongs will be * written here. It may be different from obj if AV_OPT_SEARCH_CHILDREN is present * in search_flags. This parameter is ignored if search_flags contain * AV_OPT_SEARCH_FAKE_OBJ. * * @return A pointer to the option found, or NULL if no option * was found. */const AVOption *av_opt_find2(void *obj, const char *name, const char *unit, int opt_flags, int search_flags, void **target_obj);
此外还有一个和av_opt_find2()“长得很像”的API函数av_opt_find(),功能与av_opt_find2()基本类似,与av_opt_find2()相比少了最后一个参数。从源代码中可以看出它只是简单调用了av_opt_find2()并把所有的输入参数原封不动的传递过去,并把最后一个参数设置成NULL。
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);}
下面先看一下av_opt_find2()函数的定义。该函数的定义位于libavutil\opt.c中,如下所示。
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 = NULL; if(!obj) return NULL; c= *(AVClass**)obj; if (!c) return NULL; //查找范围包含子节点的时候 //递归调用av_opt_find2() if (search_flags & AV_OPT_SEARCH_CHILDREN) { if (search_flags & AV_OPT_SEARCH_FAKE_OBJ) { const AVClass *child = NULL; while (child = av_opt_child_class_next(c, child)) if (o = av_opt_find2(&child, name, unit, opt_flags, search_flags, NULL)) return o; } else { void *child = NULL; while (child = av_opt_child_next(obj, child)) if (o = av_opt_find2(child, name, unit, opt_flags, search_flags, target_obj)) return o; } } //遍历 while (o = av_opt_next(obj, o)) { //比较名称 if (!strcmp(o->name, name) && (o->flags & opt_flags) == opt_flags && ((!unit && o->type != AV_OPT_TYPE_CONST) || (unit && o->type == AV_OPT_TYPE_CONST && o->unit && !strcmp(o->unit, unit)))) { if (target_obj) { if (!(search_flags & AV_OPT_SEARCH_FAKE_OBJ)) *target_obj = obj; else *target_obj = NULL; } return o; } } return NULL;}
这段代码的前半部分暂时不关注,前半部分的if()语句中的内容只有在search_flags指定为AV_OPT_SEARCH_CHILDREN的时候才会执行。后半部分代码是重点。后半部分代码是一个while()循环,该循环的条件是一个函数av_opt_next()。
1.2 av_opt_next()
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;}
从av_opt_next()的代码可以看出,输入的AVOption类型的last变量为空的时候,会返回该AVClass的option数组的第一个元素,否则会返回数组的下一个元素。 现在再回到av_opt_find2()函数。我们发现在while()循环中有一个strcmp()函数,正是这个函数比较输入的AVOption的name和AVClass的option数组中每个元素的name,当上述两个name相等的时候,就代表查找到了AVOption,接着就可以返回获得的AVOption。 现在再回到刚才的av_opt_set()函数。该函数有一个void型的变量dst用于确定需要设定的AVOption对应的变量的位置。具体的方法就是将输入的AVClass结构体的首地址加上该AVOption的偏移量offset。确定了AVOption对应的变量的位置之后,就可以根据该AVOption的类型type的不同调用不同的字符串转换函数设置相应的值了。
二、av_opt_get()
av_opt_get()用于获取一个AVOption变量的值。需要注意的是,不论是何种类型的变量,通过av_opt_get()取出来的值都是字符串类型的。此外,还包含了它的一系列“兄弟”函数av_opt_get_XXX()(其中“XXX”代表了int,double这些数据类型)。通过这些“兄弟”函数可以直接取出int,double类型的数值。av_opt_get()的声明如下所示。
/** * @defgroup opt_get_funcs Option getting functions * @{ * Those functions get a value of the option with the given name from an object. * * @param[in] obj a struct whose first element is a pointer to an AVClass. * @param[in] name name of the option to get. * @param[in] search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN * is passed here, then the option may be found in a child of obj. * @param[out] out_val value of the option will be written here * @return >=0 on success, a negative error code otherwise *//** * @note the returned string will be av_malloc()ed and must be av_free()ed by the caller */int av_opt_get (void *obj, const char *name, int search_flags, uint8_t **out_val);int av_opt_get_int (void *obj, const char *name, int search_flags, int64_t *out_val);int av_opt_get_double (void *obj, const char *name, int search_flags, double *out_val);int av_opt_get_q (void *obj, const char *name, int search_flags, AVRational *out_val);int av_opt_get_image_size(void *obj, const char *name, int search_flags, int *w_out, int *h_out);int av_opt_get_pixel_fmt (void *obj, const char *name, int search_flags, enum AVPixelFormat *out_fmt);int av_opt_get_sample_fmt(void *obj, const char *name, int search_flags, enum AVSampleFormat *out_fmt);int av_opt_get_video_rate(void *obj, const char *name, int search_flags, AVRational *out_val);int av_opt_get_channel_layout(void *obj, const char *name, int search_flags, int64_t *ch_layout);
下面我们看一下av_opt_get()的定义,如下所示。
int av_opt_get(void *obj, const char *name, int search_flags, uint8_t **out_val){ void *dst, *target_obj; const AVOption *o = 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<=0 o-="">type != AV_OPT_TYPE_CONST)) return AVERROR_OPTION_NOT_FOUND; if (o->flags & AV_OPT_FLAG_DEPRECATED) av_log(obj, AV_LOG_WARNING, "The \"%s\" option is deprecated: %s\n", name, o->help); dst = (uint8_t *)target_obj + o->offset; //使用sprintf()转换成字符串,存入buf buf[0] = 0; switch (o->type) { case AV_OPT_TYPE_BOOL: ret = snprintf(buf, sizeof(buf), "%s", (char *)av_x_if_null(get_bool_name(*(int *)dst), "invalid")); break; case AV_OPT_TYPE_FLAGS: ret = snprintf(buf, sizeof(buf), "0x%08X", *(int *)dst); break; case AV_OPT_TYPE_INT: ret = snprintf(buf, sizeof(buf), "%d", *(int *)dst); break; case AV_OPT_TYPE_INT64: ret = snprintf(buf, sizeof(buf), "%"PRId64, *(int64_t *)dst); break; case AV_OPT_TYPE_UINT64: ret = snprintf(buf, sizeof(buf), "%"PRIu64, *(uint64_t *)dst); break; case AV_OPT_TYPE_FLOAT: ret = snprintf(buf, sizeof(buf), "%f", *(float *)dst); break; case AV_OPT_TYPE_DOUBLE: ret = snprintf(buf, sizeof(buf), "%f", *(double *)dst); break; case AV_OPT_TYPE_VIDEO_RATE: case AV_OPT_TYPE_RATIONAL: ret = snprintf(buf, sizeof(buf), "%d/%d", ((AVRational *)dst)->num, ((AVRational *)dst)->den); break; case AV_OPT_TYPE_CONST: ret = snprintf(buf, sizeof(buf), "%f", o->default_val.dbl); break; case AV_OPT_TYPE_STRING: if (*(uint8_t **)dst) { *out_val = av_strdup(*(uint8_t **)dst); } else if (search_flags & AV_OPT_ALLOW_NULL) { *out_val = NULL; return 0; } else { *out_val = 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 = NULL; return 0; } len = *(int *)(((uint8_t *)dst) + sizeof(uint8_t *)); if ((uint64_t)len * 2 + 1 > INT_MAX) return AVERROR(EINVAL); if (!(*out_val = av_malloc(len * 2 + 1))) return AVERROR(ENOMEM); if (!len) { *out_val[0] = "\0"; return 0; } bin = *(uint8_t **)dst; for (i = 0; i < len; i++) snprintf(*out_val + i * 2, 3, "%02X", bin[i]); return 0; case AV_OPT_TYPE_IMAGE_SIZE: ret = snprintf(buf, sizeof(buf), "%dx%d", ((int *)dst)[0], ((int *)dst)[1]); break; case AV_OPT_TYPE_PIXEL_FMT: ret = snprintf(buf, sizeof(buf), "%s", (char *)av_x_if_null(av_get_pix_fmt_name(*(enum AVPixelFormat *)dst), "none")); break; case AV_OPT_TYPE_SAMPLE_FMT: ret = snprintf(buf, sizeof(buf), "%s", (char *)av_x_if_null(av_get_sample_fmt_name(*(enum AVSampleFormat *)dst), "none")); break; case AV_OPT_TYPE_DURATION: i64 = *(int64_t *)dst; format_duration(buf, sizeof(buf), i64); ret = strlen(buf); // no overflow possible, checked by an assert break; case AV_OPT_TYPE_COLOR: ret = snprintf(buf, sizeof(buf), "0x%02x%02x%02x%02x", (int)((uint8_t *)dst)[0], (int)((uint8_t *)dst)[1], (int)((uint8_t *)dst)[2], (int)((uint8_t *)dst)[3]); break; case AV_OPT_TYPE_CHANNEL_LAYOUT: i64 = *(int64_t *)dst; ret = snprintf(buf, sizeof(buf), "0x%"PRIx64, i64); break; default: return AVERROR(EINVAL); } if (ret >= sizeof(buf)) return AVERROR(EINVAL); *out_val = av_strdup(buf); return *out_val ? 0 : AVERROR(ENOMEM);}
从av_opt_get()的定义可以看出,该函数首先通过av_opt_find2()查相应的AVOption,然后取出该变量的值,最后通过snprintf()将变量的值转化为字符串(各种各样类型的变量都这样处理)并且输出出来。
三、av_opt_set_defaults()
av_opt_set_defaults()是一个FFmpeg的API,作用是给一个结构体的成员变量设定默认值。在FFmpeg初始化其各种结构体(AVFormatContext,AVCodecContext等)的时候,通常会调用该函数设置结构体中的默认值。av_opt_set_defaults()的声明如下所示。
/** * Set the values of all AVOption fields to their default values. * * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass) */void av_opt_set_defaults(void *s);
可见只需要把包含AVOption功能的结构体(第一个变量是一个AVClass类型的指针)的指针提供给av_opt_set_defaults(),就可以初始化该结构体的默认值了。 下面看一下av_opt_set_defaults()的源代码,位于libavutil\opt.c,如下所示。
void av_opt_set_defaults(void *s){//奇怪的#if...#endif#if FF_API_OLD_AVOPTIONS av_opt_set_defaults2(s, 0, 0);} void av_opt_set_defaults2(void *s, int mask, int flags){#endif const AVOption *opt = NULL; //遍历所有的AVOption while ((opt = av_opt_next(s, opt))) { //注意:offset的使用 void *dst = ((uint8_t*)s) + opt->offset;#if FF_API_OLD_AVOPTIONS if ((opt->flags & mask) != flags) continue;#endif if (opt->flags & AV_OPT_FLAG_READONLY) continue; //读取各种default_val switch (opt->type) { case AV_OPT_TYPE_CONST: /* Nothing to be done here */ break; case AV_OPT_TYPE_FLAGS: case AV_OPT_TYPE_INT: case AV_OPT_TYPE_INT64: case AV_OPT_TYPE_DURATION: case AV_OPT_TYPE_CHANNEL_LAYOUT: 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_PIXEL_FMT: write_number(s, opt, dst, 1, 1, opt->default_val.i64); break; case AV_OPT_TYPE_SAMPLE_FMT: write_number(s, opt, dst, 1, 1, opt->default_val.i64); 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); } }}
av_opt_set_defaults()代码开始的时候有一个预编译指令还是挺奇怪的。怪就怪在#if和#endif竟然横跨在了两个函数之间。简单解读一下这个定义的意思:当定义了FF_API_OLD_AVOPTIONS的时候,存在两个函数av_opt_set_defaults()和av_opt_set_defaults2(),而这两个函数的作用是一样的;当没有定义FF_API_OLD_AVOPTIONS的时候,只存在一个函数av_opt_set_defaults()。估计FFmpeg这么做主要是考虑到兼容性的问题。 av_opt_set_defaults()主体部分是一个while()循环。该循环的判断条件是一个av_opt_next(),其作用是获得下一个AVOption。该函数的定义在前文中已经做过分析。
参考文献: https://blog.csdn.net/leixiaohua1020/article/details/44279329
标签:
相关推荐:
最新新闻:
- 利用活跃变量分析来去掉vmp的大部分垃圾指令 活跃性计算的方法
- 乱码问题怎么解决?Ubuntu9.04上看电影加载中文字幕乱码问题-速递
- 百度云资源分享 百度云干货资源
- 焦点播报:Windows下【AxureRP】原型设计工具破解码与安装包 安装步骤
- 耶鲁大学耗时5年的研究成果 左脑与右脑的神奇功能研究|今日报
- JavaEE---Servlet入门教程 JavaEE操作步骤_天天即时看
- 天天时讯:FASTQ格式是什么?FASTQ格式详情
- 环球新消息丨TSLAM9是什么?中心差分卡尔曼滤波
- 全球观察:ffmpeg源码分析:结构体成员管理系统-AVOption
- 紫光电子平板电脑怎么样?紫光电子平板电脑如何刷机? 每日热门
- 天天信息:win7系统如何关闭系统默认共享文件夹?关闭系统默认共享文件夹方法
- 如何清除AcadDoc.lsp病毒?AcadDoc.lsp病毒清理步骤
- 文本显示器的价格是多少?文本显示器的优势|全球滚动
- linux安装jdk8怎么装?手把手教你安装单机版Hadoop3.2.1
- 饮水机什么牌子质量好?饮水机品牌推荐-全球观察
- 网络基础知识有哪些?网络基础知识大全 世界热闻
- 小学生电脑学习机有哪些?读书郎学生电脑主要功能
- 【时快讯】韩国泛泰手机怎么样?韩国泛泰手机参数配置如何?
- 环球快资讯丨为什么黑茶有茶梗?关于茶梗你知道多少?
- 松下变频器怎么使用?松下变频器说明书详解-全球微动态
- 二手电视机有哪些分类?二手电视机分类介绍-热消息
- 如何判断一个函数是奇函数还是偶函数?判断技巧|当前速看
- 【当前热闻】一加手机怎么开启手电筒?一加手机开启手电筒操作步骤
- 2021庆阳一中高考成绩查询 2020年庆阳市多所中学高考喜报
- Win7安装IE10或IE11怎么操作?离线安装注意问题 全球热点评
- 分布式光纤测温系统 性能指标优势
- 2021江西省高考的成绩怎么查询?江西省教育考试院高考成绩查询系统入口2021
- Oracle database 10g官方版性能拓展_世界观焦点
- 飞利浦吸尘器怎么样?维修中常见的问题 世界快播报
- 删除文件提示正在被另一程序使用怎么办?解决方法
- QGIS|构建选址模型 模型需求分析:天天消息
- 【世界时快讯】什么是umd漫画制? umd漫画制作工具详情介绍
- 视频在html不能播放器怎么办?网页播放器打不开的解决方法
- 德国坦克声卡怎么样?德国坦克声卡质量好不好?|每日快讯
- CAD怎么建立三维模型? CAD的建模方法|全球播资讯
- d3dx9_43.dll是什么丢失了怎么办?解决办法 天天快讯
- 联想轻薄笔记本怎么样?联想ThinkPad E325多少钱?:环球热资讯
- 2021年临颖一高高考成绩查询 河南漯河名列前茅的4所高中 热头条
- 小飞人熨斗怎样?小飞人熨斗特点介绍 讯息
- Module简介 module的编写方法
- 今日热议:RCLAMP0524P超低电容TVS二极管阵列 DFN-10L封装教程
- sin函数对照表怎么看?三角函数值对照表
- QT部署YOLOV5 pyqt5搭建YOLOV5的检测平台
- 微信公众号的消息免打扰怎么打开?微信公众号的消息免打扰打开方法 环球今亮点
- 微速讯:Makefile宏控是什么?宏控与systemProperty取名对应
- 什么是封建社会?封建社会详情介绍
- 【天天时快讯】华为云发布鲲鹏云服务 开启云上多元算力新赛道
- 【世界速看料】一个没有四肢的人 却给了无数人的力量
- Android应用Preference相关及源码浅析 Preference相关基础概念
- 色度抽样怎么弄?抽樣作用的解釋
- 如何关闭电脑右下角游戏广告弹窗?电脑右下角游戏广告弹窗关闭方法
- 怎么批量删除电脑桌面文件图标?批量删除电脑桌面文件图标操作步骤
- diskgenius怎么恢复数据和分区?diskgeniu恢复数据和分区方法
- win7如何安装HP打印机?win7安装HP打印机步骤
- 如何清除win10系统所有搜索历史记录?win10系统所有搜索历史记录操作步骤
- 无线路由器可连接网络的距离是多少?无线路由器可连接网络的距离介绍
- 如何找到电脑中的本地连接?电脑中的本地连接查找方法
- ctfmon.exe是什么进程?ctfmon.exe是否可以下载替换?
- 怎么将swf格式文件转为mp3格式?swf格式文件转为mp3格式方法
- 打开EXCEL时无法找到startup.xls文件怎么解决?打开EXCEL时无法找到startup.xls文件的解决方法
- 音频拨号和脉冲拨号的区别是什么?音频拨号和脉冲拨号的区别介绍
- 怎么更改鼠标指针图案?鼠标指针图案更改方法
- 笔记本电脑键盘输入错乱怎么办?笔记本电脑键盘输入错乱解决方法
- TeamViewer怎么使用?TeamViewer使用方法教程
- 电脑主板供电接口有什么不同?电脑主板供电接口介绍
- 硬盘如何安装系统?硬盘安装系统教程
- 笔记本电脑怎么外接直流电源?笔记本电脑外接直流电源连接方法
- 怎么打开nh文件?nh文件是什么?
- 暴风转码怎么用?暴风转码使用方法
- 蓝屏提示错误代码0x000000c2怎么办?Win10蓝屏提示错误代码0x000000c2的解决方法
- “强监管、一刀切”!香港期货业恐“变天”,经纪商:可操作性不强......影响几何? 环球热推荐
- 韩厂压力山大:显示屏看中国的时代来了 当前通讯
- 《星球大战绝地:幸存者》将拥有5种光剑形态 用以针对不同敌人
- 最新快讯!《战神4》Mod搞笑视频 姆巴佩和梅西一决高下
- 菲尔·斯宾塞分享自己的2022年游戏报告 《吸血鬼幸存者》游玩时长最多
- 观焦点:P社宣布 将计划对部分地区的游戏售价进行调整
- 世界看热讯:Acer推出新技术 显示器和笔记本可实现裸眼立体3D效果
- 《刺客信条:英灵殿》联动《怪物猎人:世界》 惨爪龙风飘龙装备皮肤正式上线_最新消息
- 处理器/系统百分百中国自主 国产PC新标杆 内嵌8核处理器
- 联想小新预热 2023 年新品:将有 14、16、Air 14、Pro 14、Pro 16 五款新机-观察
- 谨慎升级!苹果停止签署iOS 16.1.2
- 货币如何“类财政”? 天天关注
- 布洛芬供应提速有限:从400吨原料药到4亿片药片,至少要40天|焦点热讯
- 微软发现macOS漏洞 可绕过安全审查植入恶意软件 环球速递
- 天天新资讯:《海贼王:时光旅诗》13分钟实机演示 女角身材很棒