FFMPEG Filters模块调用概述

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012188065/article/details/88635942

以 ffplay -i linmeimei.rm -vf “trim=duration=5[tmp], [tmp]reverse”
详细说明 avfilter的使用步骤, 内部架构,以及数据的流向.

第一步: 注册所有支持的Filters

avfilter_register_all();

注意: 内部有锁保护机制,多个线程同时调用时,外部不需要再加锁保护.

第二步: 创建 filtergraph 的上下文

简单代码如下:

AVFilterGraph *mFilterGraph = avfilter_graph_alloc(void);

图解如下:
1

注: 彩色部分为新增部分, 黑色部分为已存在部分,下同.

第三步:创建 filtergraph的输入输出filter, 即 buffer 和 buffersink

简单代码如下:

AVFilterContext* mFilterIn;
AVFilterContext* mFilterOut;

avfilter_graph_create_filter(&mFilterIn,
        avfilter_get_by_name("buffer"),
        "ffplay_buffer", buffersrc_args, NULL, mFilterGraph);

avfilter_graph_create_filter(&mFilterOut,
        avfilter_get_by_name("buffersink"),
        "ffplay_buffersink", NULL, NULL, mFilterGraph);

注: buffersrc_args 为传递给buffer filter的创建参数字符串,例:
“video_size=352x288:pix_fmt=0:time_base=1/1000:pixel_aspect=0/1”
这四个参数为必传参数,否则会无法创建 buffer filter.
图解如下:
2

从图中可以看出, 经过此步骤,当前 mFilterGraph中共有两个 AVFilterContext,分别为
ff_vsrc_buffer filter (i.e. buffer, 之后简称 vsrc)和
ff_vsink_buffer filter (i.e. buffersink, 之后简称 vsink);
存放于 mFilterGraph的filters数组中

vsrc只有 outputs, 为avfilter_vsrc_buffer_outputs
(输入默认为解码后的YUV数据,没有上一级的filter, 所以无须设置inputs);
vsink只有 inputs, 为 avfilter_vsink_buffer_inputs
(输出默认为要渲染的YUV数据,没有下一级的filter, 所以无须设置outputs)
其他的filter 均有特定的 inputs 和 outputs;

第四步: 构造 AVFilterInOut,并给解析传给mFilterGraph的字符串参数

简单代码如下:

AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();

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

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

avfilter_graph_parse_ptr(mFilterGraph, cmd_args, &inputs, &outputs, NULL);

其中 cmd_args为字符串参数 “trim=duration=5[tmp], [tmp]reverse”
注: outputs->name 必须为 “in”, inputs->name 必须为 “out”, 这是与ffmpeg内部的代码对应的,
否则会导致 avfilter_graph_parse_ptr 报错;
上层传给filtergraph的参数中, 不能出现 name为 “in” 或者 “out” 的 AVFilterInOut,
否则会与这里定义的 outputs, inputs的name冲突, 引起 filtergraph解析失败.
图示如下:
在这里插入图片描述

从图中可以以下几点:

  1. 从字符串中解析出trim和reverse filter,并追加到 mFilterGraph 的 filters数组里
  2. 解析出字符串参数中的 [tmp] AVFilterInOut 以及先前设置的 [in] 和 [out] AVFilterInOut.
    所以传给filtergraph的字符串参数中,不允许出现name为 “in” 或者 “out” 的 AVFilterInOut.
  3. AVFilterLink用来描述前后两个filter之间的关系
    以 vsrc和 trim为例来说明:
    二者通过 AVFilterLink *Link1来建立关系,
    Link1的 src filter 和 dst filter 分别指向 vsrc 和 trim;
    Link1 的 srcpad 和 dstpad 分别指向 vsrc的 outputs[0] (即avfilter_vsrc_buffer_outputs[0] )
    和 trim 的 inputs[0] (即 trim_inputs[0]);
    vsrc中的output数组也会装载 Link1 (vsrc filter只有一个输入,所以此处只有一个AVFilterLink).
    trim中的input数组也会装载Link1(trim filter只接受一个输入,所以此处只有一个AVFilterLink).
    一个filter可以有一个AVFilterLink 或者 多个 AVFilterLink
    但 一个AVFilterLink尽可以关联一个输入filter和一个输入filter

–trim–>[tmp]–reverse–> 以及 --reverse–>[out]–vsink–> 之间的 AVFilterLink类似 Link1, 不再赘述.

综上, avfilter_vsrc_buffer_outputs[0] (AVFilterPad ) 和 trim_inputs[0] (AVFilterPad) 通过 AVFilterLink * Link1 建立关系,
trim_inputs[0] 和 trim_outputs[0] 通过 trim filter建立联系.
而AVFilterPad 结构体中又包含着 filter_frame, request_frame 等回调函数,
可以实现YUV数据的处理和传递,
经过 AVFilterLink 以及 AVFilterContext 建立的关系链,
YUV数据通过此关系链不断被 filter 处理, 直到最后 vsink输出.

第五步, 配置 filterGraph

简单代码如下:

avfilter_graph_config(mFilterGraph, NULL);

mFilterGraph根据之前建立的数据结构,以及传递的配置参数做配置
比如说, 传递给 vsrc的视频宽度w, 高度h, time_base等信息,
通过数据结构传给给各个filter, 直至 vsink
根据传递给 vsrc filter的 像素格式 pix_fmt, 以及 filtergraph中各个 filter所支持的 format,
共同协商出一个最终的format.

第六步, filter处理

简单代码如下:

status_t err = av_buffersrc_add_frame(mFilterIn, mFrame);
while (ret >= 0) {
        ret = av_buffersink_get_frame_flags(filt_out, frame, 0);
        if (ret < 0) {
            ret = 0;
            break;
        }
}

整体图如下:
在这里插入图片描述

可以看出, AVFilterLink中有一缓存fifo, 用于存放前一个filter的输出数据和后一个filter的输入数据;
调用一次 av_buffersrc_add_frame, 会将存储YUV数据的AVFrame放置于 vsrc的 outputs中, 即第一个AVFilterLink的fifo中, 等待接下来的filter去处理.

调用一次AV_buffersink_get_frame_flags后,
在整个FilterGraph中,根据每一个filter的input_pads中的filter_frame函数,
去处理此AVFrame数据,并存放与下一个 AVFilterLink的 fifo中,等待下一个 filter去处理,
直至存放到最后一个AVFilterLink的fifo中

再调用一次AV_buffersink_get_frame_flags, 才可以将最后一个AVFilterLink中的 AVFrame取走,供渲染使用.

猜你喜欢

转载自blog.csdn.net/u012188065/article/details/88635942
今日推荐