diff mbox series

[FFmpeg-devel,2/2] lavfi/formats: put merge functions in structures.

Message ID 20210724152947.258052-2-george@nsup.org
State Accepted
Commit 86d3dd5627b5c8c179aa48c7e4834a69371a14e6
Headers show
Series [FFmpeg-devel,1/2] lavu/internal: add FF_FIELD_AT().
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Nicolas George July 24, 2021, 3:29 p.m. UTC
It makes the code clearer and will allow adding new stages
of negotiation easier.

Signed-off-by: Nicolas George <george@nsup.org>
---
 libavfilter/avfiltergraph.c |  96 +++++++++++++-----------------
 libavfilter/formats.c       | 114 ++++++++++++++++++++++++++++++++----
 libavfilter/formats.h       |  41 ++++---------
 3 files changed, 157 insertions(+), 94 deletions(-)
diff mbox series

Patch

diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index 5389d82d9f..37f09cb686 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -458,48 +458,41 @@  static int query_formats(AVFilterGraph *graph, AVClass *log_ctx)
 
         for (j = 0; j < filter->nb_inputs; j++) {
             AVFilterLink *link = filter->inputs[j];
+            const AVFilterNegotiation *neg;
+            unsigned neg_step;
             int convert_needed = 0;
 
             if (!link)
                 continue;
 
-            if (link->incfg.formats != link->outcfg.formats
-                && link->incfg.formats && link->outcfg.formats)
-                if (!ff_can_merge_formats(link->incfg.formats, link->outcfg.formats,
-                                          link->type))
+            neg = ff_filter_get_negotiation(link);
+            av_assert0(neg);
+            for (neg_step = 1; neg_step < neg->nb; neg_step++) {
+                const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
+                void *a = FF_FIELD_AT(void *, m->offset, link->incfg);
+                void *b = FF_FIELD_AT(void *, m->offset, link->outcfg);
+                if (a && b && a != b && !m->can_merge(a, b)) {
                     convert_needed = 1;
-            if (link->type == AVMEDIA_TYPE_AUDIO) {
-                if (link->incfg.samplerates != link->outcfg.samplerates
-                    && link->incfg.samplerates && link->outcfg.samplerates)
-                    if (!ff_can_merge_samplerates(link->incfg.samplerates,
-                                                  link->outcfg.samplerates))
-                        convert_needed = 1;
-            }
-
-#define CHECKED_MERGE(field, ...) ((ret = ff_merge_ ## field(__VA_ARGS__)) <= 0)
-#define MERGE_DISPATCH(field, ...)                                           \
-            if (!(link->incfg.field && link->outcfg.field)) {                \
-                count_delayed++;                                             \
-            } else if (link->incfg.field == link->outcfg.field) {            \
-                count_already_merged++;                                      \
-            } else if (!convert_needed) {                                    \
-                count_merged++;                                              \
-                if (CHECKED_MERGE(field, __VA_ARGS__)) {                     \
-                    if (ret < 0)                                             \
-                        return ret;                                          \
-                    convert_needed = 1;                                      \
-                }                                                            \
+                    break;
+                }
             }
-
-            if (link->type == AVMEDIA_TYPE_AUDIO) {
-                MERGE_DISPATCH(channel_layouts, link->incfg.channel_layouts,
-                                                link->outcfg.channel_layouts)
-                MERGE_DISPATCH(samplerates, link->incfg.samplerates,
-                                            link->outcfg.samplerates)
+            for (neg_step = 0; neg_step < neg->nb; neg_step++) {
+                const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
+                void *a = FF_FIELD_AT(void *, m->offset, link->incfg);
+                void *b = FF_FIELD_AT(void *, m->offset, link->outcfg);
+                if (!(a && b)) {
+                    count_delayed++;
+                } else if (a == b) {
+                    count_already_merged++;
+                } else if (!convert_needed) {
+                    count_merged++;
+                    ret = m->merge(a, b);
+                    if (ret < 0)
+                        return ret;
+                    if (!ret)
+                        convert_needed = 1;
+                }
             }
-            MERGE_DISPATCH(formats, link->incfg.formats,
-                           link->outcfg.formats, link->type)
-#undef MERGE_DISPATCH
 
             if (convert_needed) {
                 AVFilterContext *convert;
@@ -572,26 +565,21 @@  static int query_formats(AVFilterGraph *graph, AVClass *log_ctx)
                     av_assert0(outlink-> incfg.channel_layouts->refcount > 0);
                     av_assert0(outlink->outcfg.channel_layouts->refcount > 0);
                 }
-                if (CHECKED_MERGE(formats, inlink->incfg.formats,
-                                  inlink->outcfg.formats, inlink->type)         ||
-                    CHECKED_MERGE(formats, outlink->incfg.formats,
-                                  outlink->outcfg.formats, outlink->type)       ||
-                    inlink->type == AVMEDIA_TYPE_AUDIO &&
-                    (CHECKED_MERGE(samplerates, inlink->incfg.samplerates,
-                                                inlink->outcfg.samplerates)  ||
-                     CHECKED_MERGE(channel_layouts, inlink->incfg.channel_layouts,
-                                   inlink->outcfg.channel_layouts))             ||
-                    outlink->type == AVMEDIA_TYPE_AUDIO &&
-                    (CHECKED_MERGE(samplerates, outlink->incfg.samplerates,
-                                                outlink->outcfg.samplerates) ||
-                     CHECKED_MERGE(channel_layouts, outlink->incfg.channel_layouts,
-                                                    outlink->outcfg.channel_layouts))) {
-                    if (ret < 0)
-                        return ret;
-                    av_log(log_ctx, AV_LOG_ERROR,
-                           "Impossible to convert between the formats supported by the filter "
-                           "'%s' and the filter '%s'\n", link->src->name, link->dst->name);
-                    return AVERROR(ENOSYS);
+                for (neg_step = 0; neg_step < neg->nb; neg_step++) {
+                    const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
+                    void *ia = FF_FIELD_AT(void *, m->offset, inlink->incfg);
+                    void *ib = FF_FIELD_AT(void *, m->offset, inlink->outcfg);
+                    void *oa = FF_FIELD_AT(void *, m->offset, outlink->incfg);
+                    void *ob = FF_FIELD_AT(void *, m->offset, outlink->outcfg);
+                    if ((ret = m->merge(ia, ib)) <= 0 ||
+                        (ret = m->merge(oa, ob)) <= 0) {
+                        if (ret < 0)
+                            return ret;
+                        av_log(log_ctx, AV_LOG_ERROR,
+                               "Impossible to convert between the formats supported by the filter "
+                               "'%s' and the filter '%s'\n", link->src->name, link->dst->name);
+                        return AVERROR(ENOSYS);
+                    }
                 }
             }
         }
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index 6c05b118c9..eceae8ba9c 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -100,6 +100,8 @@  static int merge_formats_internal(AVFilterFormats *a, AVFilterFormats *b,
     int alpha1=0, alpha2=0;
     int chroma1=0, chroma2=0;
 
+    av_assert2(check || (a->refcount && b->refcount));
+
     if (a == b)
         return 1;
 
@@ -132,43 +134,86 @@  static int merge_formats_internal(AVFilterFormats *a, AVFilterFormats *b,
     return 1;
 }
 
-int ff_can_merge_formats(const AVFilterFormats *a, const AVFilterFormats *b,
-                         enum AVMediaType type)
+
+/**
+ * Check the formats lists for compatibility for merging without actually
+ * merging.
+ *
+ * @return 1 if they are compatible, 0 if not.
+ */
+static int can_merge_pix_fmts(const void *a, const void *b)
 {
     return merge_formats_internal((AVFilterFormats *)a,
-                                  (AVFilterFormats *)b, type, 1);
+                                  (AVFilterFormats *)b, AVMEDIA_TYPE_VIDEO, 1);
+}
+
+/**
+ * Merge the formats lists if they are compatible and update all the
+ * references of a and b to point to the combined list and free the old
+ * lists as needed. The combined list usually contains the intersection of
+ * the lists of a and b.
+ *
+ * Both a and b must have owners (i.e. refcount > 0) for these functions.
+ *
+ * @return 1 if merging succeeded, 0 if a and b are incompatible
+ *         and negative AVERROR code on failure.
+ *         a and b are unmodified if 0 is returned.
+ */
+static int merge_pix_fmts(void *a, void *b)
+{
+    return merge_formats_internal(a, b, AVMEDIA_TYPE_VIDEO, 0);
 }
 
-int ff_merge_formats(AVFilterFormats *a, AVFilterFormats *b,
-                     enum AVMediaType type)
+/**
+ * See can_merge_pix_fmts().
+ */
+static int can_merge_sample_fmts(const void *a, const void *b)
 {
-    av_assert2(a->refcount && b->refcount);
-    return merge_formats_internal(a, b, type, 0);
+    return merge_formats_internal((AVFilterFormats *)a,
+                                  (AVFilterFormats *)b, AVMEDIA_TYPE_AUDIO, 1);
+}
+
+/**
+ * See merge_pix_fmts().
+ */
+static int merge_sample_fmts(void *a, void *b)
+{
+    return merge_formats_internal(a, b, AVMEDIA_TYPE_AUDIO, 0);
 }
 
 static int merge_samplerates_internal(AVFilterFormats *a,
                                       AVFilterFormats *b, int check)
 {
+    av_assert2(check || (a->refcount && b->refcount));
     if (a == b) return 1;
 
     MERGE_FORMATS(a, b, formats, nb_formats, AVFilterFormats, check, 1);
     return 1;
 }
 
-int ff_can_merge_samplerates(const AVFilterFormats *a, const AVFilterFormats *b)
+/**
+ * See can_merge_pix_fmts().
+ */
+static int can_merge_samplerates(const void *a, const void *b)
 {
     return merge_samplerates_internal((AVFilterFormats *)a, (AVFilterFormats *)b, 1);
 }
 
-int ff_merge_samplerates(AVFilterFormats *a, AVFilterFormats *b)
+/**
+ * See merge_pix_fmts().
+ */
+static int merge_samplerates(void *a, void *b)
 {
-    av_assert2(a->refcount && b->refcount);
     return merge_samplerates_internal(a, b, 0);
 }
 
-int ff_merge_channel_layouts(AVFilterChannelLayouts *a,
-                             AVFilterChannelLayouts *b)
+/**
+ * See merge_pix_fmts().
+ */
+static int merge_channel_layouts(void *va, void *vb)
 {
+    AVFilterChannelLayouts *a = va;
+    AVFilterChannelLayouts *b = vb;
     uint64_t *channel_layouts;
     unsigned a_all = a->all_layouts + a->all_counts;
     unsigned b_all = b->all_layouts + b->all_counts;
@@ -255,6 +300,51 @@  int ff_merge_channel_layouts(AVFilterChannelLayouts *a,
     return 1;
 }
 
+static const AVFilterFormatsMerger mergers_video[] = {
+    {
+        .offset     = offsetof(AVFilterFormatsConfig, formats),
+        .merge      = merge_pix_fmts,
+        .can_merge  = can_merge_pix_fmts,
+    },
+};
+
+static const AVFilterFormatsMerger mergers_audio[] = {
+    {
+        .offset     = offsetof(AVFilterFormatsConfig, channel_layouts),
+        .merge      = merge_channel_layouts,
+        .can_merge  = NULL,
+    },
+    {
+        .offset     = offsetof(AVFilterFormatsConfig, samplerates),
+        .merge      = merge_samplerates,
+        .can_merge  = can_merge_samplerates,
+    },
+    {
+        .offset     = offsetof(AVFilterFormatsConfig, formats),
+        .merge      = merge_sample_fmts,
+        .can_merge  = can_merge_sample_fmts,
+    },
+};
+
+static const AVFilterNegotiation negotiate_video = {
+    .nb = FF_ARRAY_ELEMS(mergers_video),
+    .mergers = mergers_video,
+};
+
+static const AVFilterNegotiation negotiate_audio = {
+    .nb = FF_ARRAY_ELEMS(mergers_audio),
+    .mergers = mergers_audio,
+};
+
+const AVFilterNegotiation *ff_filter_get_negotiation(AVFilterLink *link)
+{
+    switch (link->type) {
+    case AVMEDIA_TYPE_VIDEO: return &negotiate_video;
+    case AVMEDIA_TYPE_AUDIO: return &negotiate_audio;
+    default: return NULL;
+    }
+}
+
 int ff_fmt_is_in(int fmt, const int *fmts)
 {
     const int *p;
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index 65acc939e3..7a1f8408ac 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -69,6 +69,19 @@  struct AVFilterFormats {
     struct AVFilterFormats ***refs; ///< references to this list
 };
 
+typedef struct AVFilterFormatMerger {
+    unsigned offset;
+    int (*merge)(void *a, void *b);
+    int (*can_merge)(const void *a, const void *b);
+} AVFilterFormatsMerger;
+
+typedef struct AVFilterNegotiation {
+    unsigned nb;
+    const AVFilterFormatsMerger *mergers;
+} AVFilterNegotiation;
+
+const AVFilterNegotiation *ff_filter_get_negotiation(AVFilterLink *link);
+
 /**
  * A list of supported channel layouts.
  *
@@ -108,34 +121,6 @@  struct AVFilterChannelLayouts {
 #define FF_LAYOUT2COUNT(l) (((l) & 0x8000000000000000ULL) ? \
                            (int)((l) & 0x7FFFFFFF) : 0)
 
-/**
- * Check the formats/samplerates lists for compatibility for merging
- * without actually merging.
- *
- * @return 1 if they are compatible, 0 if not.
- */
-int ff_can_merge_formats(const AVFilterFormats *a, const AVFilterFormats *b,
-                         enum AVMediaType type);
-int ff_can_merge_samplerates(const AVFilterFormats *a, const AVFilterFormats *b);
-
-/**
- * Merge the formats/channel layouts/samplerates lists if they are compatible
- * and update all the references of a and b to point to the combined list and
- * free the old lists as needed. The combined list usually contains the
- * intersection of the lists of a and b.
- *
- * Both a and b must have owners (i.e. refcount > 0) for these functions.
- *
- * @return 1 if merging succeeded, 0 if a and b are incompatible
- *         and negative AVERROR code on failure.
- *         a and b are unmodified if 0 is returned.
- */
-int ff_merge_channel_layouts(AVFilterChannelLayouts *a,
-                             AVFilterChannelLayouts *b);
-int ff_merge_formats(AVFilterFormats *a, AVFilterFormats *b,
-                     enum AVMediaType type);
-int ff_merge_samplerates(AVFilterFormats *a, AVFilterFormats *b);
-
 /**
  * Construct an empty AVFilterChannelLayouts/AVFilterFormats struct --
  * representing any channel layout (with known disposition)/sample rate.