每日信息: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
标签:
相关推荐:
精彩放送:
- []乱码问题怎么解决?Ubuntu9.04上看电影加载中文字幕乱码问题
- []当前速读:吓唬的意思是什么?吓唬怎么造句?
- []稀奇古怪乐小米是什么?关于稀奇古怪乐小米的介绍
- []FASTQ格式是什么?FASTQ格式详情
- []每日信息:ffmpeg源码分析:结构体成员管理系统-AVOption
- []北京民俗文化有哪些?关于北京民俗文化的介绍
- []天天报道:400-500分上的医科大学名单黑龙江 2023年参考医科大学录取名单
- []娱乐爆料:王丽坤、李冰冰、周杰伦、刘昊然、朱一龙
- []木格措海拔有多高?关于木格措海拔的介绍
- []最新资讯:JavaEE---Servlet入门教程 JavaEE操作步骤
- []环球热点!香茅是什么味道?香茅是什么植物?
- []焦点!版载千秋第3个隐藏任务怎么过?版载千秋答题器
- []世界时讯:苹果手机越狱后怎样恢复?苹果手机恢复回越狱前的方法
- []今日热门!使命召唤10下载地址是什么?使命召唤11中文版下载迅雷
- []最新:TSLAM9是什么?中心差分卡尔曼滤波
- []人均gdp是什么意思?人均gdp的含义
- []今日聚焦!大连市房产局官网怎么查询?大连市房产局官网
- []环球快看:搬砖是什么意思?dnf搬砖的含义
- []【环球速看料】ape音乐免费下载 ape音乐怎么下载?
- []外汇交易提醒:日元在震荡交投中回落,美元兑日元小幅反弹
- []当前播报:纹的组词有哪些?纹的组词有什么?
- []每日报道:DTA决赛放送:Z世代、元宇宙……酒旅业创新趋势都在这里 |
- []原油交易提醒:库存大降助力油价飙升超3%,警惕地缘局势不确定性风险上升
- []侨银股份预中标广州荔湾和莱州两个服务项目 总成交金额约1.79亿
- []天天播报:嘉兴嘉善2宗涉宅地6.16亿成交 三达房地产封顶价获其一
- []湖州德清一宗商住地将于明年1月11日出让 起始价3.48亿元
- []每日报道:2022年市场规模将超6000亿元 我国智能家居产业发展打开新空间
- []12月22日重点数据和大事件前瞻
- []每日热文:已有25家房企发布配股或定增 雅居乐第2次公告配售后股价跌超17%
- []湖南电力“满格” 水利显威
- []天天播报:年产首次突破50万吨 我国海上稠油热采实现“三级跳”
- []简讯:我国高端电力装备研发获重大成果
- []梅耶博格:开启29.6%效率的串联钙钛矿太阳能电池研发
- []焦点播报:EIA原油库存降幅超预期,美油短线拉升0.5美元
- []世界视点!5亿元!爱旭股份设立全资子公司投建6.5GW太阳能电池项目
- []每日速递:奥海科技:12月20日公司高管吴日诚的亲属减持公司股份合计300股
- []社保平均缴费指数怎么计算 怎么计算社保平均缴费指数
- []汉港控股3.4亿元向绿城服务租赁杭州余杭物业商业空间 租期十年
- []天天热头条丨一年扣一次大额什么意思 什么是一年扣一次大额
- []车险需要买哪些 购买一般车险要买什么险
- []世界观热点:皇庭国际第一大股东8698.59万股被司法拍卖 占总股本7.41%
- []世界即时看!金融街拟出售房山金悦嘉苑共有产权房项目,交易价22.6亿
- []天天亮点!央行:引导金融机构支持地产行业重组并购 防范化解头部房企风险
- []今日视点:央行:加大稳健货币政策实施力度 加大对民营小微企业支持力度
- []今日热议:嘉凯城聘任符谙、余薇为公司证券事务代表
- []速看:房地产开发板块跌0.75% 粤泰股份涨10.18%居首
- []当前讯息:康龙化成:12月20日公司高管楼小强减持公司股份合计4.47万股
- []每日观察!二手房周报 | 15城二手成交环比下降10%,北京规模已较高点腰斩(12.12-12.18)
- []世界滚动:宁波色母:12月20日公司高管祖万年减持公司股份合计5.25万股
- []环球观察:同有科技:12月20日公司高管杨建利减持公司股份合计7.01万股
- []环球快看点丨北京宸宇将向北辰实业与北京金隅归集资金共计2.5亿元
- []今亮点!郭施亮:为何房地产概念股走势频繁变脸?
- []环球看热讯:沈晓玲:房企年底为冲业绩,推“无理由退房”等靠谱吗?
- []明珠货运(YGMZ.US)以1500万美元收购飞鹏物流100%股权
- []每日动态!龙软科技:12月20日公司高管任永智、侯立、雷小平减持公司股份合计1.6万股
- []中科软:12月21日公司高管孙熙杰减持公司股份合计71.39万股
- []雄帝科技:公司暂时没有涉及该领域,对于新技术、新业态的发展公司会持续保持关注
- []昭衍新药:12月20日公司高管顾晓磊减持公司股份合计57.3万股
- []环球热文:宝新金融:立信德豪辞任 委任国富浩华为新核数师
- []【天天报资讯】第一服务:上海鼎晖耀家就保证回报权订立补充协议
- []新资讯:挂牌价24万/平重现,一线城市首现二手房参考价“隐退”
- []天天关注:中航机电:截至2022年12月20日,公司股东总户数为146,079户
- []当前视讯!江苏南通8宗涉宅地块46亿元成交
- []中公高科:12月20日公司高管李强减持公司股份合计3.86万股
- []昊华能源索赔案仍在征集中 涉诉金额合计已逾亿元
- []世界热点评!删除文件提示正在被另一程序使用怎么办?解决方法
- []速递!飞利浦吸尘器怎么样?维修中常见的问题
- []世界今日讯!天健集团成功发行11亿元超短期融资券 利率3.05%
- []环球关注:文本显示器的价格是多少?文本显示器的优势
- []全球快看点丨快速申请QQ靓号 教你用腾讯QQ极品靓号申请
- []环球微头条丨松下变频器怎么使用?松下变频器说明书详解
- []计算机网络虚拟局域网步骤 如何构建虚拟局域网?
- []全球看点:电脑蓝屏是什么原因?电脑蓝屏的解决方法
- []右脑记忆法的个人理解 王峰、袁文魁等记忆大师的通用方法
- []小学生电脑学习机有哪些?读书郎学生电脑主要功能
- []天天快看:什么是umd漫画制? umd漫画制作工具详情介绍
- []韩国泛泰手机怎么样?韩国泛泰手机参数配置如何?
- []联想轻薄笔记本怎么样?联想ThinkPad E325多少钱?
- []环球快讯:德国坦克声卡怎么样?德国坦克声卡质量好不好?
- []全球看热讯:CAD怎么建立三维模型? CAD的建模方法
- []什么是IGBT?IGBT是大功率元器件 属于绝缘型晶体管
- []环球资讯:解决win10邮箱无法登陆 163企业邮箱常见问题
- []股权投资基金的内部管理
- []环球热门:微信公众号的消息免打扰怎么打开?微信公众号的消息免打扰打开方法
- []信息:python十大培训有哪些?python十大培训详情介绍
- []环球观焦点:饮水机什么牌子质量好?饮水机品牌推荐
- []快资讯丨网络基础知识有哪些?网络基础知识大全
- []win7系统如何关闭系统默认共享文件夹?关闭系统默认共享文件夹方法
- []快资讯丨紫光电子平板电脑怎么样?紫光电子平板电脑如何刷机?
- []焦点资讯:预告片下载网站 如何在预告片电影中添加预告片?
- []环球资讯:生活真的不过如此吗? 听“体育评书”
- []世界观热点:iframe标签已经不见执行 如何解决?
- []小飞人熨斗怎样?小飞人熨斗特点介绍
- []全球观热点:OpenStreetMap Google 百度 Bing arcgis瓦片地图服务以及瓦片计算
- []【新视野】d3dx9_43.dll是什么丢失了怎么办?解决办法
- []【世界速看料】二手电视机有哪些分类?二手电视机分类介绍
- []如何清除AcadDoc.lsp病毒?AcadDoc.lsp病毒清理步骤
- []全球快资讯丨TP-LINK 忘记密码怎么办?恢复出厂设置
- []全球速讯:linux关闭tomcat日志打印 linux下打开与关闭tomcat
- []世界实时:C++扑克牌类的设计 C++扑克牌类怎么设计?
- 环球观察:耶鲁大学耗时5年的研究成果 左脑与右脑的神奇功能研究
- 当前关注:Windows下【AxureRP】原型设计工具破解码与安装包 安装步骤
- 全球看点:关于葛晓非的介绍 你知道多少?
- 环球视讯!曲靖二中校长李舜荣:曲靖中统招生实际录取最低分数
- 观天下!针式打印机怎么调整打印范围?针式打印机调整打印位置的方法
- 焦点速讯:汽车钥匙丢了怎么办配一把多少钱?汽车钥匙丢了怎么补救?
- 潜行者是好人还是狼人?狼人杀潜行者角色什么功能?
- linux安装jdk8怎么装?手把手教你安装单机版Hadoop3.2.1
- 当前时讯:利用活跃变量分析来去掉vmp的大部分垃圾指令 活跃性计算的方法
- 【全球快播报】
百度云资源分享 百度云干货资源
- 全球报道:基金收益怎么计算?基金收益计算器
- 一加手机怎么开启手电筒?一加手机开启手电筒操作步骤
- 产品报价单模板 制作报价时格式和细节
- 【天天新要闻】工程师必知:IP协议和IP地址是什么关系?
- 【全球速看料】2021年江苏高考成绩查询网址及查分方式
- 世界看点:立方体的体积怎么计算?计算方法
- 弘扬雷锋精神走进新时代 携手走向中华民族的伟大复兴
- 证监会:允许房地产和建筑等密切相关行业上市公司实施涉房重组
- 热点聚焦:德信中国2.68亿股配售事项及认购事项已达成 所得款项净额2.31亿港元
- 新消息丨德信地产集团目前已交付25个批次 总计交付近2万套
- 看点:德展健康:公司及控股子公司主营产品为心脑血管领域,目前公司在销产品中不包括上述产品
- 【环球聚看点】建发股份:厦门钟宅畲族社区旧村改造部分土地已出让 总价21亿
- ST华英:根据中国登记结算公司主动下发的数据,截止2022年12月20日公司股东户数为32,811户
- 今日热讯:丰原药业:产品价格受市场供求变化影响。公司子公司利康制药扑热息痛当前价格虽有上升,但没有较大变化
- 上实城开:收购跃成全部发行股本先决条件均已达成
- 焦点观察:盛和资源:公司与VHM公司签署了谅解备忘录,但尚未达到应予披露的标准
- 每日讯息!中国交通建设200亿元公司债券已获受理
- 环球热点评!滔搏:第三季度零售及批发业务总销售金额同比录得10%-20%下跌
- 华峰铝业:华青铝业为公司控股股东华峰集团参与投资的企业
- 热议:南极电商:公司授权有口罩,冰贴,消毒液等品类
- 报道:顾家家居股东持有的431万股股份解除质押 另有355万股股份质押
- 世界快资讯丨美亚光电:谢谢提问。根据中登结算公司下发的相关文件,截至12月20日收盘,公司股东数为22,823户
- 世界微动态丨“21长沙高新MTN001A”利率到期调整 由3.76%上调至4.90%
- 当前聚焦:青岛四批集中供地:40宗地全部出让收金约142亿元
- 哪些纸尿裤品牌值得购买呢,看看国际妈咪怎么说?
- 天津公积金新政:二套房贷款首付比例降至40%
- 【全球快播报】福建东百集团发行1亿元超短期融资券 利率5.40%
- 粤泰股份:关于上海宗美,目前公司共收到430万元回款,公司正向当地法院申请执行其部分资产
- 全球最资讯丨广东东莞国金大厦地块调整批前公示 将增加8.25万平米住宅供地等
- 世界微速讯:电投能源:发行价格将根据国资委和证监会有关规定确定,获得批复后,公司会努力早日完成发行
- 爱与责任同行,做难而正确的事!太平洋房屋荣膺“年度社会责任奖”!
- 当前焦点!金融街控股拟22.6亿元转让孙公司北京融嘉100%股权予华融基础
- 华融化学:公司不直接从事异质结电池业务
- 昊志机电:公司2022年度业绩情况以及相关案件进展情况请以公司后续披露的相关公告为准
- 1099元酒店隔离套票:无法随意出门,只能点外卖
- 大理遭遇大面积客房退单,春节预定量从80%下降到不足30%
- 焦点速讯:酒店业变革趋势:人工智能支持下的数字营销演化逻辑
- Cirium:航空市场复苏遇“气流颠簸”
- 全球动态:获年度品牌影响力大奖 合生创展的品牌力启示录
- 晨化股份:截止2022年12月20日公司股东人数约为1.78万人
- 12月21日煌上煌涨停分析:休闲食品,社区团购,新零售概念热股
- 12月21日南岭民爆涨停分析:民爆,湖南国企改革,国企改革概念热股
- 天天资讯:山东省:房企不得使用商业承兑汇票等非现金方式支付工程款
- 每日消息!12月21日人民网涨停分析:传媒,直播/短视频,彩票概念热股
- 恒大公布债务重组进展:分歧收窄 偿债资源是否能产生预期价值存在较大不确定性
- 天津公积金二套贷款首付由60%降至40%
- 天天关注:广东惠州:高层次人才公积金贷款额度可达100万元
- 焦点播报:12月21日友阿股份涨停分析:新零售,免税店概念,长寿药NMN概念热股
- 冠珠和金舵瓷砖哪个好?产品和服务给出答案
- 甜啦啦以鲜果茶,迎战全民大健康
- 浙东“绿色充电宝”宁海抽水蓄能电站双坝完工
- 长安汽车与宁德时代将组建动力电池合资公司
- 精选!比亚迪中标山东电工电气东营储能示范项目设备采购!
- 世界观点:锂电85%,液流电池70%!江西省能源局发文加强新型储能项目全过程管理
- 今日快讯:甘肃武威市与中国三峡新能源(集团)股份有限公司签署甘肃黄羊抽水蓄能电站项目投资开发协议
- 平安基金中标西安高新区保障房公募REITs基金管理服务项目
- 环球速看:城投鹏基拟于新疆参股成立城市运营管理公司 注册资本100万元
- 中原内配:截止2022年12月20日,公司股东总户数为59,962户
- 昆明垠创地产名下经典尚城二期法拍以底价约3.65亿成交
- 视焦点讯!“20红星05”持有人会议:将延长至2023年2月26日付息
- 消息!悦心健康:截止至2022年12月20日,公司股东总户数77,166户,机构总户数为792户
- 奕东电子:公司持续保持与客户的沟通,积极推进市场开发工作
- 当前焦点!百联东方商厦嘉定店因经营调整需要将于12月30日闭店
- 浔兴股份: 公司生产基于车间管理数据采集、处理辅助决策,提升管理效率
- 天天热议:容量补偿0.35元/度!补偿期不超过10年!内蒙古发文支持新型储能发展!
- 【天天播资讯】梅西为什么搭乘经济舱回国?
- 宁吉喆:明年必将迎来旅游业的恢复性增长热潮
- 两天销售额1.57亿元 宝企便携储能出海动能强劲
- 每日速递:多地更新峰谷电价政策 价差扩大打开储能等产业空间
- 环球百事通!国家电投发布900MWh储能设备电商化采购招标公告
- 亿嘉居住房地产底价1.34亿元竞得宜兴市一宗商住地
- 今日报丨强制转让!中国恒大减持7亿股恒大物业股权
- 全球关注:市值仅15亿 控股股东免费送7亿现金!上市公司再现花式“保壳” 交易所火速关注
- 全球今亮点!长江健康:阿胶是我国古老的名贵药材,具有补血滋阴,润燥,止血
- 滚动:普洛斯与中国联通加深战略合作 将深耕数字化基础设施发展
- 天天热点评!亿田智能:截止2022年12月20日股东总户数为8796户
- 【播资讯】脂肪肝“神药”来了,一个全新的百亿美元赛道?
- 当前最新:外汇交易提醒:日本央行意外调整国债收益率范围,日元暴涨近4%创四个月新高
- 【播资讯】河北石家庄投入超亿元资金促消费惠民生 提振消费信心
- 全球头条:酒店业备战元旦、春节传统旺季
- 全球热点!住房消费限制性政策被点名 限购、限贷松绑呼声最高
- 【全球速看料】顺腾国际控股拟1.25亿港元收购香港九龙物业
- 【全球热闻】12月21日重点数据和大事件前瞻
- 热资讯!11月武汉二手房价9连跌 10重点城市仅1地环比上涨
- 广州第四批集中供地收官:5宗地揽金191亿,保利95.8亿拿地
- 世界快看:深圳首个不限购“住宅”入市:每平方米近9万元,剩44年产权
- 全球热消息:油价“三连跌”收官 加一箱油将少花约18.5元
- 环球观热点:国家发改委:全国能源供需总体平稳有序
- 简讯:国际油价19日 上涨
- 公积金如何取出来用 如何将公积金取出来用
- 农行保险理财产品五年定期可以退吗 可以退农行保险理财产品五年定期吗
- 看热讯:学平险多久能理赔下来 学平险理赔要多久才能到账
- 全球播报:今日24时起 汽、柴油每吨降低480元和460元
- 当前信息:网上退保怎么办理流程 怎么办理网上退保
- 焦点讯息:大象转身!油气巨头收购7700MW风光项目!
- 最资讯丨中炬高新:控股股东中山润田所持1087万股因无人报价流拍
- 天天微资讯!美股异动 | 短视频营销服务商宝盛(BAOS.US)涨超21% 总市值约为832万美元
- 如何查询车辆保险是否到期 车辆保险是否到期如何查询
- 越秀房托1.26亿港元向越秀地产收购香港两项商业物业
- 头条焦点:百果园上市申请已通过聆讯 一个月前重新递交招股书