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.
pull/389/head
Anton Khirnov 2 years ago
parent a1061d4bdc
commit dc5864a00c
  1. 157
      fftools/ffmpeg_filter.c

@ -62,6 +62,10 @@ typedef struct InputFilterPriv {
// used to hold submitted input // used to hold submitted input
AVFrame *frame; 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 // filter data type
enum AVMediaType type; enum AVMediaType type;
// source data type: AVMEDIA_TYPE_SUBTITLE for sub2video, // 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->ist = ist;
ifp->type_src = ist->st->codecpar->codec_type; ifp->type_src = ist->st->codecpar->codec_type;
ifp->type = ifp->type_src == AVMEDIA_TYPE_SUBTITLE ?
AVMEDIA_TYPE_VIDEO : ifp->type_src;
return 0; return 0;
} }
@ -505,7 +507,7 @@ void fg_free(FilterGraph **pfg)
av_frame_free(&frame); av_frame_free(&frame);
av_fifo_freep2(&ifp->frame_queue); av_fifo_freep2(&ifp->frame_queue);
} }
if (ist->sub2video.sub_queue) { if (ist && ist->sub2video.sub_queue) {
AVSubtitle sub; AVSubtitle sub;
while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0) while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0)
avsubtitle_free(&sub); avsubtitle_free(&sub);
@ -517,6 +519,7 @@ void fg_free(FilterGraph **pfg)
av_frame_free(&ifp->frame); av_frame_free(&ifp->frame);
av_buffer_unref(&ifp->hw_frames_ctx); av_buffer_unref(&ifp->hw_frames_ctx);
av_freep(&ifp->linklabel);
av_freep(&ifilter->name); av_freep(&ifilter->name);
av_freep(&fg->inputs[j]); 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); FilterGraphPriv *fgp = allocate_array_elem(&filtergraphs, sizeof(*fgp), &nb_filtergraphs);
FilterGraph *fg = &fgp->fg; FilterGraph *fg = &fgp->fg;
AVFilterInOut *inputs, *outputs;
AVFilterGraph *graph;
int ret = 0;
fg->index = nb_filtergraphs - 1; fg->index = nb_filtergraphs - 1;
fgp->graph_desc = graph_desc; fgp->graph_desc = graph_desc;
@ -549,6 +556,48 @@ FilterGraph *fg_create(char *graph_desc)
if (!fgp->frame) if (!fgp->frame)
report_and_exit(AVERROR(ENOMEM)); 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; return fg;
} }
@ -557,8 +606,6 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost,
{ {
FilterGraph *fg; FilterGraph *fg;
FilterGraphPriv *fgp; FilterGraphPriv *fgp;
OutputFilter *ofilter;
InputFilter *ifilter;
int ret; int ret;
fg = fg_create(graph_desc); fg = fg_create(graph_desc);
@ -568,26 +615,32 @@ int init_simple_filtergraph(InputStream *ist, OutputStream *ost,
fgp->is_simple = 1; fgp->is_simple = 1;
ofilter = ofilter_alloc(fg); if (fg->nb_inputs != 1 || fg->nb_outputs != 1) {
ofilter->ost = ost; 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) if (ret < 0)
return ret; return ret;
return 0; 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); FilterGraphPriv *fgp = fgp_from_fg(fg);
InputFilterPriv *ifp = ifp_from_ifilter(ifilter);
InputStream *ist = NULL; InputStream *ist = NULL;
enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); enum AVMediaType type = ifp->type;
InputFilter *ifilter;
int i, ret; int i, ret;
// TODO: support other filter types // TODO: support other filter types
@ -597,11 +650,11 @@ static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
exit_program(1); exit_program(1);
} }
if (in->name) { if (ifp->linklabel) {
AVFormatContext *s; AVFormatContext *s;
AVStream *st = NULL; AVStream *st = NULL;
char *p; 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) { if (file_idx < 0 || file_idx >= nb_input_files) {
av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n", 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); ist = ist_find_unused(type);
if (!ist) { if (!ist) {
av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for " av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for "
"unlabeled input pad %d on filter %s\n", in->pad_idx, "unlabeled input pad %s\n", ifilter->name);
in->filter_ctx->name);
exit_program(1); exit_program(1);
} }
} }
av_assert0(ist); av_assert0(ist);
ifilter = ifilter_alloc(fg);
ifilter->name = describe_filter_link(fg, in, 1);
ret = ifilter_bind_ist(ifilter, ist); ret = ifilter_bind_ist(ifilter, ist);
if (ret < 0) { if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, av_log(NULL, AV_LOG_ERROR,
"Error binding an input stream to complex filtergraph input %s.\n", "Error binding an input stream to complex filtergraph input %s.\n",
in->name ? in->name : ""); ifilter->name);
exit_program(1); exit_program(1);
} }
} }
int init_complex_filtergraph(FilterGraph *fg) int init_complex_filtergraph(FilterGraph *fg)
{ {
FilterGraphPriv *fgp = fgp_from_fg(fg); // bind filtergraph inputs to input streams
AVFilterInOut *inputs, *outputs, *cur; for (int i = 0; i < fg->nb_inputs; i++)
AVFilterGraph *graph; init_input_filter(fg, fg->inputs[i]);
int ret = 0; return 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;
} }
static int insert_trim(int64_t start_time, int64_t duration, 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) if ((ret = graph_parse(fg->graph, graph_desc, &inputs, &outputs, hw_device)) < 0)
goto fail; 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++) for (cur = inputs, i = 0; cur; cur = cur->next, i++)
if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) { if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) {
avfilter_inout_free(&inputs); avfilter_inout_free(&inputs);

Loading…
Cancel
Save