音视频开发36-7【FFmpeg】Filter 过滤器代码实现

查看ffmpeg内部支持哪些filter

ffmpeg -filters

ffmpeg 中 filter,filterchain,filtergraph 三者的概念和关系

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

filter的语法

1 [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的参数,是可选的。
“=parameters”有以下⼏种形式

3.1. 使⽤':'字符分隔的⼀个“键=值”对列表。如下所示。
ffmpeg -i input -vf scale=w=iw/2:h=ih/2 output
ffmpeg -i input -vf scale=h=ih/2:w=iw/2 output

3.2. 使⽤':'字符分割的“值”的列表。在这种情况下,键按照声明的顺序被假定为选项名。例如,scale filter
的前两个选项分别是w和h,当参数列表为“iw/2:ih/2”时,iw/2的值赋给w,ih/2的值赋给h。如下所
示。

ffmpeg -i input -vf scale=iw/2:ih/2 output


3.3. 使⽤':' 字符分隔混合“值”和“键=值”对的列表。“值”必须位于“键=值”对之前,并遵循与前⼀点相同的
约束顺序。之后的“键=值”对的顺序不受约束。如下所示。

ffmpeg -i input -vf scale=iw/2:h=ih/2 output


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

filterchain 语法

⽤⼀个字符串描述filterchain的组成,形式如下
"filter1, filter2, ... filterN-1, filterN"

说明:
1. 由⼀个或多个filter的连接⽽成,filter之间以逗号“,”分隔。
2. 每个filter都连接到序列中的前⼀个filter,即前⼀个filter的输出是后⼀个filter的输⼊。
⽐如示例

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

示例说明:
1. crop、vflip在同⼀个filterchain中

filtergraph的语法

⽤⼀个字符串描述filtergraph的组成,形式如下
"filterchain1;filterchain2;...filterchainN-1;fiterchainN"

AVFilter主体框架流程

我们以 如下的命令为例子,说明流程

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT

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

这个的意思是:当你将一个input 输入的时候,

会自动的split 分成两个,一个main,一个 temp,

对于temp进行处理,包括 crop(视频裁剪),vflip(垂直镜像翻转)后 会变成 flip,

再将 flip 和 main 结合起来,最终成为一个output  ,这里结合的过程 ffpemg 这里用了一个overlay的单词,这个overlay 是 覆盖的意思,是否意味着,用flip会覆盖main的一部分?,获取是我们多想了。

FFmpeg filter简介

FFmpeg filter提供了很多⾳视频特效处理的功能,⽐如视频缩放、截取、翻转、叠加等。
其中定义了很多的filter,例如以下常⽤的⼀些filter。

scale:视频/图像的缩放
overlay:视频/图像的叠加
crop:视频/图像的裁剪
trim:截取视频的⽚段
rotate:以任意⻆度旋转视频
⽀持的filter的列表可以通过以下命令获得。我们查看会发现 
ffmpeg -filters

一些基本概念:

先来看这个框架,

可以把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);

2. 滤镜配置和滤镜使用。FFmpeg中的滤镜使用分为两个步骤:滤镜配置滤镜使用滤镜配置目的是创建一个滤镜图并为其创建两个特殊的滤镜作为该滤镜图的输入端和输出端(视频:buffer滤镜和buffersink滤镜;音频:abuffer滤镜和abuffersink滤镜)

    //第四步,创建 输入 AVFilterContext, 和 输出 AVFilterContext,使用的方法是 avfilter_graph_create_filter
    //4.1 创建 输入 AVFilterContext,这里要非常清楚 avfilter_graph_create_filter 方法的各个参数
    /*
     * filt_ctx: 指向指针的指针,用于返回创建的滤镜实例的 AVFilterContext 结构体指针。
     * filt: 要创建的滤镜的 AVFilter 结构体指针。
     * name: 滤镜实例的名称。这个名字是我们随便取的,叫啥不重要,重要的是要唯一
     * args: 滤镜实例的参数,可以是滤镜实例初始化时需要的参数字符串。args则是这个AVFilter的参数, 注意仅仅是这个AVFilter的参数,不是整个graph的参数。再拿Fade举例,args就可以是t=in:st=3:d=3。


     * opaque: 不透明指针,可以传递给滤镜的初始化函数。
     * graph_ctx: 滤镜图的上下文 AVFilterGraph 结构体指针,表示滤镜实例将要被添加到的滤镜图。
     * 该函数返回一个整数值,表示操作是否成功。如果成功创建滤镜实例并将其添加到滤镜图中,则返回0;如果发生错误,则返回负值。



// 1. 创建滤镜图
AVFilterGraph *filter_graph = avfilter_graph_alloc();
if(! filter_graph) {
    RLOG_E("alloc filter graph failed.");
    return -1;
}
// 2. 配置滤镜图的输入端,即创建buffer滤镜,然后创建其滤镜实例并命名为"in",
//    并将该滤镜实例添加到之前创建的滤镜图中。需要注意的是,在创建buffer滤镜实例
//    AVFilterContext时,要传入创建所需参数args
char args[512];
snprintf(args, sizeof(args),
         "video_size=%d%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
         decode_ctx->width, decode_ctx->height, decode_ctx->pix_fmt,
         time_base.num, time_base.den, decode_ctx->sample_aspect_ratio.num,
         decode_ctx->sample_aspect_ratio.den);
AVFilterContext *buffer_ctx;
const AVFilter *buffer_filter = avfilter_get_by_name("buffer");
int ret = avfilter_graph_create_filter(&buffer_ctx,   // buffer滤镜的实例
                                       buffer_filter, // buffer滤镜
                                       "in",          // 滤镜实例命名
                                       args,        // 创建buffer滤镜实例所需参数
                                       NULL,
                                       filter_graph); // 被添加的滤镜图
if(ret < 0) {
    RLOG_E_("avfilter_graph_create_filter failed,err=%d", ret);
    return ret;
}
// 3. 配置滤镜图的输出端,即创建buffersink滤镜,然后创建其滤镜实例并命名为"out",
// 同时将该滤镜实例添加到之前创建的滤镜图中。另外,buffersink滤镜输出有一个输出参数
// 即"pix_fmt",表示输出像素格式,假如后面视频的视频帧由sws_scale进行转换可以不设置
AVFilterContext *buffersink_ctx;
const AVFilter *buffersink_filter = avfilter_get_by_name("buffersink");
ret =  avfilter_graph_create_filter(&buffersink_ctx,  // 被创建的滤镜实例
                                    buffersink_filter,// buffersink滤镜
                                    "out",            // 滤镜实例命名
                                    NULL,
                                    NULL,
                                    filter_graph);    // 被添加的滤镜图
if(ret < 0) {
    RLOG_E_("avfilter_graph_create_filter failed,err=%d", ret);
    return ret;
}
enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUYV422, AV_PIX_FMT_NONE};
ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
                          AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
if(ret < 0) {
    RLOG_E_("set output pixel format failed,err=%d", ret);
    return ret;
}

这里重点讲下在buffer滤镜和buffersink滤镜在创建滤镜实例时,为什么需要分别传入args参数信息和设定输出像素格式,以及如何知道具体需要传入什么参数?我们在命令行中分别输入ffmpeg -h filter=bufferffmpeg -h filter=buffersink命令,得到的信息如下:

# buffer滤镜帮助信息
$ ffmpeg -h filter=buffer
ffmpeg version 4.0.2 Copyright (c) 2000-2018 the FFmpeg developers
Filter buffer
Buffer video frames, and make them accessible to the filterchain.
	Inputs:
		none (source filter)
	Outputs:
		#0: default (video)
buffer AVOptions:
width         <int>        ..FV..... (from 0 to INT_MAX) (default 0)
video_size    <image_size> ..FV.....
height        <int>        ..FV..... (from 0 to INT_MAX) (default 0)
pix_fmt       <pix_fmt>    ..FV..... (default none)
sar           <rational>   ..FV... sample aspect ratio (from 0 to DBL_MAX) (default 0/1)
pixel_aspect  <rational>   ..FV... sample aspect ratio (from 0 to DBL_MAX) (default 0/1)
time_base     <rational>   ..FV..... (from 0 to DBL_MAX) (default 0/1)
frame_rate    <rational>   ..FV..... (from 0 to DBL_MAX) (default 0/1)
sws_param     <string>     ..FV.....

# buffer滤镜帮助信息  
 $  ffmpeg -h filter=buffersink
 ffmpeg version 4.0.2 Copyright (c) 2000-2018 the FFmpeg developers
 Filter buffersink
 Buffer video frames, and make them available to the end of the filter graph.
 	Inputs:
 		#0: default (video)
 	Outputs:
 		none (sink filter)
 buffersink AVOptions:
 pix_fmts          <binary>     ..FV..... set the supported pixel formats

1.AVFilter-定义filter本身的能⼒

在 libavfilter/avfilter.h 中定义

AVFilter 是 FFmpeg 中用于实现音视频滤镜的基本单位。它代表了一个特定的音视频处理功能,比如色彩转换、尺寸调整、去噪等。AVFilter 可以单独使用。

例如ffmepg提供的scale滤镜,就是一个AVFilter。

AVFilter 结构的作用主要是定义了一个滤镜的基本属性和行为,以及提供了滤镜初始化、销毁、格式查询等相关的回调函数接口。通过这些接口,用户可以对滤镜进行初始化配置,并将其应用到音视频流数据上,实现各种音视频处理效果。

数据结构
typedef struct AVFilter {
    /**
     * Filter name. Must be non-NULL and unique among filters.
     *///滤镜的名称,用于唯一标识滤镜。
    const char *name;

    /**
     * A description of the filter. May be NULL.
     *
     * You should use the NULL_IF_CONFIG_SMALL() macro to define it.
     *///对滤镜功能的文字描述。
    const char *description;

    /**
     * List of static inputs.
     *
     * NULL if there are no (static) inputs. Instances of filters with
     * AVFILTER_FLAG_DYNAMIC_INPUTS set may have more inputs than present in
     * this list.
     */
    const AVFilterPad *inputs;

    /**
     * List of static outputs.
     *
     * NULL if there are no (static) outputs. Instances of filters with
     * AVFILTER_FLAG_DYNAMIC_OUTPUTS set may have more outputs than present in
     * this list.
     */
    const AVFilterPad *outputs;

    /**
     * A class for the private data, used to declare filter private AVOptions.
     * This field is NULL for filters that do not declare any options.
     *
     * If this field is non-NULL, the first member of the filter private data
     * must be a pointer to AVClass, which will be set by libavfilter generic
     * code to this class.
     */ //用于指定滤镜的私有类,定义了滤镜的参数和选项。
    const AVClass *priv_class;

    /**
     * A combination of AVFILTER_FLAG_*
     */
    int flags;

    /*****************************************************************
     * All fields below this line are not part of the public API. They
     * may not be used outside of libavfilter and can be changed and
     * removed at will.
     * New public fields should be added right above.
     *****************************************************************
     */

    /**
     * The number of entries in the list of inputs.
     */
    uint8_t nb_inputs;

    /**
     * The number of entries in the list of outputs.
     */
    uint8_t nb_outputs;

    /**
     * This field determines the state of the formats union.
     * It is an enum FilterFormatsState value.
     */
    uint8_t formats_state;

    /**
     * Filter pre-initialization function
     *
     * This callback will be called immediately after the filter context is
     * allocated, to allow allocating and initing sub-objects.
     *
     * If this callback is not NULL, the uninit callback will be called on
     * allocation failure.
     *
     * @return 0 on success,
     *         AVERROR code on failure (but the code will be
     *           dropped a
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值