AVOption用于在FFmpeg中描述结构体中的成员变量。一个AVOption可以包含名称,简短的帮助信息,取值等。

上篇文章中概括了AVClass,AVOption和目标结构体之间的关系。以AVFormatContext为例,可以表示为下图。

上篇文章主要概括了AVClass,AVOption和目标结构体之间的从属关系,但是并没有分析有关AVOption的源代码。本文分析有关AVOption的源代码。

AVOption有关的API

AVOption常用的API可以分成两类:用于设置参数的API和用于读取参数的API。其中最有代表性的用于设置参数的API就是av_opt_set();而最有代表性的用于读取参数的API就是av_opt_get()。除了以上两个函数之外,本文再记录一个在FFmpeg的结构体初始化代码中最常用的用于设置默认值的函数av_opt_set_defaults()


函数调用关系图

av_opt_set()的函数调用关系图如下所示。

av_opt_get()的函数调用关系图如下所示。buf[128]

av_opt_set_defaults()的函数调用关系图如下所示。注:av_opt_set_defaults2是deprecated的api


av_opt_set()

通过AVOption设置参数最常用的函数就是av_opt_set()了。该函数通过字符串的方式(传入的参数是:变量名称的字符串和变量值的字符串)设置一个AVOption的值。此外,还包含了它的一系列“兄弟”函数av_opt_set_XXX(),其中“XXX”代表了int,double这些数据类型。使用这些函数的时候,可以指定int,double这些类型的变量(而不是字符串)作为输入,设定相应的AVOption的值。

  1. /**
  2. * @defgroup opt_set_funcs Option setting functions
  3. * @{
  4. * Those functions set the field of obj with the given name to value.
  5. *
  6. * @param[in] obj A struct whose first element is a pointer to an AVClass.
  7. * @param[in] name the name of the field to set
  8. * @param[in] val The value to set. In case of av_opt_set() if the field is not
  9. * of a string type, then the given string is parsed.
  10. * SI postfixes and some named scalars are supported.
  11. * If the field is of a numeric type, it has to be a numeric or named
  12. * scalar. Behavior with more than one scalar and +- infix operators
  13. * is undefined.
  14. * If the field is of a flags type, it has to be a sequence of numeric
  15. * scalars or named flags separated by '+' or '-'. Prefixing a flag
  16. * with '+' causes it to be set without affecting the other flags;
  17. * similarly, '-' unsets a flag.
  18. * @param search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN
  19. * is passed here, then the option may be set on a child of obj.
  20. *
  21. * @return 0 if the value has been set, or an AVERROR code in case of
  22. * error:
  23. * AVERROR_OPTION_NOT_FOUND if no matching option exists
  24. * AVERROR(ERANGE) if the value is out of range
  25. * AVERROR(EINVAL) if the value is not valid
  26. */
  27. int av_opt_set         (void *obj, const char *name, const char *val, int search_flags);
  28. int av_opt_set_int     (void *obj, const char *name, int64_t     val, int search_flags);
  29. int av_opt_set_double  (void *obj, const char *name, double      val, int search_flags);
  30. int av_opt_set_q       (void *obj, const char *name, AVRational  val, int search_flags);
  31. int av_opt_set_bin     (void *obj, const char *name, const uint8_t *val, int size, int search_flags);
  32. int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags);
  33. int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, int search_flags);
  34. int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags);
  35. int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags);
  36. 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,如下所示。

  1. int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
  2. {
  3. int ret = 0;
  4. void *dst, *target_obj;
  5. //查找
  6. const AVOption *o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);
  7. if (!o || !target_obj)
  8. return AVERROR_OPTION_NOT_FOUND;
  9. if (!val && (o->type != AV_OPT_TYPE_STRING &&
  10. o->type != AV_OPT_TYPE_PIXEL_FMT && o->type != AV_OPT_TYPE_SAMPLE_FMT &&
  11. o->type != AV_OPT_TYPE_IMAGE_SIZE && o->type != AV_OPT_TYPE_VIDEO_RATE &&
  12. o->type != AV_OPT_TYPE_DURATION && o->type != AV_OPT_TYPE_COLOR &&
  13. o->type != AV_OPT_TYPE_CHANNEL_LAYOUT))
  14. return AVERROR(EINVAL);
  15. if (o->flags & AV_OPT_FLAG_READONLY)
  16. return AVERROR(EINVAL);
  17. //dst指向具体的变量
  18. //注意:offset的作用
  19. dst = ((uint8_t*)target_obj) + o->offset;
  20. //根据AVOption不同的类型,调用不同的设置函数
  21. switch (o->type) {
  22. case AV_OPT_TYPE_STRING:   return set_string(obj, o, val, dst);
  23. case AV_OPT_TYPE_BINARY:   return set_string_binary(obj, o, val, dst);
  24. case AV_OPT_TYPE_FLAGS:
  25. case AV_OPT_TYPE_INT:
  26. case AV_OPT_TYPE_INT64:
  27. case AV_OPT_TYPE_FLOAT:
  28. case AV_OPT_TYPE_DOUBLE:
  29. case AV_OPT_TYPE_RATIONAL: return set_string_number(obj, target_obj, o, val, dst);
  30. case AV_OPT_TYPE_IMAGE_SIZE: return set_string_image_size(obj, o, val, dst);
  31. case AV_OPT_TYPE_VIDEO_RATE: return set_string_video_rate(obj, o, val, dst);
  32. case AV_OPT_TYPE_PIXEL_FMT:  return set_string_pixel_fmt(obj, o, val, dst);
  33. case AV_OPT_TYPE_SAMPLE_FMT: return set_string_sample_fmt(obj, o, val, dst);
  34. case AV_OPT_TYPE_DURATION:
  35. if (!val) {
  36. *(int64_t *)dst = 0;
  37. return 0;
  38. } else {
  39. if ((ret = av_parse_time(dst, val, 1)) < 0)
  40. av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as duration\n", val);
  41. return ret;
  42. }
  43. break;
  44. case AV_OPT_TYPE_COLOR:      return set_string_color(obj, o, val, dst);
  45. case AV_OPT_TYPE_CHANNEL_LAYOUT:
  46. if (!val || !strcmp(val, "none")) {
  47. *(int64_t *)dst = 0;
  48. } else {
  49. #if FF_API_GET_CHANNEL_LAYOUT_COMPAT
  50. int64_t cl = ff_get_channel_layout(val, 0);
  51. #else
  52. int64_t cl = av_get_channel_layout(val);
  53. #endif
  54. if (!cl) {
  55. av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as channel layout\n", val);
  56. ret = AVERROR(EINVAL);
  57. }
  58. *(int64_t *)dst = cl;
  59. return ret;
  60. }
  61. break;
  62. }
  63. av_log(obj, AV_LOG_ERROR, "Invalid option type.\n");
  64. return AVERROR(EINVAL);
  65. }

从源代码可以看出,av_opt_set()首先调用av_opt_find2()查找AVOption。如果找到了,则根据AVOption的type,调用不同的函数(set_string(),set_string_number(),set_string_image_size()等等)将输入的字符串转化为相应type的数据并对该AVOption进行赋值。如果没有找到,则立即返回“没有找到AVOption”的错误。

av_opt_find2() / av_opt_find()

2多一个参数void **target_obj

av_opt_find2()本身也是一个API函数,用于查找AVOption。它的声明位于libavutil\opt.h中,如下所示。

  1. /**
  2. * Look for an option in an object. Consider only options which
  3. * have all the specified flags set.
  4. *
  5. * @param[in] obj A pointer to a struct whose first element is a
  6. *                pointer to an AVClass.
  7. *                Alternatively a double pointer to an AVClass, if
  8. *                AV_OPT_SEARCH_FAKE_OBJ search flag is set.
  9. * @param[in] name The name of the option to look for.
  10. * @param[in] unit When searching for named constants, name of the unit
  11. *                 it belongs to.
  12. * @param opt_flags Find only options with all the specified flags set (AV_OPT_FLAG).
  13. * @param search_flags A combination of AV_OPT_SEARCH_*.
  14. * @param[out] target_obj if non-NULL, an object to which the option belongs will be
  15. * written here. It may be different from obj if AV_OPT_SEARCH_CHILDREN is present
  16. * in search_flags. This parameter is ignored if search_flags contain
  17. * AV_OPT_SEARCH_FAKE_OBJ.
  18. *
  19. * @return A pointer to the option found, or NULL if no option
  20. *         was found.
  21. */
  22. const AVOption *av_opt_find2(void *obj, const char *name, const char *unit,
  23. 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。

  1. const AVOption *av_opt_find(void *obj, const char *name, const char *unit,
  2. int opt_flags, int search_flags)
  3. {
  4. return av_opt_find2(obj, name, unit, opt_flags, search_flags, NULL);
  5. }

下面先看一下av_opt_find2()函数的定义。该函数的定义位于libavutil\opt.c中,如下所示。

  1. const AVOption *av_opt_find2(void *obj, const char *name, const char *unit,
  2. int opt_flags, int search_flags, void **target_obj)
  3. {
  4. const AVClass  *c;
  5. const AVOption *o = NULL;
  6. if(!obj)
  7. return NULL;
  8. c= *(AVClass**)obj;
  9. if (!c)
  10. return NULL;
  11. //查找范围包含子节点的时候
  12. //递归调用av_opt_find2()
  13. if (search_flags & AV_OPT_SEARCH_CHILDREN) {
  14. if (search_flags & AV_OPT_SEARCH_FAKE_OBJ) {
  15. const AVClass *child = NULL;
  16. while (child = av_opt_child_class_next(c, child))
  17. if (o = av_opt_find2(&child, name, unit, opt_flags, search_flags, NULL))
  18. return o;
  19. } else {
  20. void *child = NULL;
  21. while (child = av_opt_child_next(obj, child))
  22. if (o = av_opt_find2(child, name, unit, opt_flags, search_flags, target_obj))
  23. return o;
  24. }
  25. }
  26. //遍历
  27. while (o = av_opt_next(obj, o)) {
  28. //比较名称
  29. if (!strcmp(o->name, name) && (o->flags & opt_flags) == opt_flags &&
  30. ((!unit && o->type != AV_OPT_TYPE_CONST) ||
  31. (unit  && o->type == AV_OPT_TYPE_CONST && o->unit && !strcmp(o->unit, unit)))) {
  32. if (target_obj) {
  33. if (!(search_flags & AV_OPT_SEARCH_FAKE_OBJ))
  34. *target_obj = obj;
  35. else
  36. *target_obj = NULL;
  37. }
  38. return o;
  39. }
  40. }
  41. return NULL;
  42. }

前半部分的if()语句中的内容只有在search_flags指定为AV_OPT_SEARCH_CHILDREN的时候才会执行。后半部分代码是重点。后半部分代码是一个while()循环,该循环的条件是一个函数av_opt_next()。

av_opt_next()

av_opt_next()也是一个FFmpeg的API函数。使用它可以循环遍历目标结构体的所有AVOption,它的声明如下。

  1. /**
  2. * Iterate over all AVOptions belonging to obj.
  3. *
  4. * @param obj an AVOptions-enabled struct or a double pointer to an
  5. *            AVClass describing it.
  6. * @param prev result of the previous call to av_opt_next() on this object
  7. *             or NULL
  8. * @return next AVOption or NULL
  9. */
  10. const AVOption *av_opt_next(void *obj, const AVOption *prev);

av_opt_next()的定义如下所示。

  1. const AVOption *av_opt_next(void *obj, const AVOption *last)
  2. {
  3. AVClass *class = *(AVClass**)obj;
  4. if (!last && class && class->option && class->option[0].name)
  5. return class->option;
  6. if (last && last[1].name)
  7. return ++last;
  8. return NULL;
  9. }

从av_opt_next()的代码可以看出,输入的AVOption类型的last变量为空的时候,会返回该AVClass的option数组的第一个元素,否则会返回数组的下一个元素。

现在再回到av_opt_find2()函数。我们发现在while()循环中有一个strcmp()函数,正是这个函数比较输入的AVOption的name和AVClass的option数组中每个元素的name,当上述两个name相等的时候,就代表查找到了AVOption,接着就可以返回获得的AVOption。

现在再回到刚才的av_opt_set()函数。(注:opt.c中有个main函数)该函数内部有一个void型的变量dst用于确定需要设定的AVOption对应变量的位置。具体的方法就是将输入的AVClass结构体的首地址加上该AVOption的偏移量offset。

确定了AVOption对应的变量的位置之后,就可以根据该AVOption的类型type的不同调用不同的字符串转换函数设置相应的值了。
我们可以看几个设置值的的简单例子:

1. AV_OPT_TYPE_STRING
当AVOption的type为AV_OPT_TYPE_STRING的时候,调用set_string()方法设置相应的值。set_string()的定义如下:

  1. static int set_string(void *obj, const AVOption *o, const char *val, uint8_t **dst)
  2. {
  3. av_freep(dst);
  4. *dst = av_strdup(val);
  5. return 0;
  6. }

其中又调用了一个函数av_strdup(),这是一个FFmpeg的API函数,用于拷贝字符串。其中调用了memcpy()。

2. AV_OPT_TYPE_IMAGE_SIZE
当AVOption的type为AV_OPT_TYPE_IMAGE_SIZE的时候,调用set_string_image_size ()方法设置相应的值。set_string_image_size()的定义如下。

  1. static int set_string_image_size(void *obj, const AVOption *o, const char *val, int *dst)
  2. {
  3. int ret;
  4. if (!val || !strcmp(val, "none")) {
  5. dst[0] =
  6. dst[1] = 0;  //连续赋值
  7. return 0;
  8. }
  9. ret = av_parse_video_size(dst, dst + 1, val);
  10. if (ret < 0)
  11. av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\" as image size\n", val);
  12. return ret;
  13. }

可见其中调用了另一个函数av_parse_video_size()。

av_parse_video_size()

av_parse_video_size()是一个FFmpeg的API函数,用于解析出输入的分辨率字符串的宽高信息。例如,输入的字符串为“1920x1080”或者“1920*1080”,经过av_parse_video_size()的处理之后,可以得到宽度为1920,高度为1080;此外,输入一个“特定分辨率”字符串例如“vga”,也可以得到宽度为640,高度为480。该函数不属于AVOption这部分的内容,而是整个FFmpeg通用的一个字符串解析函数。声明位于libavutil\parseutils.h中,如下所示。

  1. /**
  2. * Parse str and put in width_ptr and height_ptr the detected values.
  3. *
  4. * @param[in,out] width_ptr pointer to the variable which will contain the detected
  5. * width value
  6. * @param[in,out] height_ptr pointer to the variable which will contain the detected
  7. * height value
  8. * @param[in] str the string to parse: it has to be a string in the format
  9. * width x height or a valid video size abbreviation.
  10. * @return >= 0 on success, a negative error code otherwise
  11. */
  12. int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str);

从声明中可以看出,该函数输入一个字符串str,输出结果保存在width_ptr和height_ptr所指向的内存中。av_parse_video_size()定义位于libavutil\parseutils.c中,代码如下。

  1. //解析分辨率
  2. int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str)
  3. {
  4. int i;
  5. int n = FF_ARRAY_ELEMS(video_size_abbrs);
  6. const char *p;
  7. int width = 0, height = 0;
  8. //先看看有没有“分辨率简称”相同的(例如vga,qcif等)
  9. for (i = 0; i < n; i++) {
  10. if (!strcmp(video_size_abbrs[i].abbr, str)) {
  11. width  = video_size_abbrs[i].width;
  12. height = video_size_abbrs[i].height;
  13. break;
  14. }
  15. }
  16. //如果没有使用“分辨率简称”,而是使用具体的数值(例如“1920x1080”),则执行下面的步骤
  17. if (i == n) {
  18. //strtol():字符串转换成整型,遇到非数字则停止
  19. width = strtol(str, (void*)&p, 10);
  20. if (*p)
  21. p++;
  22. height = strtol(p, (void*)&p, 10);
  23. /* trailing extraneous data detected, like in 123x345foobar */
  24. if (*p)
  25. return AVERROR(EINVAL);
  26. }
  27. //检查一下正确性
  28. if (width <= 0 || height <= 0)
  29. return AVERROR(EINVAL);
  30. *width_ptr  = width;
  31. *height_ptr = height;
  32. return 0;
  33. }

上述代码中包含了FFmpeg中两种解析视频分辨率的方法。FFmpeg中包含两种设定视频分辨率的方法:通过已经定义好的“分辨率简称”,或者通过具体的数值。代码中首先遍历“特定分辨率”的数组video_size_abbrs。该数组定义如下所示。

  1. typedef struct {
  2. const char *abbr;
  3. int width, height;
  4. } VideoSizeAbbr;
  5. static const VideoSizeAbbr video_size_abbrs[] = {
  6. { "ntsc",      720, 480 },
  7. { "pal",       720, 576 },
  8. { "qntsc",     352, 240 }, /* VCD compliant NTSC */
  9. { "qpal",      352, 288 }, /* VCD compliant PAL */
  10. { "sntsc",     640, 480 }, /* square pixel NTSC */
  11. { "spal",      768, 576 }, /* square pixel PAL */
  12. { "film",      352, 240 },
  13. { "ntsc-film", 352, 240 },
  14. { "sqcif",     128,  96 },
  15. { "qcif",      176, 144 },
  16. { "cif",       352, 288 },
  17. { "4cif",      704, 576 },
  18. { "16cif",    1408,1152 },
  19. { "qqvga",     160, 120 },
  20. { "qvga",      320, 240 },
  21. { "vga",       640, 480 },
  22. { "svga",      800, 600 },
  23. { "xga",      1024, 768 },
  24. { "uxga",     1600,1200 },
  25. { "qxga",     2048,1536 },
  26. { "sxga",     1280,1024 },
  27. { "qsxga",    2560,2048 },
  28. { "hsxga",    5120,4096 },
  29. { "wvga",      852, 480 },
  30. { "wxga",     1366, 768 },
  31. { "wsxga",    1600,1024 },
  32. { "wuxga",    1920,1200 },
  33. { "woxga",    2560,1600 },
  34. { "wqsxga",   3200,2048 },
  35. { "wquxga",   3840,2400 },
  36. { "whsxga",   6400,4096 },
  37. { "whuxga",   7680,4800 },
  38. { "cga",       320, 200 },
  39. { "ega",       640, 350 },
  40. { "hd480",     852, 480 },
  41. { "hd720",    1280, 720 },
  42. { "hd1080",   1920,1080 },
  43. { "2k",       2048,1080 }, /* Digital Cinema System Specification */
  44. { "2kflat",   1998,1080 },
  45. { "2kscope",  2048, 858 },
  46. { "4k",       4096,2160 }, /* Digital Cinema System Specification */
  47. { "4kflat",   3996,2160 },
  48. { "4kscope",  4096,1716 },
  49. { "nhd",       640,360  },
  50. { "hqvga",     240,160  },
  51. { "wqvga",     400,240  },
  52. { "fwqvga",    432,240  },
  53. { "hvga",      480,320  },
  54. { "qhd",       960,540  },
  55. };

通过调用strcmp()方法比对输入字符串的值与video_size_abbrs数组中每个VideoSizeAbbr元素的abbr字段的值,判断输入的字符串是否指定了这些标准的分辨率。如果指定了的话,则返回该分辨率的宽和高。

如果从上述列表中没有找到相应的“特定分辨率”,则说明输入的字符串应该是一个具体的分辨率的值,形如“1920*1020”,“1280x720”这样的字符串。这个时候就需要对这个字符串进行解析,并从中提取出数字信息。通过两次调用strtol()方法,从字符串中提取出宽高信息(第一次提取出宽,第二次提取出高)。

PS1:strtol()用于将字符串转换成整型,遇到非数字则停止。

PS2:从这种解析方法可以得到一个信息——FFmpeg并不管“宽{X}高”中间的那个{X}是什么字符,也就是说中间那个字符不一定非得是“*”或者“x”。后来试了一下,中间那个字符使用其他字母也是可以的。


av_opt_set_defaults()

av_opt_set_defaults()是一个FFmpeg的API,作用是给一个结构体的成员变量设定默认值。在FFmpeg初始化其各种结构体(AVFormatContext,AVCodecContext等)的时候,通常会调用该函数设置结构体中的默认值。av_opt_set_defaults()的声明如下所示。

编者注:其实av_opt_set_defaults2是弃用的api,不要被后面的2给骗了。

注:avcodec_get_context_defaults3 被avcodec_alloc_context3调用

avformat_get_context_defaults ,      被avformat_alloc_context调用

avcodec_open2,avformat_open_input里面都调用了av_opt_set_defaults函数

  1. /**
  2. * Set the values of all AVOption fields to their default values.
  3. *
  4. * @param s an AVOption-enabled struct (its first member must be a pointer to AVClass)
  5. */
  6. void av_opt_set_defaults(void *s);

可见只需要把包含AVOption功能的结构体(第一个变量是一个AVClass类型的指针)的指针提供给av_opt_set_defaults(),就可以初始化该结构体的默认值了。

下面看一下av_opt_set_defaults()的源代码,位于libavutil\opt.c,如下所示。

  1. void av_opt_set_defaults(void *s)
  2. {
  3. //奇怪的#if...#endif
  4. #if FF_API_OLD_AVOPTIONS
  5. av_opt_set_defaults2(s, 0, 0);
  6. }
  7. void av_opt_set_defaults2(void *s, int mask, int flags)
  8. {
  9. #endif
  10. const AVOption *opt = NULL;
  11. //遍历所有的AVOption
  12. while ((opt = av_opt_next(s, opt))) {
  13. //注意:offset的使用
  14. void *dst = ((uint8_t*)s) + opt->offset;
  15. #if FF_API_OLD_AVOPTIONS
  16. if ((opt->flags & mask) != flags)
  17. continue;
  18. #endif
  19. if (opt->flags & AV_OPT_FLAG_READONLY)
  20. continue;
  21. //读取各种default_val
  22. switch (opt->type) {
  23. case AV_OPT_TYPE_CONST:
  24. /* Nothing to be done here */
  25. break;
  26. case AV_OPT_TYPE_FLAGS:
  27. case AV_OPT_TYPE_INT:
  28. case AV_OPT_TYPE_INT64:
  29. case AV_OPT_TYPE_DURATION:
  30. case AV_OPT_TYPE_CHANNEL_LAYOUT:
  31. write_number(s, opt, dst, 1, 1, opt->default_val.i64);
  32. break;
  33. case AV_OPT_TYPE_DOUBLE:
  34. case AV_OPT_TYPE_FLOAT: {
  35. double val;
  36. val = opt->default_val.dbl;
  37. write_number(s, opt, dst, val, 1, 1);
  38. }
  39. break;
  40. case AV_OPT_TYPE_RATIONAL: {
  41. AVRational val;
  42. val = av_d2q(opt->default_val.dbl, INT_MAX);
  43. write_number(s, opt, dst, 1, val.den, val.num);
  44. }
  45. break;
  46. case AV_OPT_TYPE_COLOR:
  47. set_string_color(s, opt, opt->default_val.str, dst);
  48. break;
  49. case AV_OPT_TYPE_STRING:
  50. set_string(s, opt, opt->default_val.str, dst);
  51. break;
  52. case AV_OPT_TYPE_IMAGE_SIZE:
  53. set_string_image_size(s, opt, opt->default_val.str, dst);
  54. break;
  55. case AV_OPT_TYPE_VIDEO_RATE:
  56. set_string_video_rate(s, opt, opt->default_val.str, dst);
  57. break;
  58. case AV_OPT_TYPE_PIXEL_FMT:
  59. write_number(s, opt, dst, 1, 1, opt->default_val.i64);
  60. break;
  61. case AV_OPT_TYPE_SAMPLE_FMT:
  62. write_number(s, opt, dst, 1, 1, opt->default_val.i64);
  63. break;
  64. case AV_OPT_TYPE_BINARY:
  65. set_string_binary(s, opt, opt->default_val.str, dst);
  66. break;
  67. case AV_OPT_TYPE_DICT:
  68. /* Cannot set defaults for these types */
  69. break;
  70. default:
  71. av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n", opt->type, opt->name);
  72. }
  73. }
  74. }

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。该函数的定义在前文中已经做过分析,这里再重复一下。定义如下所示。

  1. const AVOption *av_opt_next(void *obj, const AVOption *last)
  2. {
  3. AVClass *class = *(AVClass**)obj;
  4. if (!last && class && class->option && class->option[0].name)
  5. return class->option;
  6. if (last && last[1].name)
  7. return ++last;
  8. return NULL;
  9. }

从av_opt_next()的代码可以看出,输入的AVOption类型的last为空的时候,会返回该AVClass的option数组的第一个元素,否则会返回下一个元素。

下面简单解读一下av_opt_set_defaults()中while()循环语句里面的内容。有一个void类型的指针dst用于确定当前AVOption代表的变量的位置。该指针的位置由结构体的首地址和变量的偏移量offset确定。然后根据AVOption代表的变量的类型type,调用不同的函数设定相应的值。例如type为AV_OPT_TYPE_INT的话,则会调用write_number();type为AV_OPT_TYPE_STRING的时候,则会调用set_string();type为AV_OPT_TYPE_IMAGE_SIZE的时候,则会调用set_string_image_size()。有关这些设置值的函数在前文中已经有所叙述,不再重复。需要注意的是,该函数中设置的值都是AVOption中的default_val变量的值。


av_opt_get()

av_opt_get()用于获取一个AVOption变量的值。需要注意的是,不论是何种类型的变量,通过av_opt_get()取出来的值都是字符串类型的。此外,还包含了它的一系列“兄弟”函数av_opt_get_XXX()(其中“XXX”代表了int,double这些数据类型)。通过这些“兄弟”函数可以直接取出int,double类型的数值。av_opt_get()的声明如下所示。

  1. /**
  2. * @defgroup opt_get_funcs Option getting functions
  3. * @{
  4. * Those functions get a value of the option with the given name from an object.
  5. *
  6. * @param[in] obj a struct whose first element is a pointer to an AVClass.
  7. * @param[in] name name of the option to get.
  8. * @param[in] search_flags flags passed to av_opt_find2. I.e. if AV_OPT_SEARCH_CHILDREN
  9. * is passed here, then the option may be found in a child of obj.
  10. * @param[out] out_val value of the option will be written here
  11. * @return >=0 on success, a negative error code otherwise
  12. */
  13. /**
  14. * @note the returned string will be av_malloc()ed and must be av_free()ed by the caller
  15. */
  16. int av_opt_get         (void *obj, const char *name, int search_flags, uint8_t   **out_val);
  17. int av_opt_get_int     (void *obj, const char *name, int search_flags, int64_t    *out_val);
  18. int av_opt_get_double  (void *obj, const char *name, int search_flags, double     *out_val);
  19. int av_opt_get_q       (void *obj, const char *name, int search_flags, AVRational *out_val);
  20. int av_opt_get_image_size(void *obj, const char *name, int search_flags, int *w_out, int *h_out);
  21. int av_opt_get_pixel_fmt (void *obj, const char *name, int search_flags, enum AVPixelFormat *out_fmt);
  22. int av_opt_get_sample_fmt(void *obj, const char *name, int search_flags, enum AVSampleFormat *out_fmt);
  23. int av_opt_get_video_rate(void *obj, const char *name, int search_flags, AVRational *out_val);
  24. int av_opt_get_channel_layout(void *obj, const char *name, int search_flags, int64_t *ch_layout);

下面我们看一下av_opt_get()的定义,如下所示。

  1. int av_opt_get(void *obj, const char *name, int search_flags, uint8_t **out_val)
  2. {
  3. void *dst, *target_obj;
  4. //查找
  5. const AVOption *o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);
  6. uint8_t *bin, buf[128];
  7. int len, i, ret;
  8. int64_t i64;
  9. if (!o || !target_obj || (o->offset<=0 && o->type != AV_OPT_TYPE_CONST))
  10. return AVERROR_OPTION_NOT_FOUND;
  11. //注意:offset的使用
  12. dst = (uint8_t*)target_obj + o->offset;
  13. //使用sprintf()转换成字符串,存入buf
  14. buf[0] = 0;
  15. switch (o->type) {
  16. case AV_OPT_TYPE_FLAGS:     ret = snprintf(buf, sizeof(buf), "0x%08X",  *(int    *)dst);break;
  17. case AV_OPT_TYPE_INT:       ret = snprintf(buf, sizeof(buf), "%d" ,     *(int    *)dst);break;
  18. case AV_OPT_TYPE_INT64:     ret = snprintf(buf, sizeof(buf), "%"PRId64, *(int64_t*)dst);break;
  19. case AV_OPT_TYPE_FLOAT:     ret = snprintf(buf, sizeof(buf), "%f" ,     *(float  *)dst);break;
  20. case AV_OPT_TYPE_DOUBLE:    ret = snprintf(buf, sizeof(buf), "%f" ,     *(double *)dst);break;
  21. case AV_OPT_TYPE_VIDEO_RATE:
  22. case AV_OPT_TYPE_RATIONAL:  ret = snprintf(buf, sizeof(buf), "%d/%d",   ((AVRational*)dst)->num, ((AVRational*)dst)->den);break;
  23. case AV_OPT_TYPE_CONST:     ret = snprintf(buf, sizeof(buf), "%f" ,     o->default_val.dbl);break;
  24. case AV_OPT_TYPE_STRING:
  25. if (*(uint8_t**)dst)
  26. *out_val = av_strdup(*(uint8_t**)dst);
  27. else
  28. *out_val = av_strdup("");
  29. return 0;
  30. case AV_OPT_TYPE_BINARY:
  31. len = *(int*)(((uint8_t *)dst) + sizeof(uint8_t *));
  32. if ((uint64_t)len*2 + 1 > INT_MAX)
  33. return AVERROR(EINVAL);
  34. if (!(*out_val = av_malloc(len*2 + 1)))
  35. return AVERROR(ENOMEM);
  36. if (!len) {
  37. *out_val[0] = '\0';
  38. return 0;
  39. }
  40. bin = *(uint8_t**)dst;
  41. for (i = 0; i < len; i++)
  42. snprintf(*out_val + i*2, 3, "%02X", bin[i]);
  43. return 0;
  44. case AV_OPT_TYPE_IMAGE_SIZE:
  45. //分辨率
  46. ret = snprintf(buf, sizeof(buf), "%dx%d", ((int *)dst)[0], ((int *)dst)[1]);
  47. break;
  48. case AV_OPT_TYPE_PIXEL_FMT:
  49. //像素格式
  50. ret = snprintf(buf, sizeof(buf), "%s", (char *)av_x_if_null(av_get_pix_fmt_name(*(enum AVPixelFormat *)dst), "none"));
  51. break;
  52. case AV_OPT_TYPE_SAMPLE_FMT:
  53. //采样格式
  54. ret = snprintf(buf, sizeof(buf), "%s", (char *)av_x_if_null(av_get_sample_fmt_name(*(enum AVSampleFormat *)dst), "none"));
  55. break;
  56. case AV_OPT_TYPE_DURATION:
  57. //时长
  58. i64 = *(int64_t *)dst;
  59. ret = snprintf(buf, sizeof(buf), "%"PRIi64":%02d:%02d.%06d",
  60. i64 / 3600000000, (int)((i64 / 60000000) % 60),
  61. (int)((i64 / 1000000) % 60), (int)(i64 % 1000000));
  62. break;
  63. case AV_OPT_TYPE_COLOR:
  64. ret = snprintf(buf, sizeof(buf), "0x%02x%02x%02x%02x",
  65. (int)((uint8_t *)dst)[0], (int)((uint8_t *)dst)[1],
  66. (int)((uint8_t *)dst)[2], (int)((uint8_t *)dst)[3]);
  67. break;
  68. case AV_OPT_TYPE_CHANNEL_LAYOUT:
  69. i64 = *(int64_t *)dst;
  70. ret = snprintf(buf, sizeof(buf), "0x%"PRIx64, i64);
  71. break;
  72. default:
  73. return AVERROR(EINVAL);
  74. }
  75. if (ret >= sizeof(buf))
  76. return AVERROR(EINVAL);
  77. //拷贝
  78. *out_val = av_strdup(buf);
  79. return 0;
  80. }

从av_opt_get()的定义可以看出,该函数首先通过av_opt_find2()查相应的AVOption,然后取出该变量的值,最后通过snprintf()将变量的值转化为字符串(各种各样类型的变量都这样处理)并且输出出来。   可以学到将各种类型转换成字符串

参考:

结构体成员管理AVClass AVOption之2AVOption,设置选项值的更多相关文章

  1. 结构体成员管理AVClass AVOption之1AVClass

    AVOption用于描述结构体中的成员变量.它最主要的作用可以概括为两个字:“赋值”. 一个AVOption结构体包含了变量名称,简短的帮助,取值等信息. 所有和AVOption有关的数据都存储在AV ...

  2. FFmpeg源代码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  3. FFmpeg源码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  4. go语言结构体作为函数参数,采用的是值传递

    经过验证,go语言结构体作为函数参数,采用的是值传递.所以对于大型结构体传参,考虑到值传递的性能损耗,最好能采用指针传递. 验证代码: package main import ( "fmt& ...

  5. ComboBoxEdit设置选项值(单选 多选)

    网上搜索的 例子 加 自己的 一点点补充 lookupedit 设置选项值: private void LookUpEditFormTest_Load(object sender, EventArgs ...

  6. 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符

    [源码下载] 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 结构体 ...

  7. FFmpeg源代码简单分析:结构体成员管理系统-AVClass

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  8. FFmpeg源代码简单分析:常见结构体的初始化和销毁(AVFormatContext,AVFrame等)

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  9. [转载] FFmpeg源代码简单分析:常见结构体的初始化和销毁(AVFormatContext,AVFrame等)

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

随机推荐

  1. C#测试程序运行时间

    一.用C#自带的StopWatch函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using System; usi ...

  2. Install and Enable Telnet server in Ubuntu Linux

    转:http://ubuntuguide.net/install-and-enable-telnet-server-in-ubuntu-linux 参考:http://auxnet.org/index ...

  3. Coherence代理节点在离开集群时的恢复

    Coherence的架构参考 在极端压力之下,有时候代理节点会忙于处理请求而不响应其他的心跳,同步,导致其他节点传输的报文没有回应,而被认为是离开集群,从而影响业务. 写了一段代码,能让进程在监听到有 ...

  4. python3读取html文件

    # htmlf=open('E:\\test2.html','r',encoding="utf-8") # htmlcont=htmlf.read() # print(type(h ...

  5. python socket timeout设置

    需要在调用socket的connect方法之前设置settimeout(time)方法,另外在设置之后要将再次调用settimeout(None)来设置socket进入阻塞模式. 如下代码示例: so ...

  6. nginx和selinux冲突

    cat /var/log/audit/audit.log |grep nginx |grep denied| audit2allow -M mynginx 取出selinux中有关于nginx被拒绝的 ...

  7. Less使用说明

    使用koala编译 Koala 是一款由国人开发的开源预处理语言图形编译工具,目前已支持 Less.Sass.Compass 与CoffeeScript. 目前支持以下系统:Windows,Mac, ...

  8. http://blog.chinaunix.net/uid-20577907-id-3519578.html

    http://blog.chinaunix.net/uid-20577907-id-3519578.html

  9. 【Cocos2d-x 3.0 基础系列一】 各类回调函数写法汇总

    一.button回调 1. Lambda 表达式,C++11 Lambda 赋予了Cocos2d-x 3.0创建回调函数的灵活性. auto itemNor = Sprite::create(&quo ...

  10. How to rebuild RPM database on a Red Hat Enterprise Linux system?

    本文是笔者最近遇到的一个故障的处理过程,解决方案是Rebuild RPM 的DB,后面内容其实是REDHAT官方的solutions,不过我遇到的现象和解决方案都与官方有点出入,故一直帖出来: 我遇到 ...