En el artículo anterior, cuando usamos filtros, creamos manualmente varios filtros y luego vinculamos manualmente diferentes filtros de acuerdo con el enlace de procesamiento, lo que nos ayudó a comprender el proceso de filtrado. En este artículo, usamos el formulario de parámetros para permitir que ffmpeg cree y vincule automáticamente los filtros para nosotros, lo que puede reducir la cantidad de código. Al mismo tiempo, podemos usar primero la prueba de parámetros y luego copiar directamente los parámetros en el código. para usar.
Preparar
ffme 4.4
Un archivo de video en formato MP4 o flv
flujo de procesamiento
Aquí tenemos que explicar:
En primer lugar, debemos comprender la sintaxis del gráfico de filtro. Cada filtro tiene al menos una entrada y una salida. Para un filtro con múltiples entradas y salidas, cómo especificar qué entrada y salida vincular con otros filtros, por lo que debe especificar un etiqueta, como el siguiente comando
[in]escala=iw/2:ih/2[in_tmp];[in_tmp]split=2[in_1][in_2];[in_1]pad=iw*2:ih*2:color=amarillo[a];[ a][en_2]superposición=w[fuera]
El filtro dividido anterior copia el cuadro de video de entrada en 2 salidas y especifica los nombres como [in_1] y [in_2], [in_2] se usa como la segunda entrada del filtro de superposición, y [in_1] es procesado por el filtro de almohadilla como la primera entrada a la superposición.
Los dos nombres [in] y [out] son los predeterminados del sistema. También puede modificarlo usted mismo.
Bueno, veamos el diagrama de flujo de arriba.
/*
和 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);
El método avfilter_graph_parse_ptr analiza los filtros en la cadena de filtros, crea filtros y los vincula. En él se utilizan dos estructuras AVFilterInOut. como sigue:
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;
las salidas y las entradas especifican respectivamente un contexto de filtro y un nombre de pin. Aquí está realmente el búfer y el receptor de búfer.
Luego, el método avfilter_graph_parse_ptr vinculará el filtro con el pin de entrada del filtro en la cadena a las salidas, y el filtro con el pin de salida a las entradas. De esta manera, los filtros buffer y buffersink se agregan a toda la cadena de filtros, formando una cadena de filtros completa.
Cuando creamos buffer y buffersink, ordenamos que el nombre de salida del buffer estuviera dentro y el nombre de entrada de buffersink fuera.
Aquí nos fijamos en los parámetros [in]......[out], de hecho, es para vincularlos con los nombres correspondientes.
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;
}