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的值。
[cpp]view plaincopy
/**??*?@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,如下所示。
[cpp]view plaincopy
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))??????????return?AVERROR(EINVAL);????????if?(o->flags?&?AV_OPT_FLAG_READONLY)??????????return?AVERROR(EINVAL);??????//dst指向具體的變量??????//注意:offset的作用??????dst?=?((uint8_t*)target_obj)?+?o->offset;??????//根據AVOption不同的類型,調用不同的設置函數??????switch?(o->type)?{??????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_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:?return?set_string_video_rate(obj,?o,?val,?dst);??????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?=?0;??????????????return?0;??????????}?else?{??????????????if?((ret?=?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?=?0;??????????}?else?{??#if?FF_API_GET_CHANNEL_LAYOUT_COMPAT??????????????int64_t?cl?=?ff_get_channel_layout(val,?0);??#else??????????????int64_t?cl?=?av_get_channel_layout(val);??#endif??????????????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”的錯誤。
av_opt_find2() / av_opt_find()
2多一個參數void **target_obj
av_opt_find2()本身也是一個API函數,用于查找AVOption。它的聲明位于libavutil\opt.h中,如下所示。
[cpp]view plaincopy
/**??*?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。
[cpp]view plaincopy
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中,如下所示。
[cpp]view plaincopy
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()。
av_opt_next()
av_opt_next()也是一個FFmpeg的API函數。使用它可以循環遍歷目標結構體的所有AVOption,它的聲明如下。
[cpp]view plaincopy
/**??*?Iterate?over?all?AVOptions?belonging?to?obj.??*??*?@param?obj?an?AVOptions-enabled?struct?or?a?double?pointer?to?an??*????????????AVClass?describing?it.??*?@param?prev?result?of?the?previous?call?to?av_opt_next()?on?this?object??*?????????????or?NULL??*?@return?next?AVOption?or?NULL??*/??const?AVOption?*av_opt_next(void?*obj,?const?AVOption?*prev);??av_opt_next()的定義如下所示。
[cpp]view plaincopy
const?AVOption?*av_opt_next(void?*obj,?const?AVOption?*last)??{??????AVClass?*class?=?*(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()函數。(注: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()的定義如下:
[cpp]view plaincopy
static?int?set_string(void?*obj,?const?AVOption?*o,?const?char?*val,?uint8_t?**dst)??{??????av_freep(dst);??????*dst?=?av_strdup(val);??????return?0;??}??其中又調用了一個函數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()的定義如下。
[cpp]view plaincopy
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]?=??????????dst[1]?=?0; ?//連續賦值????????return?0;??????}??????ret?=?av_parse_video_size(dst,?dst?+?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_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中,如下所示。
[cpp]view plaincopy
/**??*?Parse?str?and?put?in?width_ptr?and?height_ptr?the?detected?values.??*??*?@param[in,out]?width_ptr?pointer?to?the?variable?which?will?contain?the?detected??*?width?value??*?@param[in,out]?height_ptr?pointer?to?the?variable?which?will?contain?the?detected??*?height?value??*?@param[in]?str?the?string?to?parse:?it?has?to?be?a?string?in?the?format??*?width?x?height?or?a?valid?video?size?abbreviation.??*?@return?>=?0?on?success,?a?negative?error?code?otherwise??*/??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中,代碼如下。
[cpp]view plaincopy
//解析分辨率??int?av_parse_video_size(int?*width_ptr,?int?*height_ptr,?const?char?*str)??{??????int?i;??????int?n?=?FF_ARRAY_ELEMS(video_size_abbrs);??????const?char?*p;??????int?width?=?0,?height?=?0;??????//先看看有沒有“分辨率簡稱”相同的(例如vga,qcif等)??????for?(i?=?0;?i?<?n;?i++)?{??????????if?(!strcmp(video_size_abbrs[i].abbr,?str))?{??????????????width??=?video_size_abbrs[i].width;??????????????height?=?video_size_abbrs[i].height;??????????????break;??????????}??????}??????//如果沒有使用“分辨率簡稱”,而是使用具體的數值(例如“1920x1080”),則執行下面的步驟??????if?(i?==?n)?{??????????//strtol():字符串轉換成整型,遇到非數字則停止??????????width?=?strtol(str,?(void*)&p,?10);??????????if?(*p)??????????????p++;??????????height?=?strtol(p,?(void*)&p,?10);????????????/*?trailing?extraneous?data?detected,?like?in?123x345foobar?*/??????????if?(*p)??????????????return?AVERROR(EINVAL);??????}??????//檢查一下正確性??????if?(width?<=?0?||?height?<=?0)??????????return?AVERROR(EINVAL);??????*width_ptr??=?width;??????*height_ptr?=?height;??????return?0;??}??上述代碼中包含了FFmpeg中兩種解析視頻分辨率的方法。FFmpeg中包含兩種設定視頻分辨率的方法:通過已經定義好的“分辨率簡稱”,或者通過具體的數值。代碼中首先遍歷“特定分辨率”的數組video_size_abbrs。該數組定義如下所示。
[cpp]view plaincopy
typedef?struct?{??????const?char?*abbr;??????int?width,?height;??}?VideoSizeAbbr;????static?const?VideoSizeAbbr?video_size_abbrs[]?=?{??????{?"ntsc",??????720,?480?},??????{?"pal",???????720,?576?},??????{?"qntsc",?????352,?240?},?/*?VCD?compliant?NTSC?*/??????{?"qpal",??????352,?288?},?/*?VCD?compliant?PAL?*/??????{?"sntsc",?????640,?480?},?/*?square?pixel?NTSC?*/??????{?"spal",??????768,?576?},?/*?square?pixel?PAL?*/??????{?"film",??????352,?240?},??????{?"ntsc-film",?352,?240?},??????{?"sqcif",?????128,??96?},??????{?"qcif",??????176,?144?},??????{?"cif",???????352,?288?},??????{?"4cif",??????704,?576?},??????{?"16cif",????1408,1152?},??????{?"qqvga",?????160,?120?},??????{?"qvga",??????320,?240?},??????{?"vga",???????640,?480?},??????{?"svga",??????800,?600?},??????{?"xga",??????1024,?768?},??????{?"uxga",?????1600,1200?},??????{?"qxga",?????2048,1536?},??????{?"sxga",?????1280,1024?},??????{?"qsxga",????2560,2048?},??????{?"hsxga",????5120,4096?},??????{?"wvga",??????852,?480?},??????{?"wxga",?????1366,?768?},??????{?"wsxga",????1600,1024?},??????{?"wuxga",????1920,1200?},??????{?"woxga",????2560,1600?},??????{?"wqsxga",???3200,2048?},??????{?"wquxga",???3840,2400?},??????{?"whsxga",???6400,4096?},??????{?"whuxga",???7680,4800?},??????{?"cga",???????320,?200?},??????{?"ega",???????640,?350?},??????{?"hd480",?????852,?480?},??????{?"hd720",????1280,?720?},??????{?"hd1080",???1920,1080?},??????{?"2k",???????2048,1080?},?/*?Digital?Cinema?System?Specification?*/??????{?"2kflat",???1998,1080?},??????{?"2kscope",??2048,?858?},??????{?"4k",???????4096,2160?},?/*?Digital?Cinema?System?Specification?*/??????{?"4kflat",???3996,2160?},??????{?"4kscope",??4096,1716?},??????{?"nhd",???????640,360??},??????{?"hqvga",?????240,160??},??????{?"wqvga",?????400,240??},??????{?"fwqvga",????432,240??},??????{?"hvga",??????480,320??},??????{?"qhd",???????960,540??},??};??通過調用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函數
/**??*?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,如下所示。
[cpp]view plaincopy
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。該函數的定義在前文中已經做過分析,這里再重復一下。定義如下所示。
[cpp]view plaincopy
const?AVOption?*av_opt_next(void?*obj,?const?AVOption?*last)??{??????AVClass?*class?=?*(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_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()的聲明如下所示。
[cpp]view plaincopy
/**??*?@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()的定義,如下所示。
[cpp]view plaincopy
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;??????//注意:offset的使用??????dst?=?(uint8_t*)target_obj?+?o->offset;??????//使用sprintf()轉換成字符串,存入buf??????buf[0]?=?0;??????switch?(o->type)?{??????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_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??????????????*out_val?=?av_strdup("");??????????return?0;??????case?AV_OPT_TYPE_BINARY:??????????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;??????????ret?=?snprintf(buf,?sizeof(buf),?"%"PRIi64":%02d:%02d.%06d",?????????????????????????i64?/?3600000000,?(int)((i64?/?60000000)?%?60),?????????????????????????(int)((i64?/?1000000)?%?60),?(int)(i64?%?1000000));??????????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?0;??}??從av_opt_get()的定義可以看出,該函數首先通過av_opt_find2()查相應的AVOption,然后取出該變量的值,最后通過snprintf()將變量的值轉化為字符串(各種各樣類型的變量都這樣處理)并且輸出出來。? ?可以學到將各種類型轉換成字符串參考:結構體成員管理系統-AVOption
總結
以上是生活随笔為你收集整理的结构体成员管理AVClass AVOption之2AVOption,设置选项值的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。