zoukankan      html  css  js  c++  java
  • FFmpeg filters 分析:简述

    一、概述

    FFmpeg 提供了一种以管道的方式对音视频进行滤镜操作的框架。其也内置了非常多的滤镜,如果这些滤镜还不能满足需求的话也可以自行开发。

    本文主要梳理 FFmpeg 滤镜相关的一些基本概念和 API,为之后对具体内置滤镜的分析做准备。

    二、滤镜图和滤镜简述

    1、什么是滤镜图和滤镜

    滤镜图(Filter graph)是一种包含了多个已连接的滤镜的有向图。源码中,AVFilterGraph 类定义了滤镜图。避免眼花,下文使用 Filter graph 来表示滤镜图。
    从表面上看滤镜是 Filter graph 节点。源码中,使用 AVFilter 类定义滤镜,运行时使用 AVFilterContext 类的实例表示位于 Filter graph 中的滤镜实例。
    通常,滤镜有 0 或多个输入并且有 0 或多个输出——输入输出至少得有 1 个。一个滤镜的输出可以连接到另一个滤镜的输入,多个滤镜连接起来从而构成滤镜链。这里说的输入输出特指视频帧/音频采样在 Filter graph 中的处理流程,不是指整个 FFmpeg 的处理流程。源码中,使用 AVFilterPad 类定义滤镜有哪些输入和输出,运行时使用 AVFilterInOut 类的实例表示滤镜在 Filter graph 中的输入和输出实例。在 Filter graph 中使用 AVFilterLink 将滤镜连接起来。

    截止 FFmpeg 4.3.1 版本,其包含了 460 个滤镜。可在 allfilters.c 这个源文件中搜索 “extern AVFilter” 看看搜索出来的数量。也可以用命令 ffmpeg -filters 查看安装了的 滤镜。

    定义滤镜的源文件在 libavfilter 目录中。一个源文件定义了一或多个滤镜。比如 buffersrc.c 就包含了 buffer 和 abuffer 这两个滤镜, 分别是视频和音频的一种 Source (Source 是特殊的滤镜见下文)。

    备注:可以用命令 ffmpeg -h filter=<名称> 看具体滤镜的使用帮助。

    2、滤镜的分类

    可以使用两种方式对滤镜进行大致的分类。

    从输入输出(Pad)数量的角度可划分为 SourcesSinks 和 Filters。Source 没有输入有 1 个输出,Sink 有 1 个输入没有输出,Filter 有 1 个或多个输入并有 1 个或多个输出。实际上,Source 和 Skin 本质上是特殊的滤镜。有时候为了区分,本文把 Source 和 Sink 之外的滤镜称为 普通滤镜 。

    从滤镜能够处理的媒体类型的角度可划分为三类:Audio 、 Video 和 Multimedia。Audio 分类的滤镜用于处理音频,Video 分类的滤镜包含用于处理视频,而 Multmedia 分类包含用于处理视频或音频的输入以及音频通用的滤镜。特别地,单独提出了 OpenCL Video 和 VAAPI Video 这两个仅用于处理视频的滤镜小类。
    另外,Multimedia 只有 Sources 和普通滤镜,没有 Sinks;OpenCL Video 只有普通滤镜; VAAPI Video 只有一个用于视频编解码的普通滤镜。

    备注:实际上,普通滤镜也可以输入和输出都没有。比如 af_volumedetect 这个滤镜可以获取音频的音量(均方根值)、最大最小音量和音量直方图并以日志形式打印出来,不需要处理数据传递给下一个节点(Pad)。另外,如果输入为 NULL(AVFilte 的 outputs 字段为 NULL) 且有 AVFILTER_FLAG_DYNAMIC_INPUTS 标志则表示有动态地多个输入, 比如 amerge 和 mix等滤镜;如果输出为 NULL(AVFilte 的 inputs 字段为 NULL) 且有 AVFILTER_FLAG_DYNAMIC_OUTPUTS 标志则表示有动态地多个输出,比如 split 和 aplit等滤镜;甚至输入和输出都为 NULL 则表示输入和输出都是动态的,且有 AVFILTER_FLAG_DYNAMIC_INPUTS 和 AVFILTER_FLAG_DYNAMIC_OUTPUTS 两个标志,比如 streamselectastreamselect 和 concat 这三个滤镜——所以严格来说 SourcesSinks 和 Filters 不能简单按输入输出数量来区分,这样做只是考虑一般情况。


    图1:Filter 的分类

    三、滤镜相关类

    FFmpeg 虽然是用 C 语言实现了,其也用到了一些 OOP 思想。在这里直接将这些 C 结构称为类。和滤镜相关的主要类如下。

    说明
    AVFilterGraph Filter graph。
    AVFilter 滤镜的定义。它一种定义类。它的实例包含用于处理命令(process_command,见下文)等操作。
    AVFilterPad 滤镜的输入或输出定义。它一种定义类。它的实例的 filter_frame 指针指向实际的滤镜逻辑 。
    AVFilterContext 滤镜的实例。AVFitler 实例是孤立的对滤镜的描述实例,而 AVFilterContext 实例是滤镜在 Filter graph 中的实例。
    AVFilterLink AVFilterLink 类通过将两个 AVFilterContext 实例的 Pad 放在一起从而实现滤镜的连接。
    AVFilterInOut AVFilterInOut 类是用于对滤镜参数字符串进行语句解析(Parse)时的辅助类。在此过程中会将[] 命名的的名称构造成 AVFilterInOut。它是一个链表结构。

    四、滤镜的定义

    1、AVFilter 类

    AVFilter 可以看做是抽象类或接口。
    定义了 1 个 const AVClass *priv_class; 字段描述了实际的类型。priv_class 指向的类型并非直接“继承”自 AVFilter,它更像包含了”子类”的字段的元数据——而真正的子类是一个虚拟的存在。比如滤镜 ff_af_volume 是一个 AVFrame 型变量,可以想象其是一个虚拟的名为 AVFilterVolume 的子类型的变量。而 AVFilterVolume 的字段定义在 VolumeContext 中。
    定义了 1 个 int priv_size; 字段表示子类的结构大小,用于初始化时分配内存。比如滤镜 ff_af_volume 是 VolumeContext 结构的结果大小。
    定义了 3 个函数指针字段 init 、init_opaque 和 init_dict 类似于面向对象语言中的构造函数,其中 init 看做是默认构造函数, init_opaue 和 init_dict 是构造函数重载。
    定义了 1 个函数指针字段 uninit 类似于面向对象语言中的析构函数。
    特别地,函数指针 preinit 指向的函数在给 AVFilterContext 分配内存后调用,为处理帧同步(framesync)做准备。

    以下是 AVFitler 的不完整定义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    
    // File: libavfilter/avfilter.h
    /**
     * Filter definition. This defines the pads a filter contains, and all the
     * callback functions used to interact with the filter.
     */
    typedef struct AVFilter {
        /**
         * A class for the private data, used to declare filter private AVOptions.
         * This field is NULL for filters that do not declare any options.
         *
         * If this field is non-NULL, the first member of the filter private data
         * must be a pointer to AVClass, which will be set by libavfilter generic
         * code to this class.
         */
        const AVClass *priv_class;
    
        /*****************************************************************
         * All fields below this line are not part of the public API. They
         * may not be used outside of libavfilter and can be changed and
         * removed at will.
         * New public fields should be added right above.
         *****************************************************************
         */
    
        /**
         * Filter pre-initialization function
         *
         * This callback will be called immediately after the filter context is
         * allocated, to allow allocating and initing sub-objects.
         *
         * If this callback is not NULL, the uninit callback will be called on
         * allocation failure.
         *
         * @return 0 on success,
         *         AVERROR code on failure (but the code will be
         *           dropped and treated as ENOMEM by the calling code)
         */
        int (*preinit)(AVFilterContext *ctx);
    
        /**
         * Filter initialization function.
         *
         * This callback will be called only once during the filter lifetime, after
         * all the options have been set, but before links between filters are
         * established and format negotiation is done.
         *
         * Basic filter initialization should be done here. Filters with dynamic
         * inputs and/or outputs should create those inputs/outputs here based on
         * provided options. No more changes to this filter's inputs/outputs can be
         * done after this callback.
         *
         * This callback must not assume that the filter links exist or frame
         * parameters are known.
         *
         * @ref AVFilter.uninit "uninit" is guaranteed to be called even if
         * initialization fails, so this callback does not have to clean up on
         * failure.
         *
         * @return 0 on success, a negative AVERROR on failure
         */
        int (*init)(AVFilterContext *ctx);
    
        /**
         * Should be set instead of @ref AVFilter.init "init" by the filters that
         * want to pass a dictionary of AVOptions to nested contexts that are
         * allocated during init.
         *
         * On return, the options dict should be freed and replaced with one that
         * contains all the options which could not be processed by this filter (or
         * with NULL if all the options were processed).
         *
         * Otherwise the semantics is the same as for @ref AVFilter.init "init".
         */
        int (*init_dict)(AVFilterContext *ctx, AVDictionary **options);
    
        /**
         * Filter uninitialization function.
         *
         * Called only once right before the filter is freed. Should deallocate any
         * memory held by the filter, release any buffer references, etc. It does
         * not need to deallocate the AVFilterContext.priv memory itself.
         *
         * This callback may be called even if @ref AVFilter.init "init" was not
         * called or failed, so it must be prepared to handle such a situation.
         */
        void (*uninit)(AVFilterContext *ctx);
    
        int priv_size;      ///< size of private data to allocate for the filter
    
        /**
         *滤镜initialization function, alternative to the init()
         * callback. Args contains the user-supplied parameters, opaque is
         * used for providing binary data.
         */
        int (*init_opaque)(AVFilterContext *ctx, void *opaque);
    } AVFilter;
    

    上述函数指针都是可 NULL 的,比如 abuffer 滤镜就只实现了 init 和 uninit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    // File: libavfilter/buffersrc.c
    AVFilter ff_asrc_abuffer = {
        .name          = "abuffer",
        .description   = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."),
        .priv_size     = sizeof(BufferSourceContext),
        .query_formats = query_formats,
    
        .init      = init_audio,
        .uninit    = uninit,
    
        .inputs    = NULL,
        .outputs   = avfilter_asrc_abuffer_outputs,
        .priv_class = &abuffer_class,
    };
    

    再比如 abuffersink 滤镜实现了 init_opaque 、 query_formats 和 activate 而没有实现 init 和 uninit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    // File: libavfilter/buffersink.c
    AVFilter ff_asink_abuffer = {
        .name        = "abuffersink",
        .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."),
        .priv_class  = &abuffersink_class,
        .priv_size   = sizeof(BufferSinkContext),
        .init_opaque = asink_init,
    
        .query_formats = asink_query_formats,
        .activate    = activate,
        .inputs      = avfilter_asink_abuffer_inputs,
        .outputs     = NULL,
    };
    

    甚至 acopy 滤镜没有实现上述的任何函数:

    1
    2
    3
    4
    5
    6
    7
    
    // File: libavfilter/af_acopy.c
    AVFilter ff_af_acopy = {
        .name          = "acopy",
        .description   = NULL_IF_CONFIG_SMALL("Copy the input audio unchanged to the output."),
        .inputs        = acopy_inputs,
        .outputs       = acopy_outputs,
    };
    

    上述的 ff_asrc_abuffer 、 ff_asink_abuffer 可以看做是实现了 AVFrame 接口的虚拟子类的实例,而 ff_af_acopy 直接是 AVFilter 的实例。

    然后看看 AVFilter 的其他字段:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    // File: libavfilter/avfilter.h
    typedef struct AVFilter {
        // 滤镜名称
        /**
         *滤镜name. Must be non-NULL and unique among filters.
         */
        const char *name;
    
        // 滤镜描述
        /**
         * A description of the filter. May be NULL.
         *
         * You should use the NULL_IF_CONFIG_SMALL() macro to define it.
         */
        const char *description;
    
        // 输入列表
        /**
         * List of inputs, terminated by a zeroed element.
         *
         * NULL if there are no (static) inputs. Instances of filters with
         * AVFILTER_FLAG_DYNAMIC_INPUTS set may have more inputs than present in
         * this list.
         */
        const AVFilterPad *inputs;
    
        // 输出列表
        /**
         * List of outputs, terminated by a zeroed element.
         *
         * NULL if there are no (static) outputs. Instances of filters with
         * AVFILTER_FLAG_DYNAMIC_OUTPUTS set may have more outputs than present in
         * this list.
         */
        const AVFilterPad *outputs;
    
        // AVFILTER_FLAG_* 枚举类型。
        // AVFILTER_FLAG_DYNAMIC_INPUTS: 用于标示输入的数量是否是动态的,即不是由 inputs 决定的。
        // AVFILTER_FLAG_DYNAMIC_OUTPUTS: 用于标示输出的数量是否是动态的,即不是由 outputs 决定的。
        // AVFILTER_FLAG_SLICE_THREADS: 是否支持将 frame 分帧进行多线程并行处理。
        // AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC: 有些滤镜支持 enable 表达式,用时间线控制滤镜的启用与否。如果判定 enable 的结果为 false,则不会调用定义的 filter_frame 函数就将帧传递给下一个滤镜。
        // AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL: 类似于 AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC 。不用之处在于调用 filter_frame 本身的时候判断,依赖于 AVFilterContext->is_disabled 的值。
        /**
         * A combination of AVFILTER_FLAG_*
         */
        int flags;
    
        // FFmpeg 内部使用。
        int flags_internal; ///< Additional flags for avfilter internal use only.
    
        // Filter graph 是使用滤镜的输入输出 Pad 构造的,AVFitler 的 `AVFitler *next` 字段与之无关。其作用只是为了注册各个 Filter,详见 `allfilters.c` 的 `av_filter_init_next` 函数。
        /**
         * Used by the filter registration system. Must not be touched by any other
         * code.
         */
        struct AVFilter *next;
    } AVFilter;
    

    再然后看看 AVFilte 的其他函数指针:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
    // File: libavfilter/avfilter.h
    typedef struct AVFilter {
        /**
         * Query formats supported by the filter on its inputs and outputs.
         *
         * This callback is called after the filter is initialized (so the inputs
         * and outputs are fixed), shortly before the format negotiation. This
         * callback may be called more than once.
         *
         * This callback must set AVFilterLink.out_formats on every input link and
         * AVFilterLink.in_formats on every output link to a list of pixel/sample
         * formats that the filter supports on that link. For audio links, this
         * filter must also set @ref AVFilterLink.in_samplerates "in_samplerates" /
         * @ref AVFilterLink.out_samplerates "out_samplerates" and
         * @ref AVFilterLink.in_channel_layouts "in_channel_layouts" /
         * @ref AVFilterLink.out_channel_layouts "out_channel_layouts" analogously.
         *
         * This callback may be NULL for filters with one input, in which case
         * libavfilter assumes that it supports all input formats and preserves
         * them on output.
         *
         * @return zero on success, a negative value corresponding to an
         * AVERROR code otherwise
         */
        int (*query_formats)(AVFilterContext *);
    
        /**
         * Make the filter instance process a command.
         *
         * @param cmd    the command to process, for handling simplicity all commands must be alphanumeric only
         * @param arg    the argument for the command
         * @param res    a buffer with size res_size where the filter(s) can return a response. This must not change when the command is not supported.
         * @param flags  if AVFILTER_CMD_FLAG_FAST is set and the command would be
         *               time consuming then a filter should treat it like an unsupported command
         *
         * @returns >=0 on success otherwise an error code.
         *          AVERROR(ENOSYS) on unsupported commands
         */
        int (*process_command)(AVFilterContext *, const char *cmd, const char *arg, char *res, int res_len, int flags);
    
        /**
         * Filter activation function.
         *
         * Called when any processing is needed from the filter, instead of any
         * filter_frame and request_frame on pads.
         *
         * The function must examine inlinks and outlinks and perform a single
         * step of processing. If there is nothing to do, the function must do
         * nothing and not return an error. If more steps are or may be
         * possible, it must use ff_filter_set_ready() to schedule another
         * activation.
         */
        int (*activate)(AVFilterContext *ctx);
    } AVFilter;
    

    activate 在处理数据之前调用进行一些准备工作。如果没有定义该函数,会使用 ff_filter_activate_default 函数。
    query_formats 用于查询输入和输出 Pad 支持的媒体格式。
    process_command 用于处理命令。注意并不是处理数据,命令里可能包含设置某些参数之类的。实际处理数据的是 AVFilterPad 的 filter_frame 指针所指向的函数。而该函数一般在滤镜所在文件进行定义,然后让 AVFilterPad 的 filter_frame 指针指向它。

    2、AVFilterPad 类

    使用 AVFilterPad 描述滤镜的输入和输出,比如描述 buffer 滤镜的输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    // File: libavfilter/buffersrc.c
    tatic const AVFilterPad avfilter_vsrc_buffer_outputs[] = {
        {
            .name          = "default",
            .type          = AVMEDIA_TYPE_VIDEO,
            .request_frame = request_frame,
            .poll_frame    = poll_frame,
            .config_props  = config_props,
        },
        { NULL }
    };
    
    AVFilter ff_vsrc_buffer = {
        // 其他字段
        .outputs   = avfilter_vsrc_buffer_outputs,
        // 其他字段
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    
    // File: libavfilter/internal.c
    /**
     * A filter pad used for either input or output.
     */
    struct AVFilterPad {
        // Pad 名称
        /**
         * Pad name. The name is unique among inputs and among outputs, but an
         * input may have the same name as an output. This may be NULL if this
         * pad has no need to ever be referenced by name.
         */
        const char *name;
    
        // Pad 类型,视频或音频等。
        /**
         * AVFilterPad type.
         */
        enum AVMediaType type;
    
        /** 对于视频,在应用滤镜之前获取视频帧。
         * Callback function to get a video buffer. If NULL, the filter system will
         * use ff_default_get_video_buffer().
         *
         * Input video pads only.
         */
        AVFrame *(*get_video_buffer)(AVFilterLink *link, int w, int h);
    
        /** 对于音频,在应用滤镜之前获取音频帧。
        /**
         * Callback function to get an audio buffer. If NULL, the filter system will
         * use ff_default_get_audio_buffer().
         *
         * Input audio pads only.
         */
        AVFrame *(*get_audio_buffer)(AVFilterLink *link, int nb_samples);
    
        // 应用滤镜
        /**
         * Filtering callback. This is where a filter receives a frame with
         * audio/video data and should do its processing.
         *
         * Input pads only.
         *
         * @return >= 0 on success, a negative AVERROR on error. This function
         * must ensure that frame is properly unreferenced on error if it
         * hasn't been passed on to another filter.
         */
        int (*filter_frame)(AVFilterLink *link, AVFrame *frame);
    
        // 查询可用的数据帧的数量。
        // 目前只存在于 buffer 或 abuffer 这两个滤镜中。
        /**
         * Frame poll callback. This returns the number of immediately available
         * samples. It should return a positive value if the next request_frame()
         * is guaranteed to return one frame (with no delay).
         *
         * Defaults to just calling the source poll_frame() method.
         *
         * Output pads only.
         */
        int (*poll_frame)(AVFilterLink *link);
    
        // 请求数据帧,以备滤镜处理。
        /**
         * Frame request callback. A call to this should result in some progress
         * towards producing output over the given link. This should return zero
         * on success, and another value on error.
         *
         * Output pads only.
         */
        int (*request_frame)(AVFilterLink *link);
    
        // 配置属性,比如视频的高宽。注意这不是格式属性,滤镜之间协商格式使用的是在调用该回调之前的 query_formats 回调。
        /**
         * Link configuration callback.
         *
         * For output pads, this should set the link properties such as
         * width/height. This should NOT set the format property - that is
         * negotiated between filters by the filter system using the
         * query_formats() callback before this function is called.
         *
         * For input pads, this should check the properties of the link, and update
         * the filter's internal state as necessary.
         *
         * For both input and output filters, this should return zero on success,
         * and another value on error.
         */
        int (*config_props)(AVFilterLink *link);
    
        // 是否需要 FIFO 队列。如果为 true,则会在本滤镜之前插入一个 fifo 或 afifo 滤镜。
        /**
         * The filter expects a fifo to be inserted on its input link,
         * typically because it has a delay.
         *
         * input pads only.
         */
        int needs_fifo;
    
        // 是否需要将 AVFrame 设置为可写。如果 AVFrame 本是可写,则会拷贝数据创建新的 AVFrame。
        /**
         * The filter expects writable frames from its input link,
         * duplicating data buffers if needed.
         *
         * input pads only.
         */
        int needs_writable;
    };
    

    五、Filter graph 的创建和释放

    1、创建 Filter graph

    调用 avfilter_graph_alloc 函数创建 Filter graph,除了给 AVFilterGraph 结构也会给其内部使用的 AVFilterGraphInternal 型的 internal 字段分配内存。然后设置 AVOption 集的初始值。类似于面向对象语言的默认构造函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    // File: libavfilter/avfiltergraph.c
    AVFilterGraph *avfilter_graph_alloc(void)
    {
        AVFilterGraph *ret = av_mallocz(sizeof(*ret));
        if (!ret)
            return NULL;
    
        ret->internal = av_mallocz(sizeof(*ret->internal));
        if (!ret->internal) {
            av_freep(&ret);
            return NULL;
        }
    
        ret->av_class = &filtergraph_class;
        av_opt_set_defaults(ret);
        ff_framequeue_global_init(&ret->internal->frame_queues);
    
        return ret;
    }
    

    2、释放 Filter graph

    在使用 Filter graph 完成后,调用 avfilter_graph_free 函数以释放其内存。

    六、滤镜(AVFilterContext)的创建和释放

    AVFilterContext 实例是滤镜在 Filter graph 中的实例。它包含一个 AVFilter 实例——当然不同的滤镜实现不同。比如对于 overlay 这个滤镜,AVFilterContext 的 AVFilter 指针指向的是 ff_vf_overlay 对象。

    1、创建滤镜

    调用 avfilter_graph_create_filter 创建滤镜。

    2、释放滤镜

    在释放 Filter graph 的时候会将滤镜释放,无需手工释放。

    七、Filter graph 语句解析(Parse)

    AVFilterInOut 是用于对滤镜参数字符串进行语句解析(Parse)时的辅助类。在此过程中会将[] 命名的的名称构造成 AVFilterInOut。它是一个链表结构。
    在使用 FFmpeg 滤镜 API (比如官方的 filter_video 这个 Demo) 时,也会手工创建以将 Source 和 Sink 与其他滤镜连接起来。
    AVFilterLink 通过将两个滤镜的 Pad 放在一起从而实现滤镜的连接。滤镜相连是通过 AVFilterLink 对象而不是 AVFilterContext 或 AVFilter 等对象。

    1、AVFilterInOut 类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    // File: libavfilter/avfilter.h
    /**
     * A linked-list of the inputs/outputs of the filter chain.
     *
     * This is mainly useful for avfilter_graph_parse() / avfilter_graph_parse2(),
     * where it is used to communicate open (unlinked) inputs and outputs from and
     * to the caller.
     * This struct specifies, per each not connected pad contained in the graph, the
     * filter context and the pad index required for establishing a link.
     */
    typedef struct AVFilterInOut {
        /** unique name for this input/output in the list */
        char *name;
    
        /** filter context associated to this input/output */
        AVFilterContext *filter_ctx;
    
        /** index of the filt_ctx pad to use for linking */
        int pad_idx;
    
        /** next input/input in the list, NULL if this is the last */
        struct AVFilterInOut *next;
    } AVFilterInOut;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    // File: libavfilter/avfilter.h
    /**
     * A link between two filters. This contains pointers to the source and
     * destination filters between which this link exists, and the indexes of
     * the pads involved. In addition, this link also contains the parameters
     * which have been negotiated and agreed upon between the filter, such as
     * image dimensions, format, etc.
     *
     * Applications must not normally access the link structure directly.
     * Use the buffersrc and buffersink API instead.
     * In the future, access to the header may be reserved for filters
     * implementation.
     */
    struct AVFilterLink {
        AVFilterContext *src;       ///< source filter
        AVFilterPad *srcpad;        ///< output pad on the source filter
    
        AVFilterContext *dst;       ///< dest filter
        AVFilterPad *dstpad;        ///< input pad on the dest filter
    
        enum AVMediaType type;      ///< filter media type
    
        // ...Others
    };
    

    3、Filter graph 语句解析

    调用 avfilter_graph_parse_ptr 等方法对 Filter graph 的字符串参数进行语句解析。

    参考资料

    FFmpeg filters 官网文档
    FFmpeg 从入门到精通
    FFmpeg Filter深度应用
    FFmpeg原始帧处理-滤镜API用法详解
    [ffmpeg] 多输入滤波同步方式(framesync)
    ffmpeg filter过滤器 基础实例及全面解析

    https://blog.tubumu.com/2020/11/01/ffmpeg-filters-description/

  • 相关阅读:
    02 日志系统: 一条SQL 更新语句是如何执行的
    MySql 实战45讲笔记 : 01
    爬取boss直聘全国招聘数据并做可视化
    爬取豆瓣电影-长津湖短评
    尝试破解压缩文件
    将webp格式的图像批量转化成jpg
    将JPG格式图片转换成PNG格式
    几个学习编程的游戏网站
    检测网址连接有效性
    监控设备电池电量并发送通知
  • 原文地址:https://www.cnblogs.com/lidabo/p/15428232.html
Copyright © 2011-2022 走看看