FFmpeg滤镜API用法详解

Posted 老樊Lu码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg滤镜API用法详解相关的知识,希望对你有一定的参考价值。

1、滤镜API使用方法

在代码中使用滤镜,主要分为两个步骤:

[1]. 滤镜的初始化配置:根据滤镜参数,配置生成滤镜图,此滤镜图供下一步骤使用

[2]. 使用滤镜处理原始音视频帧:向滤镜图提供输入帧(AVFrame),从滤镜图取出经处理后的输出帧(AVFrame)

1. init_filters() // 配置生成可用的滤镜图,由用户编写

2. av_buffersrc_add_frame_flags() // 向滤镜图提供输入帧,API函数

3. av_buffersink_get_frame() // 从滤镜图取出处理后的输出帧,API函数

3.1 滤镜配置

在代码中,滤镜配置比滤镜使用复杂,滤镜配置代码如下:

// 功能:创建配置一个滤镜图,在后续滤镜处理中,可以往此滤镜图输入数据并从滤镜图获得输出数据

// filters_descr:输入参数,形如“transpose=cclock,pad=iw+80:ih:40”

// @vfmt:输入参数,描述提供给待生成滤镜图的视频帧和格式

// @fctx:输出参数,返回生成滤镜图的信息,供调用者使用

int init_video_filters(const char *filters_descr, const input_vfmt_t *vfmt, filter_ctx_t *fctx)

int ret = 0;

// 1. 配置滤镜图输入端和输出端

// 分配一个滤镜图filter_graph

fctx->filter_graph = avfilter_graph_alloc();

if (!fctx->filter_graph)

ret = AVERROR(ENOMEM);

goto end;

char args[512];

char *p_args = NULL;

if (vfmt != NULL)

/* buffer video source: the decoded frames from the decoder will be inserted here. */

// args是buffersrc滤镜的参数

snprintf(args, sizeof(args),

"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",

vfmt->width, vfmt->height, vfmt->pix_fmt,

vfmt->time_base.num, vfmt->time_base.den,

vfmt->sar.num, vfmt->sar.den);

p_args = args;

// buffer滤镜:缓冲视频帧,作为滤镜图的输入

const AVFilter *bufsrc = avfilter_get_by_name("buffer");

// 创建滤镜实例fctx->bufsrc_ctx,此滤镜实例从bufsrc中创建,并使用参数p_args进行初始化

// 新创建的滤镜实例命名为"in",并被添加到滤镜图fctx->filter_graph中

ret = avfilter_graph_create_filter(&fctx->bufsrc_ctx, bufsrc, "in",

p_args, NULL, fctx->filter_graph);

if (ret < 0)

av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\\n");

goto end;

// buffersink滤镜:缓冲视频帧,作为滤镜图的输出

const AVFilter *bufsink = avfilter_get_by_name("buffersink");

/* buffer video sink: to terminate the filter chain. */

// 创建滤镜实例buffersink_ctx,此滤镜实例从bufsink中创建

// 新创建的滤镜实例命名为"out",并被添加到滤镜图fctx->filter_graph中

ret = avfilter_graph_create_filter(&fctx->bufsink_ctx, bufsink, "out",

NULL, NULL, fctx->filter_graph);

if (ret < 0)

av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\\n");

goto end;

#if 0 // 因为后面显示视频帧时有sws_scale()进行图像格式转换,故此处不设置滤镜输出格式也可

enum AVPixelFormat pix_fmts[] = AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUYV422, AV_PIX_FMT_NONE ;

// 设置输出像素格式为pix_fmts[]中指定的格式(如果要用SDL显示,则这些格式应是SDL支持格式)

ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,

AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);

if (ret < 0)

av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\\n");

goto end;

#endif

// 1. end

// 2. 将filters_descr描述的滤镜图添加到fctx->filter_graph滤镜图中

/*

* Set the endpoints for the filter graph. The filter_graph will

* be linked to the graph described by filters_descr.

*/

// 设置滤镜图的端点,将filters_descr描述的滤镜图连接到此滤镜图,

// 两个滤镜图的连接是通过端点连接(AVFilterInOut)完成的

/*

* The buffer source output must be connected to the input pad of

* the first filter described by filters_descr; since the first

* filter input label is not specified, it is set to "in" by

* default.

*/

// outputs变量意指buffersrc_ctx滤镜的输出引脚(output pad)

// src缓冲区(buffersrc_ctx滤镜)的输出必须连到filters_descr中第一个

// 滤镜的输入;filters_descr中第一个滤镜的输入标号未指定,故默认为

// "in",此处将buffersrc_ctx的输出标号也设为"in",就实现了同标号相连

AVFilterInOut *outputs = avfilter_inout_alloc();

outputs->name = av_strdup("in");

outputs->filter_ctx = fctx->bufsrc_ctx;

outputs->pad_idx = 0;

outputs->next = NULL;

/*

* The buffer sink input must be connected to the output pad of

* the last filter described by filters_descr; since the last

* filter output label is not specified, it is set to "out" by

* default.

*/

// inputs变量意指buffersink_ctx滤镜的输入引脚(input pad)

// sink缓冲区(buffersink_ctx滤镜)的输入必须连到filters_descr中最后

// 一个滤镜的输出;filters_descr中最后一个滤镜的输出标号未指定,故

// 默认为"out",此处将buffersink_ctx的输出标号也设为"out",就实现了

// 同标号相连

AVFilterInOut *inputs = avfilter_inout_alloc();

inputs->name = av_strdup("out");

inputs->filter_ctx = fctx->bufsink_ctx;

inputs->pad_idx = 0;

inputs->next = NULL;

// 将filters_descr描述的滤镜图添加到fctx->filter_graph滤镜图中

// 调用前:fctx->filter_graph包含两个滤镜fctx->bufsrc_ctx和fctx->bufsink_ctx

// 调用后:filters_descr描述的滤镜图插入到fctx->filter_graph中,fctx->bufsrc_ctx连接到filters_descr

// 的输入,filters_descr的输出连接到fctx->bufsink_ctx,filters_descr只进行了解析而不

// 建立内部滤镜间的连接。filters_desc与fctx->filter_graph间的连接是利用AVFilterInOut inputs

// 和AVFilterInOut outputs连接起来的,AVFilterInOut是一个链表,最终可用的连在一起的

// 滤镜链/滤镜图就是通过这个链表串在一起的。

ret = avfilter_graph_parse_ptr(fctx->filter_graph, filters_descr,

&inputs, &outputs, NULL);

if (ret < 0)

goto end;

// 2. end

// 3. 配置filtergraph滤镜图,建立滤镜间的连接

// 验证有效性并配置filtergraph中所有连接和格式

ret = avfilter_graph_config(fctx->filter_graph, NULL);

if (ret < 0)

goto end;

// 3. end

end:

avfilter_inout_free(&inputs);

avfilter_inout_free(&outputs);

return ret;

函数参数说明:

输入参数 const char *filters_descr

以字符串形式提供滤镜选项,例如参数为 "transpose=cclock,pad=iw+80:ih:40" 时,表示将视频帧逆时针旋转 90 度,然后在视频左右各填充 40 像素的黑边。

输入参数 input_vfmt_t *vfmt

用于描述提供给滤镜图的视频帧和格式,在配置滤镜图中的第一个滤镜 buffer 时需要为滤镜提供参数,就是从 vfmt 参数转换得到。

input_vfmt_t 为自定义数据结构,定义如下:

typedef struct

int width;

int height;

enum AVPixelFormat pix_fmt;

AVRational time_base;

AVRational sar;

AVRational frame_rate;

input_vfmt_t;

输出参数 filter_ctx_t *fctx

用于返回生成滤镜图的信息,供调用者使用。

filter_ctx_t 为自定义数据结构,定义如下:

typedef struct

AVFilterContext *bufsink_ctx;

AVFilterContext *bufsrc_ctx;

AVFilterGraph *filter_graph;

filter_ctx_t;

此结构中三个成员:bufsrc_ctx 用于滤镜图的输入,bufsink_ctx 用于滤镜图的输出,filter_graph 指向滤镜图。

TODO: 一个滤镜图可能含多个滤镜链,即可能有多个输入节点(bufsrc_ctx)或多个输出节点(bufsink_ctx),此数据结构应改进为支持多输入和多输出

init_video_filters() 函数实现的几个步骤如下:

3.1.1 配置滤镜图输入端和输出端

buffer 滤镜和 buffersink 滤镜是两个特殊的视频滤镜,分别用于视频滤镜链的输入端和输出端。与之相似,abuffer 滤镜和 abuffersink 滤镜是两个特殊的音频滤镜,分别用于音频滤镜链的输入端和输出端。

一个滤镜图可能由多个滤镜链构成,每个滤镜链的输入节点就是 buffer 滤镜,输出节点是 buffersink 滤镜,因此一个滤镜图可能有多个 buffer 滤镜,也可能有多个 buffersink 滤镜。应用程序通过访问 buffer 滤镜和 buffersink 滤镜实现和滤镜图的数据交互。

buffer 滤镜

在命令行中输入 ffmpeg -h filter=buffer 查看 buffer 滤镜的帮助信息,如下:

$ ffmpeg -h filter=buffer

ffmpeg version 4.1 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 滤镜用作滤镜链的输入节点。buffer 滤镜缓冲视频帧,滤镜链可以从 buffer 滤镜中取得视频帧数据。

在上述帮助信息中,Inputs 和 Outputs 指滤镜的输入引脚和输出引脚。buffer 滤镜是滤镜链中的第一个滤镜,因此只有输出引脚而无输入引脚。

滤镜(AVFilter)需要通过滤镜实例(AVFilterContext)引用,为 buffer 滤镜创建的滤镜实例是 fctx->bufsrc_ctx,用户通过往 fctx->bufsrc_ctx 填入视频帧来为滤镜链提供输入。

为 buffer 滤镜创建滤镜实例时需要提供参数,buffer 滤镜需要的参数在帮助信息中的 “buffer AVOptions” 部分列出,由 vfmt 输入参数提供,代码如下:

char args[512];

char *p_args = NULL;

if (vfmt != NULL)

/* buffer video source: the decoded frames from the decoder will be inserted here. */

// args是buffersrc滤镜的参数

snprintf(args, sizeof(args),

"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",

vfmt->width, vfmt->height, vfmt->pix_fmt,

vfmt->time_base.num, vfmt->time_base.den,

vfmt->sar.num, vfmt->sar.den);

p_args = args;

// buffer滤镜:缓冲视频帧,作为滤镜图的输入

const AVFilter *bufsrc = avfilter_get_by_name("buffer");

// 创建滤镜实例fctx->bufsrc_ctx,此滤镜实例从bufsrc中创建,并使用参数p_args进行初始化

// 新创建的滤镜实例命名为"in",并被添加到滤镜图fctx->filter_graph中

ret = avfilter_graph_create_filter(&fctx->bufsrc_ctx, bufsrc, "in",

p_args, NULL, fctx->filter_graph);

if (ret < 0)

av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\\n");

goto end;

buffersink 滤镜

在命令行中输入 ffmpeg -h filter=buffersink 查看 buffersink 滤镜的帮助信息,如下:

$ ffmpeg -h filter=buffersink

ffmpeg version 4.1 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

buffersink 滤镜用作滤镜链的输出节点。滤镜链处理后的视频帧可以缓存到 buffersink 滤镜中。buffersink 滤镜是滤镜链中的最后一个滤镜,因此只有输入引脚而无输出引脚。

为 buffersink 滤镜创建的滤镜实例是 fctx->bufsink_ctx,用户可以从 fctx->bufsink_ctx 中读视频帧来获得滤镜链的输出。

通过帮助信息可以看到,buffersink 滤镜参数只有一个 “pix_fmt”,用于设置滤镜链输出帧的像素格式列表,这个像素格式有多种,以限制输出帧格式不超过指定的范围。

// buffersink滤镜:缓冲视频帧,作为滤镜图的输出

const AVFilter *bufsink = avfilter_get_by_name("buffersink");

// 为buffersink滤镜创建滤镜实例buffersink_ctx,命名为"out"

// 将新创建的滤镜实例buffersink_ctx添加到滤镜图filter_graph中

ret = avfilter_graph_create_filter(&fctx->bufsink_ctx, bufsink, "out",

NULL, NULL, fctx->filter_graph);

#if 0 // 因为后面显示视频帧时有sws_scale()进行图像格式转换,故此处不设置滤镜输出格式也可

enum AVPixelFormat pix_fmts[] = AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUYV422, AV_PIX_FMT_NONE ;

// 设置输出像素格式为pix_fmts[]中指定的格式(如果要用SDL显示,则这些格式应是SDL支持格式)

ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,

AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);

#endif

将 buffer 滤镜和 buffsink 滤镜添加进滤镜图中后,如下图所示:

未完待续。。。。。

以上是关于FFmpeg滤镜API用法详解的主要内容,如果未能解决你的问题,请参考以下文章

JavaCV开发详解之36:使用delogo滤镜实现去除和屏蔽视频水印

JavaCV开发详解之36:使用delogo滤镜实现去除和屏蔽视频水印

JavaCV开发详解之36:使用delogo滤镜实现去除和屏蔽视频水印

JavaCV开发详解之32:使用filter滤镜实现中文字符叠加

JavaCV开发详解之32:使用filter滤镜实现中文字符叠加

jQuery旋转插件jqueryrotate用法详解