前回の記事では、フィルターを使用するときに、さまざまなフィルターを手動で作成し、処理リンクに従って異なるフィルターを手動でリンクすることで、フィルター処理を理解するのに役立ちました。この記事では、パラメーター フォームを使用して、ffmpeg に自動的にフィルターを作成してリンクさせ、コードの量を減らすことができます。同時に、最初にパラメーター テストを使用し、次にパラメーターをコードに直接コピーすることもできます。使用するために。
準備する
ffme 4.4
MP4またはflv形式のビデオファイル
処理の流れ
ここで次のことを説明する必要があります。
まず第一に、フィルターグラフの構文を理解する必要があります。各フィルターには少なくとも 1 つの入力と出力があります。複数の入力と出力を持つフィルターの場合、どの入力と出力を他のフィルターとリンクするかを指定する方法を指定する必要があります。ラベル(次のコマンドなど)
[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]オーバーレイ=w[out]
上記の分割フィルターは、入力ビデオ フレームを 2 つの出力にコピーし、名前を [in_1] と [in_2] として指定します。[in_2] はオーバーレイ フィルターの 2 番目の入力として使用され、[in_1] はパッド フィルターによって次のように処理されます。オーバーレイへの最初の入力。
[in] と [out] の 2 つの名前はシステムのデフォルトです。自分で変更することもできます。
さて、上のフローチャートを見てみましょう。
/*
和 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 は、フィルター文字列内のフィルターを解析し、フィルターを作成してリンクします。その中で 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;
出力、入力はそれぞれフィルター コンテキストとピン名を指定します。ここに実際のバッファとバッファシンクがあります。
次に、 avfilter_graph_parse_ptr メソッドは、文字列内のフィルターの入力ピンを持つフィルターを出力にリンクし、出力ピンを持つフィルターを入力にリンクします。このようにして、バッファー フィルターとバッファーシンク フィルターがフィルター チェーン全体に追加され、完全なフィルター チェーンが形成されます。
バッファとバッファシンクを作成したとき、バッファの出力名が入力されるように、バッファシンクの入力名が出力されるように順序付けしました。
ここでパラメータ [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;
}