summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas George <nicolas.george@normalesup.org>2013-02-21 20:21:16 +0100
committerNicolas George <nicolas.george@normalesup.org>2013-03-24 23:17:47 +0100
commit125acd215250ead008938266efcacd56743f3a2a (patch)
treef012c1ca011dc6c19cfc3cce6e02e13b57a3b963
parent00da527b44a6b7195bf2b287c3214f349cd8dd0b (diff)
lavfi: support multiple rounds of format negotiation.
Remove the temporary hack for amerge and replace it with a generic solution.
-rw-r--r--doc/filter_design.txt5
-rw-r--r--libavfilter/avfiltergraph.c109
2 files changed, 88 insertions, 26 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 <string.h>
#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))
+#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