FFMpeg-7、libavfilter滤波器相关介绍,实现翻转叠加和水印_libavfilter 对视频帧水印添加的示例

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

*/
AVFilterContext* bufferSrc_ctx;
ret = avfilter_graph_create_filter(&bufferSrc_ctx, bufferSrc, “in”, args, NULL, filter_graph);
if (ret < 0) {
printf(“Fail to create filter bufferSrc\n”);
return -1;
}

4、将加入的多个滤波器实例进行链接link
//进行链接滤波器buffer – split
// src filter to split filter
ret = avfilter_link(bufferSrc_ctx, 0, splitFilter_ctx, 0);
if (ret != 0) {
printf(“Fail to link src filter and split filter\n”);
return -1;
}
5、检查滤波器图的正确性
//检查有效性并配置图中的所有链接和格式。
// check filter graph
ret = avfilter_graph_config(filter_graph, NULL);
if (ret < 0) {
printf(“Fail in filter graph\n”);
return -1;
}
6、可以输出滤波器图以字符串的形式打印出来方便观看(可选)
//将图形转储为人类可读的字符串表示形式。
char *graph_str = avfilter_graph_dump(filter_graph, NULL);


### 2.2、各种滤波器AVFilter


在libavfilter中,一个过滤器可以有多个输入和多个输出。  
 AVFilter相关的源码结构



// 定义filter本身的能力,拥有的pads,回调函数接口定义
struct AVFilter
{
const char *name;
const AVFilterPad *inputs;
const AVFilterPad *outputs;
}
// filter实例,管理filter与外部的联系
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;

}
// 定义两个filters之间的联接
struct AVFilterLink
{
AVFilterContext *src;
AVFilterPad *srcpad;

AVFilterContext \*dst;
AVFilterPad \*dstpad;

struct AVFilterGraph \*graph;

}
// 定义filter的输入/输出接口
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);
}
struct AVFilterInOut
{
char *name;
AVFilterContext *filter_ctx;
int pad_idx;
struct AVFilterInOut *next;
}
在AVFilter模块中定义了AVFilter结构,很个AVFilter都是具有独立功能的节点,
如scale filter的作用就是进行图像尺寸变换,
overlay filter的作用就是进行图像的叠加,
滤波器buffer代表filter graph中的源头,原始数据就往这个filter节点输入的;
滤波器buffersink代表filter graph中的输出节点,处理完成的数据从这个filter节点输出。


暂时先介绍用到的,其他的用到再补上 总共分类就这几大类



Audio Filters
Audio Sources
Audio Sinks
Video Filters
crop 裁剪输入视频到给定的尺寸。
overlay 将一个视频叠加在另一个视频上。它有两个输入,一个输出。第一个输入是覆盖第二个输入的“主”视频。
OpenCL Video Filters
VAAPI Video Filters
Video Sources
buffer 滤波器buffer代表filter graph中的源头,原始数据就往这个filter节点输入的
Video Sinks
buffersink 滤波器buffersink代表filter graph中的输出节点,处理完成的数据从这个filter节点输出。
Multimedia Filters
split, asplit 把输入分成几个相同的输出。asplit工作在音频输入,split只分割视频。过滤器只接受一个参数,该参数指定输出的数量。如果未指定,则默认为2。
Multimedia Sources


滤波器相关的代码有



// 获取FFmpeg中定义的filter,调用该方法前需要先调用avfilter_register_all();进行滤波器注册
AVFilter *avfilter_get_by_name(const char *name);
//滤波器buffer代表filter graph中的源头,原始数据就往这个filter节点输入的
AVFilter* bufferSrc = avfilter_get_by_name(“buffer”);
avfilter_get_by_name的源码,具体的name有哪些在源码的什么位置需要追源代码
const AVFilter *avfilter_get_by_name(const char *name)
{
const AVFilter *f = NULL;
void *opaque = 0;

if (!name)
    return NULL;

while ((f = av\_filter\_iterate(&opaque)))
    if (!strcmp(f->name, name))
        return (AVFilter \*)f;

return NULL;

}

// 往源滤波器buffer中输入待处理的数据
int av_buffersrc_add_frame(AVFilterContext *ctx, AVFrame *frame);
//向缓冲区源添加一个帧。
if (av_buffersrc_add_frame(bufferSrc_ctx, frame_in) < 0) {
printf(“Error while add frame.\n”);
break;
}

// 从目的滤波器buffersink中输出处理完的数据
int av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame);
//从接收器获得一个过滤后的数据帧,并将其放入帧中。
ret = av_buffersink_get_frame(bufferSink_ctx, frame_out);
if (ret < 0)
break;

// 创建一个滤波器图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);
AVFilterContext* bufferSrc_ctx;
ret = avfilter_graph_create_filter(&bufferSrc_ctx, bufferSrc, “in”, args, NULL, filter_graph);
//需要传参的 输出2路 之后链接split节点的时候需要指定哪路数据
AVFilterContext *splitFilter_ctx;
ret = avfilter_graph_create_filter(&splitFilter_ctx, splitFilter, “split”, “outputs=2”, NULL, filter_graph);

// 连接两个滤波器节点
int avfilter_link(AVFilterContext *src, unsigned srcpad,
AVFilterContext *dst, unsigned dstpad);
//split分出第2pad–crop
ret = avfilter_link(splitFilter_ctx, 1, cropFilter_ctx, 0);


### 3、AVFilter实现上面滤波器图实例



            [main]

input --> split ---------------------> overlay --> output
| ^
|[tmp] [flip]|
±----> crop --> vflip -------+


实例  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210404110302275.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 核心代码



//生成滤波器图抽象成一个函数出来,其实可以关于滤波器抽象出一个类来,之后需要的滤波器图可以统一管理
int InitAvFilter(AVFilterGraph *&filter_graph, AVFilterContext * &bufferSrc_ctx, AVFilterContext* &bufferSink_ctx, const char *args)
{
int ret = 0;

avfilter\_register\_all();

filter_graph = avfilter\_graph\_alloc();
if (!filter_graph)
{
	cout << "create filter graph Fial" << endl;
	return -1;
}

//创建一个buffer源过滤器并创建对应的过滤器实列并加入过滤器图中,之后就需要将图里面的不同过滤器链接到一起即可

AVFilter\* bufferSrc = avfilter\_get\_by\_name("buffer");
ret = avfilter\_graph\_create\_filter(&bufferSrc_ctx, bufferSrc ,"in", args, NULL, filter_graph);
if (ret < 0) {
	printf("Fail to create filter\n");
	return -1;
}

//创建buffersink目的过滤器
//bufferSink需要设置参数 允许的像素格式列表,以AV\_PIX\_FMT\_NONE结束
AVBufferSinkParams \*bufferSink_params = NULL;
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
bufferSink_params = av\_buffersink\_params\_alloc();
bufferSink_params->pixel_fmts = pix_fmts;
AVFilter\* bufferSink = avfilter\_get\_by\_name("buffersink"); //ffbuffersink带缓冲的buffersink
ret = avfilter\_graph\_create\_filter(&bufferSink_ctx, bufferSink, "out", NULL, bufferSink_params, filter_graph);
if (ret < 0) {
	printf("Fail to create filter sink filter\n");
	return -1;
}

//创建split分割过滤器 可以传入参数outputs=2表示分割出两路 注意链接的时候需要指定哪一路 从0开始计数
AVFilter \*splitFilter = avfilter\_get\_by\_name("split");
AVFilterContext \*splitFilter_ctx;
ret = avfilter\_graph\_create\_filter(&splitFilter_ctx, splitFilter, "split", "outputs=2", NULL, filter_graph);
if (ret < 0) {
	printf("Fail to create split filter\n");
	return -1;
}

//创建crop裁剪处理过滤器
AVFilter \*cropFilter = avfilter\_get\_by\_name("crop");
AVFilterContext \*cropFilter_ctx;
ret = avfilter\_graph\_create\_filter(&cropFilter_ctx, cropFilter, "crop", "out\_w=iw:out\_h=ih/2:x=0:y=0", NULL, filter_graph);
if (ret < 0) {
	printf("Fail to create crop filter\n");
	return -1;
}

//创建vflip水平翻转过滤器
AVFilter \*vflipFilter = avfilter\_get\_by\_name("vflip");
AVFilterContext \*vflipFilter_ctx;
ret = avfilter\_graph\_create\_filter(&vflipFilter_ctx, vflipFilter, "vflip", NULL, NULL, filter_graph);
if (ret < 0) {
	printf("Fail to create vflip filter\n");
	return -1;
}

//创建overlay覆盖过滤器,将一个视频覆盖再另外一个视频上
AVFilter \*overlayFilter = avfilter\_get\_by\_name("overlay");
AVFilterContext \*overlayFilter_ctx;
ret = avfilter\_graph\_create\_filter(&overlayFilter_ctx, overlayFilter, "overlay", "y=0:H/2", NULL, filter_graph);
if (ret < 0) {
	printf("Fail to create overlay filter\n");
	return -1;
}

//对上面添加到过滤器图中的过滤器进行链接生成逻辑关系
//进行链接滤波器buffer -- split
ret = avfilter\_link(bufferSrc_ctx, 0, splitFilter_ctx, 0);
if (ret != 0) {
	printf("Fail to link src filter and split filter\n");
	return -1;
}
//链接split分出第1pad--overlay
ret = avfilter\_link(splitFilter_ctx, 0, overlayFilter_ctx, 0);
if (ret != 0) {
	printf("Fail to link split filter and overlay filter main pad\n");
	return -1;
}
//链接split分出第2pad--crop
ret = avfilter\_link(splitFilter_ctx, 1, cropFilter_ctx, 0);
if (ret != 0) {
	printf("Fail to link split filter's second pad and crop filter\n");
	return -1;
}
//链接crop到vflip
ret = avfilter\_link(cropFilter_ctx, 0, vflipFilter_ctx, 0);
if (ret != 0) {
	printf("Fail to link crop filter and vflip filter\n");
	return -1;
}
//链接vflip到overlay的第二个输入pad 主屏幕 第一个输入是覆盖第二个输入的“主”视频。
ret = avfilter\_link(vflipFilter_ctx, 0, overlayFilter_ctx, 1);
if (ret != 0) {
	printf("Fail to link vflip filter and overlay filter's second pad\n");
	return -1;
}
//链接overlay的输出到sink目的过滤器
ret = avfilter\_link(overlayFilter_ctx, 0, bufferSink_ctx, 0);
if (ret != 0) {
	printf("Fail to link overlay filter and sink filter\n");
	return -1;
}

//检查有效性并配置图中的所有链接和格式。
ret = avfilter\_graph\_config(filter_graph, NULL);
if (ret < 0) {
	printf("Fail in filter graph\n");
	return -1;
}

//测试使用;
//将图形转储为人类可读的字符串表示形式。
char \*graph_str = avfilter\_graph\_dump(filter_graph, NULL);
FILE\* graphFile = NULL;
fopen\_s(&graphFile, "graphFile.txt", "w");
fprintf(graphFile, "%s", graph_str);
av\_free(graph_str);

return 0;

}

调用
//初始化avfilter滤波器
AVFilterGraph *filter_graph = NULL;
AVFilterContext * bufferSrc_ctx = NULL;
AVFilterContext* bufferSink_ctx = NULL;
char args pFormatCtx->streams[videoindex]->codecpar->width, pFormatCtx->streams[videoindex]->codecpar->height, AV_PIX_FMT_YUV420P,
1, 25, 1, 1);
ret = InitAvFilter(filter_graph, bufferSrc_ctx, bufferSink_ctx, args);
if (ret != 0)
{
cout << “InitAvFilter faile” << endl;
return -1;
}

显示调用
//向缓冲区源添加一个帧。
if (av_buffersrc_add_frame(bufferSrc_ctx, pFrame) < 0) {
printf(“Error while add frame.\n”);
break;
}

//从接收器获得一个过滤后的数据帧,并将其放入帧中。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

");
break;
}

//从接收器获得一个过滤后的数据帧,并将其放入帧中。

[外链图片转存中…(img-qlut7zv9-1715745300243)]
[外链图片转存中…(img-XahrZQTo-1715745300244)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值