FFmpeg filter过滤器使用详解


1、FFmpeg过滤器简介

FFmpeg filter 提供了很多音视频特效处理功能,比如视频缩放、截取、翻转、叠加等。
FFmpeg有很多已经实现好的滤波器,这些滤波器的实现位于libavfilter目录之下,用户需要可以调用这些滤波器实现滤波。

我们可以通过ffmpeg -filters命令查看FFmpeg支持的过滤器。FFmpeg常用的filter包括:

  • scale:视频/图像的缩放;
  • overlay:视频/图片的叠加;
  • crop:视频/图像的裁剪;
  • trim:截取视频的片段;
  • rotate:以任意⻆度旋转视频
  • movie:加载第三方的视频;
  • yadif:去隔行。

使用示例(对视频的宽和高减半):ffmpeg -i input -vf scale=iw/2:ih/2 output


2、过滤器使用方法

ffmpeg的filter用起来是和Gstreamer的plugin是一样的概念,通过avfilter_link,将各个创建好的filter按自己想要的次序链接到一起,然后avfilter_graph_config之后,就可以正常使用。

FFmpeg的filter包含3个层次:filter -> filterchain -> filtergraph

filtergraph可以用文本形式表示,可以作为ffmpeg中的 -filter/-vf/-af和-filter_complex 选项以及ffplay中的- vf/-af 和libavfilter/avfilter.h中定义的avfilter_graph_parse2()函数的参数。

示例”把视频的上部分镜像到下半部分“ , 流程如下:
在这里插入图片描述
(1)使用 split filter 将输入流分割为两个流 [main] 和 [temp];
(2)其中一个流[temp]通过crop filter把下半部分裁剪掉;
(3)步骤2中的输出再经过vflip filter对视频进行垂直翻转,输出[flip];
(4)把步骤3中输出[flip]叠加到[main]的下半部分。

这个流程可以编程实现,也可以使用命令实现。命令实现如下:ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip[flip]; [main][flip] overlay=0:H/2" OUTPUT

处理结果如下:
在这里插入图片描述


3、过滤器语法

3.1 filter语法

用一个字符串描述filter的组成:
形式:[in_link_1]…[in_link_N]filter_name=parameters[out_link_1]…[out_link_M]

参数:
1)[in_link_N]、[out_link_N]:用来标识输⼊和输出的标签。in_link_N是标签名,可以任意命名,需要使用方括号括起来。在filter_name的前面的标签用于标识输入,在filter_name的后面的标签用于标识输出。一个filter可以有多个输入和多个输出,没有输入的filter称为source filter,没有输出的filter称为sink filter。对于输入或输出打标签是可选的,打上标签是为了连接其他filter时使用。

2)filter_name:filter的名称。

3) “=parameters”:包含初始化filter的参数,是可选的。有以下几种形式:

  • 使用 : 字符分隔的一个“键=值”对列表。如:ffmpeg -i input -vf scale=w=iw/2:h=ih/2 output

  • 使用 : 字符分割的“值”的列表。在这种情况下,键按照声明的顺序被假定为选项名 。例如,scale filter的前两个选项分别是w和h,当参数列表为“iw/2:ih/2”时,iw/2的值赋给w,ih/2的值赋给h。如:ffmpeg -i input -vf scale=iw/2:ih/2 output

  • 使用 : 字符分隔混合“值”和“键=值”对的列表。“值”必须位于“键=值”对之前,并遵循与前一点相同的
    约束顺序。之后的“键=值”对的顺序不受约束。如:ffmpeg -i input -vf scale=iw/2:h=ih/2 output

filter类定义了filter的特性以及输入和输出的数量,某个filter的使用方式可以通过以下命令获知:ffmpeg -h filter=filter_name


抽取视频Y、U、V分量到不同的文件示例(extractplanes filter指定了三个输出,分别是 [y][u][v],抽取后,将不同的输出保存到不同的文件中):ffmpeg -i input.mp4 -filter_complex "extractplanes=y+u+v[y][u][v]" -map "[y]" input_y.mp4 -map "[u]" input_u.mp4 -map "[v]" input_v.mp4


3.2 filterchain的语法

用一个字符串描述filterchain的组成:
形式:"filter1, filter2, ... filterN-1, filterN"

说明:
1)由1个或多个filter的连接而成,filter之间以逗号“,”分隔。
2)每个filter都连接到序列中的前1个filter,即1个filter的输出是后1个filter的输入。比如示例(crop)


3.3 filtergraph的语法

用一个字符串描述filtergraph的组成:
形式:"filterchain1;filterchain2;...filterchainN-1;fiterchainN"

说明:
1)由1个或多个filter的组合而成,filterchain之间用分号";"分隔。
2)filtergraph是连接filter的有向图。它可以包含循环,一对filter之间可以有多个连接。
3)当在filtergraph中找到两个相同名称的标签时,将创建相应输入和输出之间的连接。
4)如果输出没有被打标签,则默认将其连接到filterchain中下1个filter的第1个未打标签的输入。

例如:以下filterchain中:nullsrc, split[L1], [L2]overlay, nullsink
说明:split filter有两个输出,overlay filter有两个输入。split的第1个输出标记为“L1”,overlay的第1个输入pad标记为“L2”。split的第1个输出将连接到overlay的第1个输入。

5)在一个filter描述中,如果没有指定第1个filter的输入标签,则假定为“In”。如果没有指定最后一个filter的输出标签,则假定为“out”。

6)在一个完整的filterchain中,所有没有打标签的filter输入和输出必须是连接的。如果所有filterchain的所有filter输入和输出pad都是连接的,则认为filtergraph是有效的[2]。
示例:ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip[flip]; [main][flip] overlay=0:H/2" OUTPUT
说明:其中有三个filterchain, 分别是,

  • “split [main][tmp]”。它只有1个filter,即 split,它有1个默认的输入,即INPUT解码后的frame。有两个输出, 以 [main], [tmp] 标识。
  • “[tmp] crop=iw:ih/2:0:0, vflip [flip]”。它由两个filter组成,crop和vflip,crop的输入 为[tmp],vflip的输出标识为[flip]。
  • “[main][flip] overlay=0:H/2”。它由⼀个filter组成,即overlay。有两个输入,[main]和[flip]。有1个默认的输出。


4、过滤器主要结构体

(1)AVFilterGraph结构体
定义描述:对filter系统的整体管理;主要用于统合这整个滤波过程。

重点字段:

typedef struct AVFilterGraph {
	AVFilterContext **filters;
	unsigned nb_filters;
	....


(2)AVFilter
定义描述:滤波器的实现是通过AVFilter以及位于其下的结构体/函数来维护的。

重点字段:

typedef struct AVFilter {
	const char *name;			//overlay, Filter name. Must be non-NULL and unique among filters.
	const AVFilterPad *inputs;	//List of inputs, terminated by a zeroed element.
	const AVFilterPad *outputs;	//List of outputs, terminated by a zeroed element.
	...

使用示例:定义filter本身的能力,拥有pads,回调函数接口定义。

AVFilter ff_vf_overlay = {
 .name = "overlay",
 .description = NULL_IF_CONFIG_SMALL("Overlay a video source on top of the input."),
 .preinit = overlay_framesync_preinit,
 .init = init,
 .uninit = uninit,
 .priv_size = sizeof(OverlayContext),
 .priv_class = &overlay_class,
 .query_formats = query_formats,
 .activate = activate,
 .process_command = process_command,
 .inputs = avfilter_vf_overlay_inputs,
 .outputs = avfilter_vf_overlay_outputs,
 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS,
 };


(3)AVFilterContext
定义描述:一个滤波器实例,即使是同一个滤波器,在进行实际的滤波时,也会由于输入的参数不同而有不同的滤波效果,AVFilterContext就是在实际进行滤波时用于维护滤波相关信息的实体。

重点字段:

 struct AVFilterContext {
 	const AVFilter *filter;		///< the AVFilter of which this is an instance
	char *name;					///< name of this filter instance
	AVFilterPad *input_pads;	///< array of input pads
	AVFilterLink **inputs;		///< array of pointers to input links
	unsigned nb_inputs;			///< number of input pads
	AVFilterPad *output_pads;	///< array of output pads
	AVFilterLink **outputs;		///< array of pointers to output links
	unsigned nb_outputs;		///< number of output pads
	struct AVFilterGraph *graph; ///< filtergraph this filter belongs to 从属于哪个AVFilterGraph
	...
};


(4)AVFilterLink
定义描述:滤波器链,主要是用于链接相邻的两个AVFilterContext,为了实现一个滤波过程,可能会需要多个滤波器协同完成,即一个滤波器的输出可能会是另一个滤波器输入,AVFilterLink的作用是串联两个相邻的滤波器实例,形成两个滤波器之间的通道。

重点参数:

struct AVFilterLink
{
	AVFilterContext *src; 			///< source filter
	AVFilterPad *srcpad;			///< output pad on the source filte
	AVFilterContext *dst;			///< dest filter
	AVFilterPad *dstpad;			///< input pad on the dest filter
	struct AVFilterGraph *graph;	//Graph the filter belongs to.
	...
};


(5)AVFilterPad
定义描述:滤波器的输入输出端口,一个滤波器可以有多个输入以及多个输出端口,相邻滤波器之间是通过AVFilterLink来串联的,而位于AVFilterLink两端的分别就是前一个滤波器的输出端口以及一个滤波器的输入端口。

重点参数:

struct AVFilterPad
{
	const char *name;	//Pad name. The name is unique among inputs and among outputs,
	AVFrame *(*get_video_buffer)(AVFilterLink *link, int w, int h);	// Callback function to get a video buffer. 
	AVFrame *(*get_audio_buffer)(AVFilterLink *link, int nb_samples);	//Callback function to get an audio buffer. I
	int (*filter_frame)(AVFilterLink *link, AVFrame *frame);	//Filtering callback. 
	int (*request_frame)(AVFilterLink *link);	//Frame poll callback.
	...
}


(6)AVFilterInOut
定义描述:过滤器链输入/输出的链接列表。

重点参数:

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;

在AVFilter模块中定义了AVFilter结构,每个AVFilter结构都是具有独立功能的节点,如scale filter的作用就是进行图像尺寸变换,overlay filter的作用就是进行图像的叠加。

这里需要重点提的两个特别的filter:buffer和buffersink
(1)buffer:滤波器buffer代表filter graph中的源头,原始数据就往这个filter节点输入的。通过调用该滤波器提供的函数(如av_buffersrc_add_frame)可以把需要滤波的帧传输进入滤波过程。在创建该滤波器实例的时候需要提供一些关于所输入的帧的格式的必要参数(如:time_base、图像的宽高、图像像素格式等)。

(2)buffersink:一个特殊的滤波器,滤波器buffersink代表filter graph中的输出节点,处理完成的数据从这个filter节点输出。通过调用滤波器提供的函数(如av_buffersink_get_frame)可以提供出被滤波过程过滤完成后的帧。


5、主要函数

//进行滤波器注册
avfilter_register_all();

// 获取FFmpeg中定义的filter,调用该方法前需要先调用avfilter_register_all();进行滤波器注册
AVFilter avfilter_get_by_name(const char name);

// 往源滤波器buffer中输入待处理的数据
int av_buffersrc_add_frame(AVFilterContext ctx, AVFrame frame);

// 从目的滤波器buffersink中获取处理完的数据
int av_buffersink_get_frame(AVFilterContext ctx, AVFrame frame);

// 创建一个滤波器图filter graph
AVFilterGraph *avfilter_graph_alloc(void);

// 创建一个滤波器实例AVFilterContext,并添加到AVFilterGraph中
int avfilter_graph_create_filter(AVFilterContext **filt_ctx, 
								const AVFilter *filt,const char name, 
								const char args, void *opaque,AVFilterGraph *graph_ctx);

// 连接两个滤波器节点
int avfilter_link(AVFilterContext *src, unsigned srcpad,AVFilterContext *dst, unsigned dstpad);


6、滤波实例过程

step1:注册过滤器

 avfilter_register_all();//注册



step2:创建统合整个滤波过程的滤波图结构体(AVFilterGraph)

AVFilterGraph* filter_graph = avfilter_graph_alloc();



step3:获取滤波过程需要的滤波器(AVFilter)

const AVFilter *buffersrc = avfilter_get_by_name("buffer");		//AVFilterGraph的输入源
const AVFilter *buffersink = avfilter_get_by_name("buffersink");//输出滤波器
const AVFilter *myfilter = avfilter_get_by_name("myfilter");	//处理业务的滤波器



step4:创建用于维护滤波相关信息的滤波器实例(AVFilterContext)

AVFilterContext *in_video_filter = NULL;
AVFilterContext *out_video_filter = NULL;
AVFilterContext *my_video_filter = NULL;

  char args[512];
    sprintf(args,
        "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
        in_width, in_height, AV_PIX_FMT_YUV420P,
        1, 25, 1, 1);
avfilter_graph_create_filter(&in_video_filter, buffersrc, "in", args,NULL, filter_graph);
avfilter_graph_create_filter(&out_video_filter, buffersink, "out", NULL, NULL, filter_graph);
avfilter_graph_create_filter(&my_video_filter, myfilter, "myfilter",NULL, NULL, filter_graph);



step5:用AVFilterLink把相邻的两个滤波实例连接起来

avfilter_link(in_video_filter, 0, my_video_filter, 0);
avfilter_link(my_video_filter, 0, out_video_filter, 0);



step6:提交整个滤波图

avfilter_graph_config(filter_graph, NULL);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值