mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-02-04 06:08:26 +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
|
// 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…
x
Reference in New Issue
Block a user