新手学习FFmpeg - 经过API实现可控的Filter调用链

虽然经过声明[x][y]avfilter=a=x:b=y;avfilter=xxx的方式能够建立一个可用的Filter调用链,而且在绝大多数场合下这种方式都是靠谱和实用的。 但若是想精细化的管理AVFilter调用链,例如根据某些条件来动态生成AVFilter Graph。这种声明方式就不太灵活(也能够经过if判断来动态组装字符串,若是你很是喜欢这种字符串声明方式,到此为止不在建议你往下阅读了)。git

首先快速温习一下,如何建立一个AVFilter Graphgithub

+-------+             +---------------------+         +---------------+
             |buffer |             |Filter ..... Filter N|         |   buffersink  |
 ----------> |    |output|------>|input|            |output|---> |input|           |-------->
             +-------+             +---------------------+         +---------------+

建立三部曲:web

  1. 初始化bufferbuffersink
  2. 初始化其它filter
  3. 设定Filter Graph的Input和Output。

其中bufferbuffersink分别表明Graph的起始和结束。shell

而后快速封装args也就是movie=t.png[wm];[in][wm]overlay=10:20[out]这样的filter-complex命令。 并且经过avfilter_graph_parse_ptr完成中间filter的初始化,restful

最后指定各个filter的input和output,一个graph就算搞定了。函数

好,下面来看如何经过API精细化生成AVFilter Graph。 生成下面的Graph:rest

+-------+             +---------------------+         +---------------+
             |buffer |             |       Filter        |         |   buffersink  |
 ----------> |    |output|------>|input|    Fade      |output|---> |input|           |-------->
             +-------+             +---------------------+         +---------------+

首先初始化各个AVFilter。全部的AVFilter的初始化均可以简化为两步操做:code

  • 经过avfilter_get_by_name查找指定的AVFilter
  • 经过avfilter_graph_create_filter初始化AVFilterContext

AVcodecAVCodecContext的关系同样, 全部的AVFilter的执行都依靠对应的AVFilterContext(在ffmpeg开发中,每一个组件都会对应一个上下文管理器,由这个上下文管理器封装各类参数而后调用组件执行)。server

经过avfilter_get_by_name生成AVFilter实例以后,紧跟着就须要调用avfilter_graph_create_filter初始化上下文管理器。开发

按照下面的流程,依次初始化三个AVFilter:

buffer_src = avfilter_get_by_name("buffer");
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffer_src, "in", args, NULL, filter_graph);

这里重点聊一下avfilter_graph_create_filter。 下面是函数原型:

int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
                                 const char *name, const char *args, void *opaque,
                                 AVFilterGraph *graph_ctx);

filt_ctx表示这个AVFilter的上下文管理器。

name代表的是AVFilter在Graph中的名称,这个名称叫啥不重要但必须惟一。 例如Fade AVFilter就能够叫作fade1,fade2或者ifade等等。

args则是这个AVFilter的参数, 注意仅仅是这个AVFilter的参数,不是整个graph的参数。再拿Fade举例,args就能够是t=in:st=3:d=3

opaque通常给NULL就能够了。

初始完这三个AVFilter以后,就进入到本次文档的重点: avfilter_link.

int avfilter_link(  AVFilterContext *   src,
                    unsigned    srcpad,
                    AVFilterContext *   dst,
                    unsigned    dstpad
)

avfilter_link分别用来连接两个AVFilter(传说中的一手托两家)。 srcdst分别表示源Filter和目标Filter。 srcpad表示src第N个输出端, dstpad表示dst第N个输入端。 若是很差理解,直接看下面的图:

+-------------+                  +-------------+
    |  src      srcpad 1 ----->   dstpad 3    dst  |
    |           srcpad 2 ----->   dstpad 2         |
    |           srcpad 3 ----->   dstpad 1         |
    +-------------+                  +-------------+

上图假设srcdst分别有三个输出端和三个输入端(不是全部avfilter都有这么多的输入输出端,像fade只有一个,但overlay就有多个)。

srcpaddstpad表示的就是输出/输入端的序号。假如将buffer第一个输出端对应fade第一个输入端。 那么就应该这么写:

avfilter_link(buffersrc_ctx, 0, ifade_ctx, 0);

而后将fade的第一个输出端对应buffersink的输入端,就这么写:

avfilter_link(ifade_ctx, 0, buffersink_ctx, 0);

而所谓的精细化就是在这里体现的,经过代码的逻辑判断,能够动态的组合不一样的AVFilter生成不一样的Filter Graph。而且还能够组合不一样的输入/输出端。

本次代码示例能够参考ifilter。同时也能够参考 ffmpeg-go-server(一个尝试为ffmpeg提供restful API的web server)。

相关文章
相关标签/搜索