mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-01-29 22:00:58 +02:00
fftools/ffmpeg_filter: create Input/OutputFilters together with FilterGraph
This way the list of filtergraph inputs/outputs is always known after FilterGraph creation. This will allow treating simple and complex filtergraphs in a more uniform manner.
This commit is contained in:
parent
a1061d4bdc
commit
dc5864a00c
@ -62,6 +62,10 @@ typedef struct InputFilterPriv {
|
||||
// used to hold submitted input
|
||||
AVFrame *frame;
|
||||
|
||||
/* for filters that are not yet bound to an input stream,
|
||||
* this stores the input linklabel, if any */
|
||||
uint8_t *linklabel;
|
||||
|
||||
// filter data type
|
||||
enum AVMediaType type;
|
||||
// source data type: AVMEDIA_TYPE_SUBTITLE for sub2video,
|
||||
@ -456,8 +460,6 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist)
|
||||
|
||||
ifp->ist = ist;
|
||||
ifp->type_src = ist->st->codecpar->codec_type;
|
||||
ifp->type = ifp->type_src == AVMEDIA_TYPE_SUBTITLE ?
|
||||
AVMEDIA_TYPE_VIDEO : ifp->type_src;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -505,7 +507,7 @@ void fg_free(FilterGraph **pfg)
|
||||
av_frame_free(&frame);
|
||||
av_fifo_freep2(&ifp->frame_queue);
|
||||
}
|
||||
if (ist->sub2video.sub_queue) {
|
||||
if (ist && ist->sub2video.sub_queue) {
|
||||
AVSubtitle sub;
|
||||
while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0)
|
||||
avsubtitle_free(&sub);
|
||||
@ -517,6 +519,7 @@ void fg_free(FilterGraph **pfg)
|
||||
av_frame_free(&ifp->frame);
|
||||
|
||||
av_buffer_unref(&ifp->hw_frames_ctx);
|
||||
av_freep(&ifp->linklabel);
|
||||
av_freep(&ifilter->name);
|
||||
av_freep(&fg->inputs[j]);
|
||||
}
|
||||
@ -542,6 +545,10 @@ FilterGraph *fg_create(char *graph_desc)
|
||||
FilterGraphPriv *fgp = allocate_array_elem(&filtergraphs, sizeof(*fgp), &nb_filtergraphs);
|
||||
FilterGraph *fg = &fgp->fg;
|
||||
|
||||
AVFilterInOut *inputs, *outputs;
|
||||
AVFilterGraph *graph;
|
||||
int ret = 0;
|
||||
|
||||
fg->index = nb_filtergraphs - 1;
|
||||
fgp->graph_desc = graph_desc;
|
||||
|
||||
@ -549,6 +556,48 @@ FilterGraph *fg_create(char *graph_desc)
|
||||
if (!fgp->frame)
|
||||
report_and_exit(AVERROR(ENOMEM));
|
||||
|
||||
/* this graph is only used for determining the kinds of inputs
|
||||
* and outputs we have, and is discarded on exit from this function */
|
||||
graph = avfilter_graph_alloc();
|
||||
if (!graph)
|
||||
report_and_exit(AVERROR(ENOMEM));
|
||||
graph->nb_threads = 1;
|
||||
|
||||
ret = graph_parse(graph, fgp->graph_desc, &inputs, &outputs, NULL);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
for (AVFilterInOut *cur = inputs; cur; cur = cur->next) {
|
||||
InputFilter *const ifilter = ifilter_alloc(fg);
|
||||
InputFilterPriv *ifp = ifp_from_ifilter(ifilter);
|
||||
|
||||
ifp->linklabel = cur->name;
|
||||
cur->name = NULL;
|
||||
|
||||
ifp->type = avfilter_pad_get_type(cur->filter_ctx->input_pads,
|
||||
cur->pad_idx);
|
||||
ifilter->name = describe_filter_link(fg, cur, 1);
|
||||
}
|
||||
|
||||
for (AVFilterInOut *cur = outputs; cur; cur = cur->next) {
|
||||
OutputFilter *const ofilter = ofilter_alloc(fg);
|
||||
|
||||
ofilter->linklabel = cur->name;
|
||||
cur->name = NULL;
|
||||
|
||||
ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads,
|
||||
cur->pad_idx);
|
||||
ofilter->name = describe_filter_link(fg, cur, 0);
|
||||
}
|
||||
|
||||
fail:
|
||||
avfilter_inout_free(&inputs);
|
||||
avfilter_inout_free(&outputs);
|
||||
avfilter_graph_free(&graph);
|
||||
|
||||
if (ret < 0)
|
||||
report_and_exit(ret);
|
||||
|
||||
return fg;
|
||||
}
|
||||
|
||||
@ -557,8 +606,6 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost,
|
||||
{
|
||||
FilterGraph *fg;
|
||||
FilterGraphPriv *fgp;
|
||||
OutputFilter *ofilter;
|
||||
InputFilter *ifilter;
|
||||
int ret;
|
||||
|
||||
fg = fg_create(graph_desc);
|
||||
@ -568,26 +615,32 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost,
|
||||
|
||||
fgp->is_simple = 1;
|
||||
|
||||
ofilter = ofilter_alloc(fg);
|
||||
ofilter->ost = ost;
|
||||
if (fg->nb_inputs != 1 || fg->nb_outputs != 1) {
|
||||
av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected "
|
||||
"to have exactly 1 input and 1 output. "
|
||||
"However, it had %d input(s) and %d output(s). Please adjust, "
|
||||
"or use a complex filtergraph (-filter_complex) instead.\n",
|
||||
graph_desc, fg->nb_inputs, fg->nb_outputs);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
ost->filter = ofilter;
|
||||
fg->outputs[0]->ost = ost;
|
||||
|
||||
ifilter = ifilter_alloc(fg);
|
||||
ost->filter = fg->outputs[0];
|
||||
|
||||
ret = ifilter_bind_ist(ifilter, ist);
|
||||
ret = ifilter_bind_ist(fg->inputs[0], ist);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
|
||||
static void init_input_filter(FilterGraph *fg, InputFilter *ifilter)
|
||||
{
|
||||
FilterGraphPriv *fgp = fgp_from_fg(fg);
|
||||
InputFilterPriv *ifp = ifp_from_ifilter(ifilter);
|
||||
InputStream *ist = NULL;
|
||||
enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
|
||||
InputFilter *ifilter;
|
||||
enum AVMediaType type = ifp->type;
|
||||
int i, ret;
|
||||
|
||||
// TODO: support other filter types
|
||||
@ -597,11 +650,11 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
|
||||
exit_program(1);
|
||||
}
|
||||
|
||||
if (in->name) {
|
||||
if (ifp->linklabel) {
|
||||
AVFormatContext *s;
|
||||
AVStream *st = NULL;
|
||||
char *p;
|
||||
int file_idx = strtol(in->name, &p, 0);
|
||||
int file_idx = strtol(ifp->linklabel, &p, 0);
|
||||
|
||||
if (file_idx < 0 || file_idx >= nb_input_files) {
|
||||
av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n",
|
||||
@ -631,63 +684,27 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
|
||||
ist = ist_find_unused(type);
|
||||
if (!ist) {
|
||||
av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for "
|
||||
"unlabeled input pad %d on filter %s\n", in->pad_idx,
|
||||
in->filter_ctx->name);
|
||||
"unlabeled input pad %s\n", ifilter->name);
|
||||
exit_program(1);
|
||||
}
|
||||
}
|
||||
av_assert0(ist);
|
||||
|
||||
ifilter = ifilter_alloc(fg);
|
||||
ifilter->name = describe_filter_link(fg, in, 1);
|
||||
|
||||
ret = ifilter_bind_ist(ifilter, ist);
|
||||
if (ret < 0) {
|
||||
av_log(NULL, AV_LOG_ERROR,
|
||||
"Error binding an input stream to complex filtergraph input %s.\n",
|
||||
in->name ? in->name : "");
|
||||
ifilter->name);
|
||||
exit_program(1);
|
||||
}
|
||||
}
|
||||
|
||||
int init_complex_filtergraph(FilterGraph *fg)
|
||||
{
|
||||
FilterGraphPriv *fgp = fgp_from_fg(fg);
|
||||
AVFilterInOut *inputs, *outputs, *cur;
|
||||
AVFilterGraph *graph;
|
||||
int ret = 0;
|
||||
|
||||
/* this graph is only used for determining the kinds of inputs
|
||||
* and outputs we have, and is discarded on exit from this function */
|
||||
graph = avfilter_graph_alloc();
|
||||
if (!graph)
|
||||
return AVERROR(ENOMEM);
|
||||
graph->nb_threads = 1;
|
||||
|
||||
ret = graph_parse(graph, fgp->graph_desc, &inputs, &outputs, NULL);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
for (cur = inputs; cur; cur = cur->next)
|
||||
init_input_filter(fg, cur);
|
||||
|
||||
for (cur = outputs; cur;) {
|
||||
OutputFilter *const ofilter = ofilter_alloc(fg);
|
||||
|
||||
ofilter->linklabel = cur->name;
|
||||
cur->name = NULL;
|
||||
|
||||
ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads,
|
||||
cur->pad_idx);
|
||||
ofilter->name = describe_filter_link(fg, cur, 0);
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
fail:
|
||||
avfilter_inout_free(&inputs);
|
||||
avfilter_inout_free(&outputs);
|
||||
avfilter_graph_free(&graph);
|
||||
return ret;
|
||||
// bind filtergraph inputs to input streams
|
||||
for (int i = 0; i < fg->nb_inputs; i++)
|
||||
init_input_filter(fg, fg->inputs[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int insert_trim(int64_t start_time, int64_t duration,
|
||||
@ -1324,32 +1341,6 @@ int configure_filtergraph(FilterGraph *fg)
|
||||
if ((ret = graph_parse(fg->graph, graph_desc, &inputs, &outputs, hw_device)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (simple && (!inputs || inputs->next || !outputs || outputs->next)) {
|
||||
const char *num_inputs;
|
||||
const char *num_outputs;
|
||||
if (!outputs) {
|
||||
num_outputs = "0";
|
||||
} else if (outputs->next) {
|
||||
num_outputs = ">1";
|
||||
} else {
|
||||
num_outputs = "1";
|
||||
}
|
||||
if (!inputs) {
|
||||
num_inputs = "0";
|
||||
} else if (inputs->next) {
|
||||
num_inputs = ">1";
|
||||
} else {
|
||||
num_inputs = "1";
|
||||
}
|
||||
av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected "
|
||||
"to have exactly 1 input and 1 output."
|
||||
" However, it had %s input(s) and %s output(s)."
|
||||
" Please adjust, or use a complex filtergraph (-filter_complex) instead.\n",
|
||||
graph_desc, num_inputs, num_outputs);
|
||||
ret = AVERROR(EINVAL);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (cur = inputs, i = 0; cur; cur = cur->next, i++)
|
||||
if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) {
|
||||
avfilter_inout_free(&inputs);
|
||||
|
Loading…
x
Reference in New Issue
Block a user