FFmpeg滤镜学习之AVFilter

FFmpeg滤镜学习之AVFilter


什么是AVFilter?

对音视频数据添加特效,如黑白视频、混音,同时也可以完成音视频像素格式转码工作;官方特效说明请点击;一般使用filter都是在后台服务器对视频进行特效处理,移动端的话相对比较耗资源


AVFilter处理框架

大致框架如下图所示,可以把AVFilter看做一些列Filter节点链组成,这个链由AVfilterGraph管理,每个AVFilter节点都会对数据处理,处理完成后交给下一个节点继续处理,直到最后一个节点处理完成。每个AVFilter节点都会有一个AVFilterContext上下文对其进行管理,第一个节点音视频名称为buffer/abuffer,最后一个节点名称为buffersink/abuffersink;内部各个节点链接方式可以自由灵活配置,前一个的输出配置在后一个的输入,可以多个节点进行过滤,也可以少数2个节点过滤
在这里插入图片描述


代码介绍各个关键环节


创建Filter链的管理者

创建AVFilterGraph

AVFilterGraph* filterGraph = avfilter_graph_alloc();

配置一个AVFilter节点

  1. 创建一个AVFilter、AVFilterContext,并初始化
AVFilterContext* bufferSrcCtx;
AVFilter *bufferSrc = avfilter_get_by_name("buffer");
ret = avfilter_graph_create_filter(&bufferSrcCtx, bufferSrc, "in", 
												args, nullptr,filterGraph);

函数介绍:
int avfilter_graph_create_filter(AVFilterContext **filt_ctx,
const AVFilter *filt, const char *name, const char *args, void *opaque,
AVFilterGraph *graph_ctx);

filt_ctx: 当前节点上下文
filt: 当前节点AVfilter
name:名字自取,但是要在整个链中唯一
args:节点过滤参数
opaque:不知所意,传null即可
graph_ctx:整个链的管理者

filter有哪些呢?移步

  1. 为这个filter节点配置滤镜规则
    a. 上述avfilter_graph_create_filter函数中的args,就是配置的参数,该args配置如下:
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
 mWidth, mHeight, mInputPixelFormat, timeBase.num, timeBase.den,
 ratio.num, ratio.den);

当前节点配置是进入的第一个节点,所以配置的都是输入的视频格式

b. 除以上配置方式外,还有另一种配置方式
直接向AVFilterContext上下文结构体中进行设置:

int av_opt_set         (void *obj, const char *name, const char *val, int search_flags);
int av_opt_set_int     (void *obj, const char *name, int64_t     val, int search_flags);
int av_opt_set_double  (void *obj, const char *name, double      val, int search_flags);
int av_opt_set_q       (void *obj, const char *name, AVRational  val, int search_flags);
int av_opt_set_bin     (void *obj, const char *name, const uint8_t *val, int size, int search_flags);
int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags);
int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, int search_flags);
int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags);
int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags);
int av_opt_set_channel_layout(void *obj, const char *name, int64_t ch_layout, int search_flags);

其中Obj就是上下文指针,name对应上下文结构体中的变量名,val是变量的值

  1. 同理,配置其他AVFilter也是向上面的操作一样;每个Filter的过滤规则都不一样的

AVFilter节点之间的链接

每个AVFilter都有端口,其类型为AVFilterPad,通过端口可以把两个AVFilter连接起来,如下代码:

err = avfilter_link(abuffer_ctx, 0, volume_ctx, 0); 
if (err >= 0)
    err = avfilter_link(volume_ctx, 0, aformat_ctx, 0); 
if (err >= 0)
    err = avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0); 
if (err < 0) {
    fprintf(stderr, "Error connecting filters\n");
    return err;
}  

上述代码大致意思是,用abuffer_ctx的输出0号端口,输出到volume_ctx的0号输入端口;volume_ctx的输出0号端口,输出到aformat_ctx的0号输入端口,后面同理
但是,主要注意的是 buffer的AVFilter只有输出端口,buffersink的AVFilter只有输入端口,其他的AVFilter都有输入和输出

第二种链接方式:
可能大家还看过另一种链接的函数,
int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
AVFilterInOut **inputs, AVFilterInOut **outputs,
void *log_ctx);
以上使用场景,在只有buffer和buffersink两个节点的情况下,可以使用这个函数进行链接;
graph:不用说了,链表管理者
filter:以字符串的形式指定了过滤规则,如lutyuv='u=128:v=128黑白过滤规则,更多特效请移步
inputs:指定输入的节点
outputs:指定输出节点
log_ctx:null即可

AVFilterInOut,当你使用了avfilter_graph_parse_ptr和avfilter_graph_parse2的时候就需要使用AVFilterInOut,用于指定输入和输出

AVFilterInOut *output = avfilter_inout_alloc();
AVFilterInOut *in = avfilter_inout_alloc();
//绑定输入端
output->name = av_strdup("in");
output->filter_ctx = bufferSrcCtx;
//filter_ctx的序号
output->pad_idx = 0;
output->next = nullptr;

//绑定输出端
in->name = av_strdup("out");
in->filter_ctx = bufferSinkCtx;
in->pad_idx = 0;
in->next = nullptr;

检查整个Graph配置

//解析filter描述
if((ret = avfilter_graph_config(mVideoFilterGraph, nullptr)) < 0){
    LOGE("failed to call avfilter_graph_config: %s", av_err2str(ret));
    goto end;
}

输入数据开始过滤

无论有多少个AVFilter节点,主要保留AVFilterGraph和第一个以及最后一个的AVFilterContext引用,第一个AVFilterContext用于传入数据,第二个用于接收处理的数据;

输入数据

int av_buffersrc_add_frame(AVFilterContext *ctx, AVFrame *frame);
int av_buffersrc_add_frame_flags(AVFilterContext *buffer_src,
AVFrame *frame, int flags);

获取输出

int av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame);
int av_buffersink_get_samples(AVFilterContext *ctx, AVFrame *frame, int nb_samples);

最后使用完以后记得释放AVFilterGraph
void avfilter_graph_free(AVFilterGraph **graph);

demo下载地址

更多精彩博文,加入我们,一同进步!

在这里插入图片描述

发布了148 篇原创文章 · 获赞 41 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/jackzhouyu/article/details/103999489