上一篇我们在使用滤镜时是手动创建各种滤镜,然后根据处理链路手动链接不同的过滤器,有助于我们理解滤镜的流程。这一篇我们使用参数形式,让ffmpeg自动帮我们创建和链接过滤器,这样可以减少代码量,同时我们可以先使用参数测试后,直接将参数复制到代码中使用。
准备
ffmeg 4.4
一个MP4或flv格式视频文件
处理流程
这里我们需要说明下:
首先我们需要理解filtergraph语法,每个过滤器至少有一个输入和输出,对于多个输入和输出的过滤器如何指定那个输入和输出与其他过滤器进行链接呢,所以需要指定标签,如下面的命令
[in]scale=iw/2:ih/2[in_tmp];[in_tmp]split=2[in_1][in_2];[in_1]pad=iw*2:ih*2:color=yellow[a];[a][in_2]overlay=w[out]
上面的split的滤镜将输入的视频帧复制为2个输出并指定名称为[in_1]和[in_2],[in_2]作为overlay滤镜的第二个输入,[in_1]经过pad滤镜处理后作为overlay的第一个输入。
而[in]和[out]这两个名称是系统默认的。也可自行修改。
好了我们来看上面的流程图。
/*
和 avfilter_graph_parse 类似。不同的是 inputs 和 outputs 参数,即做输入参数,也做输出参数。
在函数返回时,它们将会保存 graph 中所有的处于 open 状态的 pad。返回的 inout 应该使用 avfilter_inout_free() 释放掉。
注意:在字符串描述的 graph 中,第一个 filter 的输入如果没有被一个字符串标识,默认其标识为"in",最后一个 filter 的输出如果没有被标识,默认为"output"。
intpus:作为输入参数是,用于保存已经存在的graph的open inputs,可以为NULL。
作为输出参数,用于保存这个parse函数之后,仍然处于open的inputs,当然如果传入为NULL,则并不输出。
outputs:同上。
*/
int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
AVFilterInOut **inputs, AVFilterInOut **outputs, void *log_ctx);
方法 avfilter_graph_parse_ptr解析filters字符串中滤镜,创建滤镜并将滤镜链接起来。里面用到了2个AVFilterInOut结构。如下:
AVFilterInOut* outputs = avfilter_inout_alloc();
AVFilterInOut* inputs = avfilter_inout_alloc();
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
outputs,inputs分别指定一个滤镜的上下文和引脚名称。这里其实就时buffer和buffersink.
然后avfilter_graph_parse_ptr 方法会将字符串的中滤镜输入引脚为in的滤镜与outputs链接,输出引脚为out的滤镜与inputs链接。这样就把buffer和buffersink滤镜加入到了整个滤镜链路中了,形成了完整的滤镜链路。
我们在创建buffer和buffersink时,分别命令了buffer的输出名称为in,buffersink的输入名称为out
这里我们看面的参数[in]............[out],其实就是为了将他们使用对应的名称链接起来。
const char* filter_descr = "[in]scale=iw/2:ih/2[in_tmp];[in_tmp]split=2[in_1][in_2];[in_1]pad=iw*2:ih*2:color=yellow[a];[a][in_2]overlay=w[out]";
char args[512];
int ret = 0;
const AVFilter* buffersrc = avfilter_get_by_name("buffer");//预缓存帧数据。用于输入
const AVFilter* buffersink = avfilter_get_by_name("buffersink");//缓冲视频帧,并使它们可用于过滤器图形的末尾。用于输出
AVFilterInOut* outputs = avfilter_inout_alloc();
AVFilterInOut* inputs = avfilter_inout_alloc();
AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base;
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };//注意输入的格式类型pix_fmts[0]
filter_graph = avfilter_graph_alloc();
if (!outputs || !inputs || !filter_graph) {
ret = AVERROR(ENOMEM);
goto end;
}
/* buffer video source: the decoded frames from the decoder will be inserted here. */
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt,
time_base.num, time_base.den,
dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den);
//创建和初始化过滤器实例并将其添加到现有图形中。【输入】
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
goto end;
}
//创建和初始化过滤器实例并将其添加到现有图形中。【输出】
/* buffer video sink: to terminate the filter chain. */
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
NULL, NULL, filter_graph);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
goto end;
}
//给buffersink_ctx设置参数
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;
}
/*
* Set the endpoints for the filter graph. The filter_graph will
* be linked to the graph described by filters_descr.
*/
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
//将字符串描述的图形添加到图形中。
if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr, &inputs, &outputs, NULL)) < 0)
{
av_log(NULL, AV_LOG_ERROR, "Error avfilter_graph_parse_ptr\n");
goto end;
}
//检查AVFilterGraph有效性
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Error avfilter_graph_config\n");
goto end;
}