FFMPEG 3.4.2 - ffmpeg源代码分析 (一)

1 命令行解析

1.1 命令行例子

  • 命令行包括三个部分:输入参数,输出参数,和全局选项。
  • -i /home/ron/music/avm.mp4是输入参数,a.mp4是输出参数。输入/输出参数可以有专属的选项,这些选项应该紧挨着放在输入输出参数前面。如-vf “split [main][tmp]...[main][flip]”就是输出参数a.mp4的选项。
  • 全局选项的位置不需要限定,因为选项是以选项名字查找的。
  • 可以有多组输入参数和多组输出参数。

1.2 解析命令行

split_commandline()负责解析命令行。

int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options,
const OptionGroupDef *groups, int nb_groups);

解析的结果保存在OptionParseContext中。解析时需要参考OptionDef和OptionGroupDef。OptonDef[]是支持ffmpeg的选项列表,OptionGroupDef[]是支持的组列表,包括输入类和输出类,前者以-i开头,加上设备名。后者只有文件名。

下面的类图显示了涉及的类:

FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第1张图片
  • OptionGroup保存一个输入(或输出)和它的选项列表。Option表示一个选项。
  • OptionParseContext中包括多个OptionGroup。全局选项保存在global_opts中。所有输入设备的选项保存在一个OptionGroupList实例中,所有输出设备的选项保存在另一个实例中。两者合起来组成数组groups.

1.3 split_commandline()

split_commandline()在一个循环中解析命令行,主要涉及如下函数。

函数 功能
find_option() 查询支持的option列表,检查当前元素是否一个option
add_option() 将option加入一个临时组。(因为option先于group出现,还不知道应该加入到哪个组。)
match_group_separator() 查询支持的group列表,检查当前元素是否是一个Group
finish_group() 设置临时组的参数,并用它填充OptionParseContext.groups(现在知道应该加入哪个组了)

1.4 parse_optgroup()

parse_optgroup()负责将OptionGroup转换成OptionsContext。

int parse_optgroup(void *optctx, OptionGroup *g);
  • OptionGroup保存的选项值是字符串,而OptionsContext保存的值是由OptionDef定义的实际类型。parse_optgroup()的第一个参数optctx实际上是OptonsContext。

下面的类图显示了涉及的类:

FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第2张图片
  • SpecifierOpt保存实际类型的选项。OptionsContext有若干个SpecifierOpt数组的成员。每个specfier数组保存一类选项。如filters保存”filter”选项。但filter可以是”filter:v”,属于video,也可以是“filter:a”,属于audio。SpecifierOpt.specifier成员就是用来标记这个选项应该属于谁的。对于”filter:v”,SpecifierOpt.specifier就是”v”。
  • 这里顺便提一下AVDictionary。解析过程没有用到它。用户设置的选项可能不成功,而选项的最终值会保存在这里。用av_dict_set()函数设置它。

1.5 parse_optgroup()

parse_optgroup()函数遍历OptonGroup中的Option,调用write_option()将其写入OptionsContext。

  • 对于基本的选项,它的OptionDef中定义了它在OptionsContext的偏移,所以将字符串转化后,直接写入就好了。比如”filter:v”。
  • 有的选项可能是其他选项的别名。这时它的OptionDef指定了一个回调函数。这个函数会重定向到所指向的选项上去。如”vf”就是”filter:v”的别名,它的OptionDef指定了回调函数opt_video_filter()。这个函数会调用parse_option()和find_option()查找”filter:v”对应的OptionDef,并再次调用write_option()。
  • 全局选项。它的OptionDef也定义了一个回调函数。这个函数直接设置全局变量。如loglevel,它的OptionDef定义了opt_loglevel()。这个函数调用av_log_set_level()设置日志输出等级。

1.6 MATCH_PER_XXX_OPT()

宏MATCH_PER_TYPE_OPT()和MATCH_PER_STREAM_OPT()用于从OptionsContext读值。

  • 前者指定参数mediatype,用它跟OptionsContext.spcifier比较,找出option并读出。后者指定参数AVStream,调用check_stream_specifier(),用AVStream的属性与OptionContext.specifier匹配,找出option并读出。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第3张图片

2 vf选项解析

2.1 avfilter_graph_parse2()

avfilter_graph_parse2()负责解析vf选项内容。

int avfilter_graph_parse2 (AVFilterGraph *graph, 
const char *filters,
AVFilterInOut **inputs,
AVFilterInOut **outputs);

输入参数filters是vf选项内容。输出参数Inputs是导出的输入接口,outputs是filters导出的输出接口。

2.2 filters

如下是filters的一个例子。它来自ffmpeg的文档:

http://ffmpeg.org/ffmpeg-filters.html#Filtergraph-description

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

对应FilterGraph的结构示意图如下。 矩形框内是vf的内容对应的部分。其中split应该导出到inputs中,overlay应该导出到outputs中。

FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第4张图片

2.3 vf术语

描述vf的解析过程需要使用一些术语。其中一部分是关于vf语法的,另外一部分是关于生成的FilterGraph结构的。

上图标出了vf语法的术语。

  • 过滤器。过滤器用红色标出,包括它的名字和参数。如”split”,只有名字。又如”overlay=0:H/2”,overlay是名字,”0:H/2”是参数。名字和参数用 = 连接。
  • 位置点。有两类位置点,有名的和无名的。有名位置点用绿色标出,名字用 [] 包住,如main, flip, tmp。无名位置点不必标出。
  • 路径。路径是一条从位置点开始,中间过滤器和位置点交错,在位置点结束的处理流程。多条路径组成整个filtergraph。中间的位置点都是无名的,开始和结束的位置点应该是有名的,除非这条路径在filtergraph的开始和结束位置。路径之间用 ; 隔开。如 [tmp] crop=iw:ih/2:0:0, vflip [flip]。以tmp开始,中间包括crop和vflip和一个无名位置点,在flip结束。有名位置点是该路径与其他路径的连接点,所以需要有一个名字来标记,而无名位置点只存在该路径内部的两个过滤器之间,是隐含的,所以不需要名字。

下图是FilterGraph的结构图。

FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第5张图片
  • FilterGraph是由一系列的过滤器,Pad和Pad Link构成的。
  • 过滤器来自FilterGraph语法中的过滤器,它有一组In Pad和一组OutPad, Pad与语法中的位置点对应。过滤器之间通过Pad联系,Pad Link用来将一个In Pad连接到一个OutPad。Pad Link没有对应的语法元素。
  • Input/Output用于解析过程,也用于保存整个解析的结果,以返回给调用者。open_inputs标记当前还没有解析(与其他OutPad连接)的InPad,open_outputs标记当前还没有解析的OutPad,curr_inputs标记当前将要解析的InPad。

2.4 avfilter_graph_parse2()

avfilter_graph_parse2()主要调用四个函数进行解析。

函数 功能
parse_input() 选取若干open_outputs,以更新curr_inputs
parse_filter() 解析过滤器
link_filter_inouts() 将新的过滤器连入当前的curr_inputs,并更新curr_inputs
parse_output() 结束当前的curr_inputs,加入open_outputs。

2.5 解析过程

下面的图描述了上述语法的解析过程。图上部的xxx()是当前步骤调用的函数,下面的字符串是语法,当前变色的部分是正在解析的部分。

  • 解析split
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第6张图片
  • 将split连入curr_inputs,当前的curr_inputs原来为空,所以更新为split的两个out pads。Pads的数量可以来自split class指定的默认值,或者split的参数。这里是前者。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第7张图片
  • 给split的outpads命名,以便后面引用。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第8张图片
  • 从open_outputs中选取tmp位置开始一段分支路径。将tmp标记为curr_inputs。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第9张图片
  • 解析crop。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第10张图片
  • 将crop连入curr_inputs。更新curr_inputs,指向crop的out pads。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第11张图片
  • 解析vflip。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第12张图片
  • 将vflip连入curr_inputs。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第13张图片
  • 给vflip的out pads命名为flip,以便后面引用。当前分支路径结束。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第14张图片
  • 从open_outputs中选取main和flip,开始新路径。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第15张图片
  • 解析overlay。Overlay的pads来自overlay class的默认值。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第16张图片
  • 将overlay连入curr_inputs。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第17张图片
  • 没有更多的语法元素了,结束前将curr_inputs标记为open_outputs。注意split的in pads一直没有解析,所以它是open_inputs。将open_inputs和open_outputs返回调用者。
FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第18张图片

2.6 FilterGraph类

下面的类图显示了FilterGraph各元素对应的类。

FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第19张图片
  • AVFilterContext表示过滤器。AVFilter是它的属性类。
  • AVFilterPad是Pad类。一个AVFilterContext实例包括AVFilterPad的一组In Pad实例和一组Out Pad实例。AVFilterLink是Pad Link类,它连接两个AVFilterPad实例。
  • AVFilterLink有一个FFFrameQueue,用于保存过滤的中间结果。这时一个frame的数据通道。
  • AVFilterContext有一个空间,用于保存该特定类型Filter的私有信息,可以是CropContext,SplitContext或其他filter的一种。
  • AVFilterInOut用于解析过程标记open_iputs, open_ouputs和curr_inputs。它没有直接引用AVFilterPad,而是引用AVFilterContext,和用序号间接指向AVFilterPad。
  • AVFilterGraph和FilterGraph是代表整个FilterGraph的容器类。

2.7 avfilter_graph_parse2()

下图是avfilter_graph_parse2()的函数调用关系。

FFMPEG 3.4.2 - ffmpeg源代码分析 (一)_第20张图片

相关链接

FFMPEG 3.4.2 - ffmpeg源代码分析 (一)
FFMPEG 3.4.2 - ffmpeg源代码分析 (二)
FFMPEG 3.4.2 - ffmpeg源代码分析 (三)
FFMPEG 3.4.2 - ffmpeg源代码分析 (四)- x264
FFMPEG 3.4.2 - ffplay源代码分析 (一)
FFMPEG 3.4.2 - ffplay源代码分析 (二)
FFMPEG 3.4.2 - ffplay源代码分析 (三)

你可能感兴趣的:(FFMPEG 3.4.2 - ffmpeg源代码分析 (一))