FFmpeg开发教程(3) --- 过滤器使用整体流程

       本系列文章居于FFMpeg源码4.1版本,因此,有些流程和老版本会有稍微差别(源码我做了详细注释,有需要请在下方评论留言,写明邮箱,我会统一发给你们)。   

       讲了FFMpeg中过滤器涉及到的数据结构和相关的函数调用说明,我们看下在我们的程序中使用FFMpeg过滤器的整体流程是什么样的,具体有什么步骤,要注意什么细节。

       下面这幅图从整体上说明了FFMpeg过滤器的使用关系图。

       FFmpeg开发教程(3) --- 过滤器使用整体流程_第1张图片

       首先,我们对上图先做下说明,理解下图中每个步骤的关系,然后,才从代码的角度来给出其使用的步骤。

       1. 最顶端的AVFilterGraph,这个结构前面介绍过,主要管理加入的过滤器,其中加入的过滤器就是通过函数avfilter_graph_create_filter来创建并加入,这个函数返回是AVFilterContext(其封装了AVFilter的详细参数信息)。

       2. buffer和buffersink这两个过滤器是FFMpeg为我们实现好的,buffer表示源,用来向后面的过滤器提供数据输入(其实就是原始的AVFrame);buffersink过滤器是最终输出的(经过过滤器链处理后的数据AVFrame),其它的诸如filter 1 等过滤器是由avfilter_graph_parse_ptr函数解析外部传入的过滤器描述字符串自动生成的,内部也是通过avfilter_graph_create_filter来创建过滤器的。

       3. 上面的buffer、filter 1、filter 2、filter n、buffersink之间是通过avfilter_link函数来进行关联的(通过AVFilterLink结构),这样子过滤器和过滤器之间就通过AVFilterLink进行关联上了,前一个过滤器的输出就是下一个过滤器的输入,注意,除了源和接收过滤器之外,其它的过滤器至少有一个输入和输出,这很好理解,中间的过滤器处理完AVFrame后,得到新的处理后的AVFrame数据,然后把新的AVFrame数据作为下一个过滤器的输入。

       4. 过滤器建立完成后,首先我们通过av_buffersrc_add_frame把最原始的AVFrame(没有经过任何过滤器处理的)加入到buffer过滤器的fifo队列。

       5. 然后调用buffersink过滤器的av_buffersink_get_frame_flags来获取处理完后的数据帧(这个最终放入buffersink过滤器的AVFrame是通过之前创建的一系列过滤器处理后的数据)。

       使用流程图就介绍到这里,下面结合上面的使用流程图详细说下FFMpeg中使用过滤器的步骤,这个过程我们分为三个部分:过滤器构建、数据加工、资源释放。

     过滤器构建:

       1)分配AVFilterGraph

             AVFilterGraph* graph = avfilter_graph_alloc();

       2)创建过滤器源

             char srcArgs[256] = {0};

             AVFilterContext *srcFilterCtx;

             AVFilter* srcFilter = avfilter_get_by_name("buffer");

             avfilter_graph_create_filter(&srcFilterCtx, srcFilter ,"out_buffer", srcArgs, NULL, graph);

       3)创建接收过滤器

             AVFilterContext *sinkFilterCtx;

             AVFilter* sinkFilter = avfilter_get_by_name("buffersink");

             avfilter_graph_create_filter(&sinkFilterCtx, sinkFilter,"in_buffersink", NULL, NULL, graph);

       4)生成源和接收过滤器的输入输出

             这里主要是把源和接收过滤器封装给AVFilterInOut结构,使用这个中间结构来把过滤器字符串解析并链接进graph,主要代码如下:

             AVFilterInOut *inputs = avfilter_inout_alloc();

             AVFilterInOut *outputs = avfilter_inout_alloc();

             outputs->name       = av_strdup("in");
             outputs->filter_ctx = srcFilterCtx;
             outputs->pad_idx    = 0;
             outputs->next       = NULL;

             inputs->name        = av_strdup("out");
             inputs->filter_ctx  = sinkFilterCtx;
             inputs->pad_idx     = 0;
             inputs->next        = NULL;

             这里源对应的AVFilterInOut的name最好定义为in,接收对应的name为out,因为FFMpeg源码里默认会通过这样个name来对默认的输出和输入进行查找。

       5)通过解析过滤器字符串添加过滤器

             const *char filtergraph = "[in1]过滤器名称=参数1:参数2[out1]";

             int ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, NULL);

             这里过滤器是以字符串形式描述的,其格式为:[in]过滤器名称=参数[out],过滤器之间用,或;分割,如果过滤器有多个参数,则参数之间用:分割,其中[in]和[out]分别为过滤器的输入和输出,可以有多个。

       6)检查过滤器的完整性

             avfilter_graph_config(graph, NULL);

     数据加工

       1)向源过滤器加入AVFrame

             AVFrame* frame; // 这是解码后获取的数据帧

             int ret = av_buffersrc_add_frame(srcFilterCtx, frame);
       2)从buffersink接收处理后的AVFrame

             int ret = av_buffersink_get_frame_flags(sinkFilterCtx, frame, 0);

             现在我们就可以使用处理后的AVFrame,比如显示或播放出来。

     资源释放


       使用结束后,调用avfilter_graph_free(&graph);释放掉AVFilterGraph类型的graph。

       到此,ffmpeg中使用过滤器的整个流程说完了,有些地方可能说得不是太到位,欢迎大家提出意见,一起讨论。

 

       本系列文章均为原创,主要总结作者多年在软件行业的一些经验,和大家共同学习、进步,转载请注明出处,谢谢!

 

你可能感兴趣的:(FFmpeg开发教程(3) --- 过滤器使用整体流程)