虽然通过声明[x][y]avfilter=a=x:b=y;avfilter=xxx的方式可以创建一个可用的Filter调用链,并且在绝大多数场合下这种方式都是靠谱和实用的。 但如果想精细化的管理AVFilter调用链,例如根据某些条件来动态生成AVFilter Graph。这种声明方式就不太灵活(也可以通过if判断来动态组装字符串,如果你非常喜欢这种字符串声明方式,到此为止不在建议你往下阅读了)。
首先快速温习一下,如何创建一个AVFilter Graph。
+-------+ +---------------------+ +---------------+
|buffer | |Filter ..... Filter N| | buffersink |
----------> | |output|------>|input| |output|---> |input| |-------->
+-------+ +---------------------+ +---------------+
创建三部曲:
buffer和buffersink。其中buffer和buffersink分别代表Graph的起始和结束。
然后快速封装args也就是movie=t.png[wm];[in][wm]overlay=10:20[out]这样的filter-complex命令。 而且通过avfilter_graph_parse_ptr完成中间filter的初始化,
最后指定各个filter的input和output,一个graph就算搞定了。
好,下面来看如何通过API精细化生成AVFilter Graph。 生成下面的Graph:
+-------+ +---------------------+ +---------------+
|buffer | | Filter | | buffersink |
----------> | |output|------>|input| Fade |output|---> |input| |-------->
+-------+ +---------------------+ +---------------+
首先初始化各个AVFilter。所有的AVFilter的初始化都可以简化为两步操作:
avfilter_get_by_name查找指定的AVFilteravfilter_graph_create_filter初始化AVFilterContext同AVcodec和AVCodecContext的关系一样, 所有的AVFilter的执行都依靠对应的AVFilterContext(在ffmpeg开发中,每个组件都会对应一个上下文管理器,由这个上下文管理器封装各种参数然后调用组件执行)。
通过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(传说中的一手托两家)。 src和dst分别表示源Filter和目标Filter。 srcpad表示src第N个输出端, dstpad表示dst第N个输入端。 如果不好理解,直接看下面的图:
+-------------+ +-------------+
| src srcpad 1 -----> dstpad 3 dst |
| srcpad 2 -----> dstpad 2 |
| srcpad 3 -----> dstpad 1 |
+-------------+ +-------------+
上图假设src和dst分别有三个输出端和三个输入端(不是所有avfilter都有这么多的输入输出端,像fade只有一个,但overlay就有多个)。
而srcpad和dstpad表示的就是输出/输入端的序号。假如将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)。
新手学习FFmpeg - 通过API实现可控的Filter调用链
原文:https://www.cnblogs.com/vikings-blog/p/11613245.html