音视频基础 (九)---FFmpeg过滤器框架

时间:2024-04-04 06:59:31

ffmpeg的filter⽤起来是和Gstreamer的plugin是⼀样的概念,通过avfilter_link,将各个创建好的filter按
⾃⼰想要的次序链接到⼀起,然后avfilter_graph_config之后,就可以正常使⽤。
⽐较常⽤的滤镜有:scale、trim、overlay、rotate、movie、yadif。scale 滤镜⽤于缩放,trim 滤镜⽤
于帧级剪切,overlay 滤镜⽤于视频叠加,rotate 滤镜实现旋转,movie 滤镜可以加载第三⽅的视频,
yadif 滤镜可以去隔⾏

1、主要结构体和API介绍

1.1 AVFilterGraph-对filters系统的整体管理

AVFilterGraph是FFmpeg中用于管理音视频滤镜的数据结构。它表示一个完整的滤镜图,可以包含多个输入输出,并通过连接不同的滤镜节点来实现各种音视频处理操作。

AVFilterGraph提供了创建、配置和管理滤镜的接口,允许用户构建复杂的滤镜拓扑结构,以实现音视频的处理和编辑。通AVFilterGraph,用户可以添加各种滤镜(如变换、剪切、合并、调节等)并将它们连接起来,最终实现所需的音视频处理效果。

在使用AVFilterGraph时,通常的流程包括创建滤镜图、添加输入输出流、添加滤镜节点、连接滤镜节点、设置参数等。一旦完成了滤镜图的构建,就可以将音视频帧送入滤镜图进行处理,最终得到处理后的输出。
**重点
struct AVFilterGraph
{
AVFilterContext filters;
unsigned nb_filters;
}

1.2 AVFilter-定义filter本身的能⼒

AVFilter 是 FFmpeg 中用于实现音视频滤镜的基本单位。它代表了一个特定的音视频处理功能,比如色彩转换、尺寸调整、去噪等。AVFilter 可以单独使用,也可以通过 AVFilterGraph 组合成复杂的滤镜链。
AVFilter 结构的作用主要是定义了一个滤镜的基本属性和行为,以及提供了滤镜初始化、销毁、格式查询等相关的回调函数接口。通过这些接口,用户可以对滤镜进行初始化配置,并将其应用到音视频流数据上,实现各种音视频处理效果。

在使用 AVFilter 时,通常的步骤包括:

  • 创建 AVFilterContext 对象,表示一个具体的 AVFilter 实例。
  • 设置滤镜参数和选项,可以通过 priv_class定义的私有类进行配置。
  • 连接输入输出端口,将 AVFilterContext 对象连接到其他滤镜或音视频流中。
  • 将音视频帧数据送入滤镜进行处理,得到处理后的输出数据。

重点
**const char *name; // overlay
const AVFilterPad inputs;
const AVFilterPad outputs;

typedef struct AVFilter {
    const char *name;			//滤镜的名称,用于唯一标识滤镜。
    const char *description;	//对滤镜功能的文字描述。
    const AVFilterPad *inputs;	//
    const AVFilterPad *outputs;
    const AVClass *priv_class; //用于指定滤镜的私有类,定义了滤镜的参数和选项。
    int flags;
    int (*preinit)(AVFilterContext *ctx);
    int (*init)(AVFilterContext *ctx);	//用于初始化滤镜的回调函数,可以在滤镜被创建时执行一些初始化操作。
    int (*init_dict)(AVFilterContext *ctx, AVDictionary **options);
    void (*uninit)(AVFilterContext *ctx);	//用于销毁滤镜的回调函数,在滤镜不再需要时执行清理操作。
    int (*query_formats)(AVFilterContext *);	//用于查询支持的输入输出格式的回调函数。
    int priv_size;      //滤镜私有数据的大小,用于分配存储私有数据的内存空间。
    int flags_internal; ///< Additional flags for avfilter internal use only.
    struct AVFilter *next;
    int (*process_command)(AVFilterContext *, const char *cmd, const char *arg, char *res, int res_len, int flags);
    int (*init_opaque)(AVFilterContext *ctx, void *opaque);
    int (*activate)(AVFilterContext *ctx);
} AVFilter;

1.3 AVFilterContext-filter实例,管理filter与外部的联系

AVFilterContext 是 FFmpeg 中用于表示一个特定 AVFilter 实例的数据结构。它包含了一个 AVFilter 的所有信息和状态,以便在音视频处理中对该滤镜进行配置、连接和处理。
AVFilterContext 结构的作用是管理一个 AVFilter 实例的输入输出端口,通过输入端口接收数据并经过滤镜处理后输出到输出端口。它还负责与滤镜图(AVFilterGraph)进行交互,将滤镜上下文与其他滤镜连接起来以实现复杂的音视频处理链。

在使用 AVFilterContext 时,通常的步骤包括:

  • 创建 AVFilterContext 对象,可以通过 avfilter_alloc_context() 函数创建。
  • 配置 AVFilterContext 的输入输出端口,可以通过 avfilter_link() 函数连接输入输出端口。
  • 设置滤镜参数和选项,可以通过 AVFilterContext 的属性进行配置。
  • 将音视频帧数据送入 AVFilterContext 进行处理,得到处理后的输出数据。

**重点
struct AVFilterContext
{
const AVFilter *filter;
char *name;
AVFilterPad *input_pads;
AVFilterLink **inputs;
unsigned nb_inputs
AVFilterPad *output_pads;
AVFilterLink *outputs;
unsigned nb_outputs;
struct AVFilterGraph graph; // 从属于哪个AVFilterGraph
}

1.4 AVFilterLink-定义两个filters之间的联接

AVFilterLink用于连接滤镜图中两个滤镜之间的输入和输出。它包含了连接两个滤镜所需的所有信息,例如输入和输出的样本格式、缓冲区、时间戳等等。AVFilterLink还包含了有关滤镜链中帧的信息,如PTS(Presentation Time Stamp,显示时间戳)和DTS(Decoding Time Stamp,解码时间戳)。通过AVFilterLink,不同的滤镜可以在滤镜图中相互连接,从而实现视频和音频处理的各种功能。

**重点
struct AVFilterLink
{
AVFilterContext *src;
AVFilterPad *srcpad;
AVFilterContext *dst;
AVFilterPad dstpad;
struct AVFilterGraph graph;
}

1.5 AVFilterPad-定义filter的输⼊/输出接⼝

AVFilterPad是FFmpeg中的结构,用于描述滤镜的输入或输出端口。滤镜可以有多个输入和输出端口,每个端口都由一个AVFilterPad结构表示。该结构包含了端口的名称、类型(输入或输出)、滤镜所支持的样本格式、是否支持多通道等信息。
在滤镜图中,滤镜之间的连接通过连接它们的输入和输出端口来实现。因此,AVFilterPad在滤镜之间建立连接时起到了关键的作用。通过AVFilterPad,FFmpeg能够正确地识别滤镜之间的连接,从而实现音视频数据的流动和处理。
**重点
struct AVFilterPad
{
const char *name;
AVFrame *(*get_video_buffer)(AVFilterLink *link, int w, int h);
AVFrame *(*get_audio_buffer)(AVFilterLink *link, int nb_samples);
int (*filter_frame)(AVFilterLink *link, AVFrame *frame);
int (request_frame)(AVFilterLink link);
}

1.6AVFilterInOut-过滤器链输⼊/输出的链接列表

在AVFilter模块中定义了AVFilter结构,很个AVFilter都是具有独⽴功能的节点,如scale filter的作⽤就是进⾏图像尺⼨变换,overlay filter的作⽤就是进⾏图像的叠加。
这⾥需要重点提的是两个特别的filter,⼀个是buffer,⼀个是buffersink,

  • 滤波器buffer代表filter graph中的源头,原始数据就往这个filter节点输⼊的;
  • ⽽滤波器buffersink代表filter graph中的输出节点,处理完成的数据从这个filter节点输出。
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;

2、函数使用

2.1 获取指定名称的滤镜–avfilter_get_by_name

// 获取FFmpeg中定义的filter,调⽤该⽅法前需要先调⽤avfilter_register_all();进⾏滤波器注册
AVFilter avfilter_get_by_name(const char name);
如果找到了指定名称的滤镜,该函数将返回一个指向对应AVFilter结构体的指针。如果未找到匹配的滤镜,函数将返回NULL。

2.2 向FFmpeg中的缓冲源滤镜(Buffer Source Filter)添加视频帧或音频帧–av_buffersrc_add_frame

/*
 *参数ctx是指向Buffer Source Filter的AVFilterContext结构体指针,通过它可以访问和控制Buffer Source Filter的属性和状态。
 *参数frame是要添加到Buffer Source Filter中的AVFrame结构体指针,它包含了要添加的视频帧或音频帧的数据和相关信息。
 *函数返回一个整数值,表示操作是否成功。如果成功添加帧数据,则返回0;如果发生错    误,则返回负值。
 */
int av_buffersrc_add_frame(AVFilterContext ctx, AVFrame frame);

通过调用av_buffersrc_add_frame函数,你可以将视频帧或音频帧添加到Buffer Source Filter中,从而使得该帧数据成为滤镜链的输入。

2.3 从缓冲汇滤镜(Buffer Sink Filter)中获取输出帧数据–

av_buffersink_get_frame

/* 
 *参数 ctx 是指向 Buffer Sink Filter 的 AVFilterContext 结构体指针,通过它可以访问和控制 Buffer Sink Filter 的属性和状态。
 *函数返回一个指向 AVFrame 结构体的指针,该结构体包含了从 Buffer Sink Filter 中获取的输出帧数据。如果没有可用的输出帧数据,函数将返回 NULL。
 */
int av_buffersink_get_frame(AVFilterContext ctx, AVFrame frame);

2.4 分配并初始化一个空的滤镜图–avfilter_graph_alloc

//分配并初始化一个空的滤镜图`在这里插入代码片`
AVFilterGraph *avfilter_graph_alloc(void);

在使用滤镜图进行音视频处理时,首先需要通过 avfilter_graph_alloc 分配一个滤镜图,然后在该滤镜图上添加各种滤镜节点,构建滤镜链,最后配置和链接这些滤镜节点,从而完成音视频处理任务。

2.5 在滤镜图中创建一个新的滤镜–avfilter_graph_create_filter

/* 
 * filt_ctx: 指向指针的指针,用于返回创建的滤镜实例的 AVFilterContext 结构体指针。
 * filt: 要创建的滤镜的 AVFilter 结构体指针。
 * name: 滤镜实例的名称。
 * args: 滤镜实例的参数,可以是滤镜实例初始化时需要的参数字符串。
 * opaque: 不透明指针,可以传递给滤镜的初始化函数。
 * graph_ctx: 滤镜图的上下文 AVFilterGraph 结构体指针,表示滤镜实例将要被添加到的滤镜图。
 * 该函数返回一个整数值,表示操作是否成功。如果成功创建滤镜实例并将其添加到滤镜图中,则返回0;如果发生错误,则返回负值。
 */
int avfilter_graph_create_filter(AVFilterContext **filt_ctx, 
								const AVFilter *filt,const char name, 
								const char args, void *opaque,
								AVFilterGraph *graph_ctx);

avfilter_graph_create_filter函数用于在滤镜图中创建一个新的滤镜实例,并将其添加到滤镜链中。通过调用 avfilter_graph_create_filter 函数,可以在滤镜图中创建一个指定类型的滤镜实例,并配置其参数。

2.6 在滤镜图中连接两个滤镜–avfilter_link

/*
 * src: 源滤镜的 AVFilterContext 结构体指针,表示连接的起始滤镜。
 * srcpad: 源滤镜的输出端口索引,表示连接的起始滤镜的输出端口。
 * dst: 目标滤镜的 AVFilterContext 结构体指针,表示连接的目标滤镜。
 * dstpad: 目标滤镜的输入端口索引,表示连接的目标滤镜的输入端口。
 * 该函数返回一个整数值,表示连接是否成功。如果成功连接两个滤镜,则返回0;如果发生错误,则返回负值。
 */ 
int avfilter_link(AVFilterContext *src, unsigned srcpad,AVFilterContext *dst, unsigned dstpad);

通过调用 avfilter_link 函数,可以在滤镜图中连接两个滤镜,从而构建滤镜链。连接的源滤镜的输出端口将与目标滤镜的输入端口相连。连接后,数据将从源滤镜流向目标滤镜,进行进一步的处理。

3、 AVFilter主体框架流程

在利⽤AVFilter进⾏⾳视频数据处理前先将在进⾏的处理流程绘制出来,现在以FFmpeg filter官⽅⽂档中的⼀个例⼦为例进⾏说明。
在这里插入图片描述
这个例⼦的处理流程如上所示,⾸先使⽤split滤波器将input流分成两路流(main和tmp),然后分别对两路流进⾏处理。对于tmp流,先经过crop滤波器进⾏裁剪处理,再经过flip滤波器进⾏垂直⽅向上的翻转操作,输出的结果命名为flip流。再将main流和flip流输⼊到overlay滤波器进⾏合成操作。上图的input就是上⾯提过的buffer源滤波器,output就是上⾯的提过的buffersink滤波器。上图中每个节点都是⼀个AVFilterContext,每个连线就是AVFliterLink。所有这些信息都统⼀由AVFilterGraph来管理。