From 125acd215250ead008938266efcacd56743f3a2a Mon Sep 17 00:00:00 2001 From: Nicolas George Date: Thu, 21 Feb 2013 20:21:16 +0100 Subject: [PATCH] lavfi: support multiple rounds of format negotiation. Remove the temporary hack for amerge and replace it with a generic solution. --- doc/filter_design.txt | 5 ++ libavfilter/avfiltergraph.c | 111 +++++++++++++++++++++++++++--------- 2 files changed, 89 insertions(+), 27 deletions(-) diff --git a/doc/filter_design.txt b/doc/filter_design.txt index 772ca9dfb0..2f9e57d66a 100644 --- a/doc/filter_design.txt +++ b/doc/filter_design.txt @@ -29,6 +29,11 @@ Format negotiation same format amongst a supported list, all it has to do is use a reference to the same list of formats. + query_formats can leave some formats unset and return AVERROR(EAGAIN) to + cause the negotiation mechanism to try again later. That can be used by + filters with complex requirements to use the format negotiated on one link + to set the formats supported on another. + Buffer references ownership and permissions =========================================== diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 89cdda33dd..1ff69fb346 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -23,6 +23,7 @@ #include #include "libavutil/avassert.h" +#include "libavutil/bprint.h" #include "libavutil/channel_layout.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" @@ -211,8 +212,9 @@ static int filter_query_formats(AVFilterContext *ctx) AVMEDIA_TYPE_VIDEO; if ((ret = ctx->filter->query_formats(ctx)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': %s\n", - ctx->name, av_err2str(ret)); + if (ret != AVERROR(EAGAIN)) + av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': %s\n", + ctx->name, av_err2str(ret)); return ret; } @@ -238,26 +240,47 @@ static int filter_query_formats(AVFilterContext *ctx) return 0; } +static int formats_declared(AVFilterContext *f) +{ + int i; + + for (i = 0; i < f->nb_inputs; i++) { + if (!f->inputs[i]->out_formats) + return 0; + if (f->inputs[i]->type == AVMEDIA_TYPE_AUDIO && + !(f->inputs[i]->out_samplerates && + f->inputs[i]->out_channel_layouts)) + return 0; + } + for (i = 0; i < f->nb_outputs; i++) { + if (!f->outputs[i]->in_formats) + return 0; + if (f->outputs[i]->type == AVMEDIA_TYPE_AUDIO && + !(f->outputs[i]->in_samplerates && + f->outputs[i]->in_channel_layouts)) + return 0; + } + return 1; +} + static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) { int i, j, ret; int scaler_count = 0, resampler_count = 0; + int count_queried = 0, count_merged = 0, count_already_merged = 0, + count_delayed = 0; - for (j = 0; j < 2; j++) { - /* ask all the sub-filters for their supported media formats */ for (i = 0; i < graph->nb_filters; i++) { - /* Call query_formats on sources first. - This is a temporary workaround for amerge, - until format renegociation is implemented. */ - if (!graph->filters[i]->nb_inputs == j) + AVFilterContext *f = graph->filters[i]; + if (formats_declared(f)) continue; - if (graph->filters[i]->filter->query_formats) - ret = filter_query_formats(graph->filters[i]); + if (f->filter->query_formats) + ret = filter_query_formats(f); else - ret = ff_default_query_formats(graph->filters[i]); - if (ret < 0) + ret = ff_default_query_formats(f); + if (ret < 0 && ret != AVERROR(EAGAIN)) return ret; - } + count_queried++; } /* go through and merge as many format lists as possible */ @@ -271,20 +294,33 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) if (!link) continue; - if (link->in_formats != link->out_formats && - !ff_merge_formats(link->in_formats, link->out_formats, - link->type)) - convert_needed = 1; - if (link->type == AVMEDIA_TYPE_AUDIO) { - if (link->in_channel_layouts != link->out_channel_layouts && - !ff_merge_channel_layouts(link->in_channel_layouts, - link->out_channel_layouts)) - convert_needed = 1; - if (link->in_samplerates != link->out_samplerates && - !ff_merge_samplerates(link->in_samplerates, - link->out_samplerates)) - convert_needed = 1; +#define MERGE_DISPATCH(field, statement) \ + if (!(link->in_ ## field && link->out_ ## field)) { \ + count_delayed++; \ + } else if (link->in_ ## field == link->out_ ## field) { \ + count_already_merged++; \ + } else { \ + count_merged++; \ + statement \ } + MERGE_DISPATCH(formats, + if (!ff_merge_formats(link->in_formats, link->out_formats, + link->type)) + convert_needed = 1; + ) + if (link->type == AVMEDIA_TYPE_AUDIO) { + MERGE_DISPATCH(channel_layouts, + if (!ff_merge_channel_layouts(link->in_channel_layouts, + link->out_channel_layouts)) + convert_needed = 1; + ) + MERGE_DISPATCH(samplerates, + if (!ff_merge_samplerates(link->in_samplerates, + link->out_samplerates)) + convert_needed = 1; + ) + } +#undef MERGE_DISPATCH if (convert_needed) { AVFilterContext *convert; @@ -368,6 +404,25 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) } } + av_log(graph, AV_LOG_DEBUG, "query_formats: " + "%d queried, %d merged, %d already done, %d delayed\n", + count_queried, count_merged, count_already_merged, count_delayed); + if (count_delayed) { + AVBPrint bp; + + if (count_queried || count_merged) + return AVERROR(EAGAIN); + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); + for (i = 0; i < graph->nb_filters; i++) + if (!formats_declared(graph->filters[i])) + av_bprintf(&bp, "%s%s", bp.len ? ", " : "", + graph->filters[i]->name); + av_log(graph, AV_LOG_ERROR, + "The following filters could not choose their formats: %s\n" + "Consider inserting the (a)format filter near their input or " + "output.\n", bp.str); + return AVERROR(EIO); + } return 0; } @@ -831,7 +886,9 @@ static int graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) int ret; /* find supported formats from sub-filters, and merge along links */ - if ((ret = query_formats(graph, log_ctx)) < 0) + while ((ret = query_formats(graph, log_ctx)) == AVERROR(EAGAIN)) + av_log(graph, AV_LOG_DEBUG, "query_formats not finished\n"); + if (ret < 0) return ret; /* Once everything is merged, it's possible that we'll still have