ffmpeg 多个-vf_ffmpeg音视频过滤器实战

目录

音视频过滤器

视频过滤器

FFmpeg 音视频过过滤器

在编码之前,FFmpeg 可以使用 libavfilter 库中的过滤器处理原始音频和视频帧。几个链式过滤器行程一个过滤图像。

FFmpeg中的filter包含三个层次,filter -> filterchain -> filtergraph。

在多媒体处理中,filter的意思是被编码到输出文件之前用来修改输入文件的内容到一个软件工具。如:视频翻转、旋转、缩放等。

1、filter

在简单过滤器中,只包含一个输入和一个输出,并且输入输出是同一类型。在下面的处理过程中,仅仅是在解码和编码之前加上了一个额外的过滤步骤。简单过滤器有 per-stream-filter 参数(视频为-vf, 音频为-af)配置。

(1)视频过滤器 -vf

# 如testsrc视频按顺时针方向旋转90度
ffplay -f lavfi -i testsrc -vf transpose=1

# 如testsrc视频水平翻转(左右翻转)
ffplay -f lavfi -i testsrc -vf hflip
12345

(2)音频过滤器 -af

# 实现慢速播放,声音速度是原始速度的50%
ffplay p629100.mp3 -af atempo=0.5
12

2、filterchain

filterchain 不是简单的将线性操作链应用于一个流(上面的简单过滤器便是如此)。例如,当图形有多个输入和/或输出,或者当输出流类型与输入不同时,就是这种情况。

基本用法:

Filterchain = 逗号分隔的一组filter

“filter1,filter2,filter3,…filterN-2,filterN-1,filterN”
123

顺时针旋转90度并水平翻转:

ffplay -f lavfi -i testsrc -vf transpose=1,hflip
1

复杂的过滤器图使用-filter_complex选项进行配置。 请注意,此选项是全局性的,因为复杂的过滤器图形本质上不能与单个流或文件明确关联。-lavfi选项等同于-filter_complex。

3、filtergraph

基本语法:

Filtergraph = 分号分隔的一组filterchain 语法: “filterchain1;filterchain2;…filterchainN-1;filterchainN” 
1

Filtergraph的分类:

  • 1、简单(simple)一对一
  • 2、负责(complex)多对一,多对多

一个复杂的过滤器图的一个简单的例子是覆盖过滤器,它有两个视频输入和一个视频输出,包含一个视频叠加在另一个上面。 它的音频对应是amix滤波器。

视频过滤器

讲了FFMpeg中过滤器涉及到的数据结构和相关的函数调用说明,我们看下在我们的程序中使用FFMpeg过滤器的整体流程是什么样的,具体有什么步骤,要注意什么细节。

下面这幅图从整体上说明了FFMpeg过滤器的使用关系图

f717d3b211889a21666dd8b9969832e5.png

首先,我们对上图先做下说明,理解下图中每个步骤的关系,然后,才从代码的角度来给出其使用的步骤。

1. 最顶端的AVFilterGraph,这个结构前面介绍过,主要管理加入的过滤器,其中加入的过滤器就是通过函数avfilter_graph_create_filter来创建并加入,这个函数返回是AVFilterContext(其封装了AVFilter的详细参数信息)。

2. buffer和buffersink这两个过滤器是FFMpeg为我们实现好的,buffer表示源,用来向后面的过滤器提供数据输入(其实就是原始的AVFrame);buffersink过滤器是最终输出的(经过过滤器链处理后的数据AVFrame),其它的诸如filter 1 等过滤器是由avfilter_graph_parse_ptr函数解析外部传入的过滤器描述字符串自动生成的,内部也是通过avfilter_graph_create_filter来创建过滤器的。

3. 上面的buffer、filter 1、filter 2、filter n、buffersink之间是通过avfilter_link函数来进行关联的(通过AVFilterLink结构),这样子过滤器和过滤器之间就通过AVFilterLink进行关联上了,前一个过滤器的输出就是下一个过滤器的输入,注意,除了源和接收过滤器之外,其它的过滤器至少有一个输入和输出,这很好理解,中间的过滤器处理完AVFrame后,得到新的处理后的AVFrame数据,然后把新的AVFrame数据作为下一个过滤器的输入。

4. 过滤器建立完成后,首先我们通过av_buffersrc_add_frame把最原始的AVFrame(没有经过任何过滤器处理的)加入到buffer过滤器的fifo队列。

5. 然后调用buffersink过滤器的av_buffersink_get_frame_flags来获取处理完后的数据帧(这个最终放入buffersink过滤器的AVFrame是通过之前创建的一系列过滤器处理后的数据)。

使用流程图就介绍到这里,下面结合上面的使用流程图详细说下FFMpeg中使用过滤器的步骤,这个过程我们分为三个部分:过滤器构建、数据加工、资源释放。

过滤器构建:

)分配AVFilterGraph
             AVFilterGraph* graph = avfilter_graph_alloc();
       2)创建过滤器源
             char srcArgs[256] = {0};
             AVFilterContext *srcFilterCtx;
             AVFilter* srcFilter = avfilter_get_by_name("buffer");
             avfilter_graph_create_filter(&srcFilterCtx, srcFilter ,"out_buffer", srcArgs, NULL, graph);
       3)创建接收过滤器
             AVFilterContext *sinkFilterCtx;
             AVFilter* sinkFilter = avfilter_get_by_name("buffersink");
             avfilter_graph_create_filter(&sinkFilterCtx, sinkFilter,"in_buffersink", NULL, NULL, graph);
       4)生成源和接收过滤器的输入输出
             这里主要是把源和接收过滤器封装给AVFilterInOut结构,使用这个中间结构来把过滤器字符串解析并链接进graph,主要代码如下:
             AVFilterInOut *inputs = avfilter_inout_alloc();
             AVFilterInOut *outputs = avfilter_inout_alloc();
             outputs->name       = av_strdup("in");             outputs->filter_ctx = srcFilterCtx;             outputs->pad_idx    = 0;             outputs->next       = NULL;
             inputs->name        = av_strdup("out");             inputs->filter_ctx  = sinkFilterCtx;             inputs->pad_idx     = 0;             inputs->next        = NULL;

这里源对应的AVFilterInOut的name最好定义为in,接收对应的name为out,因为FFMpeg源码里默认会通过这样个name来对默认的输出和输入进行查找。

               通过解析过滤器字符串添加过滤器
             const *char filtergraph = "[in1]过滤器名称=参数1:参数2[out1]";
             int ret = avfilter_graph_parse_ptr(graph, filtergraph, &inputs, &outputs, NULL);

这里过滤器是以字符串形式描述的,其格式为:[in]过滤器名称=参数[out],过滤器之间用,或;分割,如果过滤器有多个参数,则参数之间用:分割,其中[in]和[out]分别为过滤器的输入和输出,可以有多个。

             检查过滤器的完整性
             avfilter_graph_config(graph, NULL);

数据加工

            向源过滤器加入AVFrame
             AVFrame* frame; // 这是解码后获取的数据帧
             int ret = av_buffersrc_add_frame(srcFilterCtx, frame);       2)从buffersink接收处理后的AVFrame
             int ret = av_buffersink_get_frame_flags(sinkFilterCtx, frame, 0);
             现在我们就可以使用处理后的AVFrame,比如显示或播放出来。

总结

使用结束后,调用avfilter_graph_free(&graph);释放掉AVFilterGraph类型的graph。

原文地址:https://www.cnblogs.com/lidabo/p/15397073.html