我正在尝试将overlay
过滤器与多个输入源一起用于Android应用。基本上,我想在静态图像上叠加多个视频源。
我查看了ffmpeg附带的示例并基于此实现了我的代码,但事情似乎没有按预期工作。
在ffmpeg过滤示例中,似乎只有一个视频输入。我必须处理多个视频输入,我不确定我的解决方案是否正确。我试图找到其他例子,但看起来这是唯一的例子。
这是我的代码:
AVFilterContext **inputContexts;
AVFilterContext *outputContext;
AVFilterGraph *graph;
int initFilters(AVFrame *bgFrame, int inputCount, AVCodecContext **codecContexts, char *filters)
{
int i;
int returnCode;
char args[512];
char name[9];
AVFilterInOut **graphInputs = NULL;
AVFilterInOut *graphOutput = NULL;
AVFilter *bufferSrc = avfilter_get_by_name("buffer");
AVFilter *bufferSink = avfilter_get_by_name("buffersink");
graph = avfilter_graph_alloc();
if(graph == NULL)
return -1;
//allocate inputs
graphInputs = av_calloc(inputCount + 1, sizeof(AVFilterInOut *));
for(i = 0; i <= inputCount; i++)
{
graphInputs[i] = avfilter_inout_alloc();
if(graphInputs[i] == NULL)
return -1;
}
//allocate input contexts
inputContexts = av_calloc(inputCount + 1, sizeof(AVFilterContext *));
//first is the background
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=1/1:pixel_aspect=0", bgFrame->width, bgFrame->height, bgFrame->format);
returnCode = avfilter_graph_create_filter(&inputContexts[0], bufferSrc, "background", args, NULL, graph);
if(returnCode < 0)
return returnCode;
graphInputs[0]->filter_ctx = inputContexts[0];
graphInputs[0]->name = av_strdup("background");
graphInputs[0]->next = graphInputs[1];
//allocate the rest
for(i = 1; i <= inputCount; i++)
{
AVCodecContext *codecCtx = codecContexts[i - 1];
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
codecCtx->time_base.num, codecCtx->time_base.den,
codecCtx->sample_aspect_ratio.num, codecCtx->sample_aspect_ratio.den);
snprintf(name, sizeof(name), "video_%d", i);
returnCode = avfilter_graph_create_filter(&inputContexts[i], bufferSrc, name, args, NULL, graph);
if(returnCode < 0)
return returnCode;
graphInputs[i]->filter_ctx = inputContexts[i];
graphInputs[i]->name = av_strdup(name);
graphInputs[i]->pad_idx = 0;
if(i < inputCount)
{
graphInputs[i]->next = graphInputs[i + 1];
}
else
{
graphInputs[i]->next = NULL;
}
}
//allocate outputs
graphOutput = avfilter_inout_alloc();
returnCode = avfilter_graph_create_filter(&outputContext, bufferSink, "out", NULL, NULL, graph);
if(returnCode < 0)
return returnCode;
graphOutput->filter_ctx = outputContext;
graphOutput->name = av_strdup("out");
graphOutput->next = NULL;
graphOutput->pad_idx = 0;
returnCode = avfilter_graph_parse_ptr(graph, filters, graphInputs, &graphOutput, NULL);
if(returnCode < 0)
return returnCode;
returnCode = avfilter_graph_config(graph, NULL);
return returnCode;
return 0;
}
该函数的filters
参数传递给avfilter_graph_parse_ptr
,它可能如下所示:[background] scale=512x512 [base]; [video_1] scale=256x256 [tmp_1]; [base][tmp_1] overlay=0:0 [out]
在调用avfilter_graph_config
并发出警告后,通话中断:
Output pad "default" with type video of the filter instance "background" of buffer not connected to any destination
和错误Invalid argument
。
我做得不对的是什么?
编辑:我发现了两个问题:
看起来avfilter_graph_parse_ptr
的描述有点模糊。 ouputs
参数表示图表的当前输出列表,在我的情况下是graphInputs
变量,因为这些是buffer
过滤器的输出。 inputs
参数表示图表当前输入的列表,在这种情况下,这是graphOutput
变量,因为它代表buffersink
过滤器的输入。
我使用scale
过滤器和单个输入进行了一些测试。似乎AVFilterInOut
所需的avfilter_graph_parse_ptr
结构名称必须为in
。我尝试过不同的版本:in_1
,in_link_1
。他们都没有工作,我也找不到任何与此相关的文件。
所以这个问题仍然存在。如何实现具有多个输入的过滤器图形?
答案 0 :(得分:9)
我找到了解决问题的简单方法。
这涉及将avfilter_graph_parse_ptr
替换为avfilter_graph_parse2
并将buffer
和buffersink
过滤器添加到filters
的{{1}}参数。
因此,在您有一个背景图像和一个输入视频的简单情况下,avfilter_graph_parse2
参数的值应如下所示:
filters
buffer=video_size=1024x768:pix_fmt=2:time_base=1/25:pixel_aspect=3937/3937 [in_1]; buffer=video_size=1920x1080:pix_fmt=0:time_base=1/180000:pixel_aspect=0/1 [in_2]; [in_1] [in_2] overlay=0:0 [result]; [result] buffersink
将进行所有图表连接并初始化所有过滤器。输入缓冲区和输出缓冲区的过滤器上下文可以在结尾处从图形本身中检索。这些用于从过滤器图形中添加/获取帧。
代码的简化版本如下所示:
avfilter_graph_parse2
答案 1 :(得分:1)
我无法添加评论,所以我只想添加你可以修复&#34;输出垫&#34;默认&#34;与过滤器实例的类型视频&#34;背景&#34;没有连接到任何目的地的缓冲区&#34;没有水槽。过滤器将自动为您制作水槽。所以你添加了太多的垫子
答案 2 :(得分:0)
就我而言,我进行了如下转换:
[0:v]pad=1008:734:144:0:black[pad];[pad][1:v]overlay=0:576[out]
如果您从命令行尝试ffmpeg,它将起作用:
ffmpeg -i first.mp4 -i second.mp4 -filter_complex "[0:v]pad=1008:734:144:0:black[pad];[pad][1:v]overlay=0:576[out]" -map "[out]" -map 0:a output.mp4
基本上,增加第一个视频的整体大小,然后重叠第二个视频。经过长时间的尝试,与此线程相同的问题,我让它正常工作。 FFMPEG文档(https://ffmpeg.org/doxygen/2.1/doc_2examples_2filtering_video_8c-example.html)中的视频过滤示例可以正常工作,在深入研究之后,效果很好:
filterGraph = avfilter_graph_alloc();
NULLC(filterGraph);
bufferSink = avfilter_get_by_name("buffersink");
NULLC(bufferSink);
filterInput = avfilter_inout_alloc();
AVBufferSinkParams* buffersinkParams = av_buffersink_params_alloc();
buffersinkParams->pixel_fmts = pixelFormats;
FFMPEGHRC(avfilter_graph_create_filter(&bufferSinkContext, bufferSink, "out", NULL, buffersinkParams, filterGraph));
av_free(buffersinkParams);
filterInput->name = av_strdup("out");
filterInput->filter_ctx = bufferSinkContext;
filterInput->pad_idx = 0;
filterInput->next = NULL;
filterOutputs = new AVFilterInOut*[inputFiles.size()];
ZeroMemory(filterOutputs, sizeof(AVFilterInOut*) * inputFiles.size());
bufferSourceContext = new AVFilterContext*[inputFiles.size()];
ZeroMemory(bufferSourceContext, sizeof(AVFilterContext*) * inputFiles.size());
for (i = inputFiles.size() - 1; i >= 0 ; i--)
{
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
videoCodecContext[i]->width, videoCodecContext[i]->height, videoCodecContext[i]->pix_fmt, videoCodecContext[i]->time_base.num, videoCodecContext[i]->time_base.den, videoCodecContext[i]->sample_aspect_ratio.num, videoCodecContext[i]->sample_aspect_ratio.den);
filterOutputs[i] = avfilter_inout_alloc();
NULLC(filterOutputs[i]);
bufferSource = avfilter_get_by_name("buffer");
NULLC(bufferSource);
sprintf(args2, outputTemplate, i);
FFMPEGHRC(avfilter_graph_create_filter(&bufferSourceContext[i], bufferSource, "in", args, NULL, filterGraph));
filterOutputs[i]->name = av_strdup(args2);
filterOutputs[i]->filter_ctx = bufferSourceContext[i];
filterOutputs[i]->pad_idx = 0;
filterOutputs[i]->next = i < inputFiles.size() - 1 ? filterOutputs[i + 1] : NULL;
}
FFMPEGHRC(avfilter_graph_parse_ptr(filterGraph, description, &filterInput, filterOutputs, NULL));
FFMPEGHRC(avfilter_graph_config(filterGraph, NULL));
变量的类型与上面的示例相同,args和args2为char [512],其中outputTemplate为“%d:v”,基本上是过滤表达式中的输入视频ID。需要注意的几件事: