[FFmpeg-devel] [PATCH] lavfi: support multiple rounds of format negotiation.

Nicolas George nicolas.george at normalesup.org
Sun Mar 24 19:43:21 CET 2013


Remove the temporary hack for amerge and replace it with a
generic solution.

Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
 doc/filter_design.txt       |    5 ++
 libavfilter/avfiltergraph.c |  109 ++++++++++++++++++++++++++++++++-----------
 2 files changed, 88 insertions(+), 26 deletions(-)


Stefano:
> Do you mean AVERROR(EAGAIN)?

Yes, of course.

> I suggest: merge_already -> merge_already_done

All these variables have been renamed.

> nit++: more meaningful variable (e.g. "ctx" or even "filter_ctx"
> should be ok)

I do not think that ctx is more meaningful than f (like *f*ilter).

> queried"), but the formats may not be set. So a possibly more
> descriptive name could be:
> formats_selected()

Renamed to "formats_declared()".

> EINVAL may be more adequate

The input is not really invalid. EIO is not perfect, but I believe it is
closer to the mark than EINVAL.

Thanks for the review. Patch updated, with an additional corner case taken
care of.




diff --git a/doc/filter_design.txt b/doc/filter_design.txt
index 772ca9d..2f9e57d 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 89cdda3..449e5cc 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_query = 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_query++;
     }
 
     /* 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_query, count_merged, count_already_merged, count_delayed);
+    if (count_delayed) {
+        AVBPrint bp;
+
+        if (count_query || 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
-- 
1.7.10.4



More information about the ffmpeg-devel mailing list