diff mbox series

[FFmpeg-devel,3/3] avfilter/avfiltergraph: add an auto hwmap filter

Message ID 20220629063246.183-3-tong1.wu@intel.com
State New
Headers show
Series [FFmpeg-devel,1/3] avutil/hwcontext: add a function to get the AVHWDeviceType | expand

Checks

Context Check Description
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_fate_x86 success Make fate finished
yinshiyou/make_loongarch64 warning New warnings during build
andriy/make_x86 warning New warnings during build
andriy/make_fate_armv7_RPi4 success Make fate finished
andriy/make_armv7_RPi4 warning New warnings during build

Commit Message

Wu, Tong1 June 29, 2022, 6:32 a.m. UTC
When two formats lists cannot be merged, a scale filter is
auto-inserted. However, when it comes to hardware map, we have to
manually add a hwmap filter to do the conversion. This patch introduces
an auto hwmap filter to do the hwmap conversion automatically and
modifies the negotiation structures, making it extensible for more auto
filters.

Signed-off-by: Tong Wu <tong1.wu@intel.com>
---
 libavfilter/avfiltergraph.c | 183 ++++++++++++++++++++++++------------
 libavfilter/formats.c       |  26 ++++-
 libavfilter/formats.h       |   9 +-
 3 files changed, 150 insertions(+), 68 deletions(-)
diff mbox series

Patch

diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index b7dbfc063b..0f12f2d802 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -393,6 +393,83 @@  static int formats_declared(AVFilterContext *f)
     return 1;
 }
 
+static int insert_auto_filter(AVFilterContext **convert, AVFilterGraph *graph,
+                              AVFilterLink *link, const AVFilterNegotiation *neg,
+                              unsigned conv_step, int *converter_count, void *log_ctx)
+{
+    int ret;
+    const AVFilter *filter;
+    AVFilterContext *ctx;
+    AVFilterLink *inlink, *outlink;
+    char inst_name[30];
+    const char *opts = neg->conversion_filters[conv_step].conversion_opts_offset == 0 ? NULL :
+                       FF_FIELD_AT(char *, neg->conversion_filters[conv_step].conversion_opts_offset, *graph);
+    const char *name = neg->conversion_filters[conv_step].conversion_filter;
+
+    if (graph->disable_auto_convert) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "The filters '%s' and '%s' do not have a common format "
+               "and automatic conversion is disabled.\n",
+               link->src->name, link->dst->name);
+        return AVERROR(EINVAL);
+    }
+
+    if (!(filter = avfilter_get_by_name(name))) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "'%s' filter not present, cannot convert formats.\n",
+               name);
+        return AVERROR(EINVAL);
+    }
+    snprintf(inst_name, sizeof(inst_name), "auto_%s_%d",
+             name, (*converter_count)++);
+    ret = avfilter_graph_create_filter(&ctx, filter, inst_name, opts, NULL, graph);
+    if (ret < 0)
+        return ret;
+
+    if ((ret = avfilter_insert_filter(link, ctx, 0, 0)) < 0)
+        return ret;
+
+    if ((ret = filter_query_formats(ctx)) < 0)
+        return ret;
+
+    inlink  = ctx->inputs[0];
+    outlink = ctx->outputs[0];
+    av_assert0( inlink->incfg.formats->refcount > 0);
+    av_assert0( inlink->outcfg.formats->refcount > 0);
+    av_assert0(outlink->incfg.formats->refcount > 0);
+    av_assert0(outlink->outcfg.formats->refcount > 0);
+    if (outlink->type == AVMEDIA_TYPE_AUDIO) {
+        av_assert0( inlink-> incfg.samplerates->refcount > 0);
+        av_assert0( inlink->outcfg.samplerates->refcount > 0);
+        av_assert0(outlink-> incfg.samplerates->refcount > 0);
+        av_assert0(outlink->outcfg.samplerates->refcount > 0);
+        av_assert0( inlink-> incfg.channel_layouts->refcount > 0);
+        av_assert0( inlink->outcfg.channel_layouts->refcount > 0);
+        av_assert0(outlink-> incfg.channel_layouts->refcount > 0);
+        av_assert0(outlink->outcfg.channel_layouts->refcount > 0);
+    }
+
+    *convert = ctx;
+    return 0;
+}
+
+static int merge_auto_filter(AVFilterContext *convert, const AVFilterNegotiation *neg)
+{
+    int neg_step, ret;
+    AVFilterLink *inlink  = convert->inputs[0];
+    AVFilterLink *outlink = convert->outputs[0];
+#define MERGE(merger, link)                                                  \
+    ((merger)->merge(FF_FIELD_AT(void *, (merger)->offset, (link)->incfg),   \
+                     FF_FIELD_AT(void *, (merger)->offset, (link)->outcfg)))
+    for (neg_step = 0; neg_step < neg->nb_mergers; neg_step++) {
+        const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
+        if ((ret = MERGE(m,  inlink)) <= 0 ||
+            (ret = MERGE(m, outlink)) <= 0)
+                break;
+    }
+    return ret;
+}
+
 /**
  * Perform one round of query_formats() and merging formats lists on the
  * filter graph.
@@ -433,7 +510,7 @@  static int query_formats(AVFilterGraph *graph, void *log_ctx)
         for (j = 0; j < filter->nb_inputs; j++) {
             AVFilterLink *link = filter->inputs[j];
             const AVFilterNegotiation *neg;
-            unsigned neg_step;
+            unsigned neg_step, conv_step;
             int convert_needed = 0;
 
             if (!link)
@@ -469,68 +546,50 @@  static int query_formats(AVFilterGraph *graph, void *log_ctx)
             }
 
             if (convert_needed) {
-                AVFilterContext *convert;
-                const AVFilter *filter;
-                AVFilterLink *inlink, *outlink;
-                char inst_name[30];
-                const char *opts;
-
-                if (graph->disable_auto_convert) {
-                    av_log(log_ctx, AV_LOG_ERROR,
-                           "The filters '%s' and '%s' do not have a common format "
-                           "and automatic conversion is disabled.\n",
-                           link->src->name, link->dst->name);
-                    return AVERROR(EINVAL);
-                }
-
-                /* couldn't merge format lists. auto-insert conversion filter */
-                if (!(filter = avfilter_get_by_name(neg->conversion_filter))) {
-                    av_log(log_ctx, AV_LOG_ERROR,
-                           "'%s' filter not present, cannot convert formats.\n",
-                           neg->conversion_filter);
-                    return AVERROR(EINVAL);
-                }
-                snprintf(inst_name, sizeof(inst_name), "auto_%s_%d",
-                         neg->conversion_filter, converter_count++);
-                opts = FF_FIELD_AT(char *, neg->conversion_opts_offset, *graph);
-                ret = avfilter_graph_create_filter(&convert, filter, inst_name, opts, NULL, graph);
-                if (ret < 0)
-                    return ret;
-                if ((ret = avfilter_insert_filter(link, convert, 0, 0)) < 0)
-                    return ret;
-
-                if ((ret = filter_query_formats(convert)) < 0)
-                    return ret;
+                for (conv_step = 0; conv_step < neg->nb_conversion_filters; conv_step++) {
+                    AVFilterContext *convert;
+                    ret = insert_auto_filter(&convert, graph, link, neg,
+                                             conv_step, &converter_count, log_ctx);
+                    if (ret < 0) {
+                        av_log(log_ctx, AV_LOG_ERROR, "Failed to insert an auto filter.\n");
+                        return ret;
+                    }
 
-                inlink  = convert->inputs[0];
-                outlink = convert->outputs[0];
-                av_assert0( inlink->incfg.formats->refcount > 0);
-                av_assert0( inlink->outcfg.formats->refcount > 0);
-                av_assert0(outlink->incfg.formats->refcount > 0);
-                av_assert0(outlink->outcfg.formats->refcount > 0);
-                if (outlink->type == AVMEDIA_TYPE_AUDIO) {
-                    av_assert0( inlink-> incfg.samplerates->refcount > 0);
-                    av_assert0( inlink->outcfg.samplerates->refcount > 0);
-                    av_assert0(outlink-> incfg.samplerates->refcount > 0);
-                    av_assert0(outlink->outcfg.samplerates->refcount > 0);
-                    av_assert0( inlink-> incfg.channel_layouts->refcount > 0);
-                    av_assert0( inlink->outcfg.channel_layouts->refcount > 0);
-                    av_assert0(outlink-> incfg.channel_layouts->refcount > 0);
-                    av_assert0(outlink->outcfg.channel_layouts->refcount > 0);
-                }
-#define MERGE(merger, link)                                                  \
-    ((merger)->merge(FF_FIELD_AT(void *, (merger)->offset, (link)->incfg),   \
-                     FF_FIELD_AT(void *, (merger)->offset, (link)->outcfg)))
-                for (neg_step = 0; neg_step < neg->nb_mergers; neg_step++) {
-                    const AVFilterFormatsMerger *m = &neg->mergers[neg_step];
-                    if ((ret = MERGE(m,  inlink)) <= 0 ||
-                        (ret = MERGE(m, outlink)) <= 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);
+                    ret = merge_auto_filter(convert, neg);
+                    if (ret < 0)
+                        return ret;
+                    else if (ret > 0)
+                        break;
+                    else if (conv_step < neg->nb_conversion_filters - 1) {
+                            AVFilterLink *inlink  = convert->inputs[0];
+                            AVFilterLink *outlink = convert->outputs[0];
+                            av_log(log_ctx, AV_LOG_VERBOSE,
+                                   "Impossible to convert between the formats supported by the filter "
+                                   "'%s' and the filter '%s', try another conversion filter.\n",
+                                   link->src->name, link->dst->name);
+                            unsigned dstpad_idx = outlink->dstpad - outlink->dst->input_pads;
+                            converter_count--;
+                            /* re-hookup the link */
+                            inlink->dst                      = outlink->dst;
+                            inlink->dstpad                   = &outlink->dst->input_pads[dstpad_idx];
+                            outlink->dst->inputs[dstpad_idx] = inlink;
+                            if (outlink->outcfg.formats)
+                                ff_formats_changeref(&outlink->outcfg.formats,
+                                                 &inlink->outcfg.formats);
+                            if (outlink->outcfg.samplerates)
+                                ff_formats_changeref(&outlink->outcfg.samplerates,
+                                                    &inlink->outcfg.samplerates);
+                            if (outlink->outcfg.channel_layouts)
+                                ff_channel_layouts_changeref(&outlink->outcfg.channel_layouts,
+                                                            &inlink->outcfg.channel_layouts);
+                            /* remove the previous auto filter */
+                            convert->inputs[0]       = NULL;
+                            convert->outputs[0]->dst = NULL;
+                            avfilter_free(convert);
+                    } else {
+                            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);
                     }
                 }
             }
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index e8c2888c0c..fee10fa0ee 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -326,18 +326,36 @@  static const AVFilterFormatsMerger mergers_audio[] = {
     },
 };
 
+static const AVFilterFormatsFilter filters_video[] = {
+    {
+        .conversion_filter = "scale",
+        .conversion_opts_offset = offsetof(AVFilterGraph, scale_sws_opts),
+    },
+    {
+        .conversion_filter = "hwmap",
+        .conversion_opts_offset = 0,
+    }
+};
+
+static const AVFilterFormatsFilter filters_audio[] = {
+    {
+        .conversion_filter = "aresample",
+        .conversion_opts_offset = offsetof(AVFilterGraph, aresample_swr_opts),
+    }
+};
+
 static const AVFilterNegotiation negotiate_video = {
     .nb_mergers = FF_ARRAY_ELEMS(mergers_video),
     .mergers = mergers_video,
-    .conversion_filter = "scale",
-    .conversion_opts_offset = offsetof(AVFilterGraph, scale_sws_opts),
+    .nb_conversion_filters = FF_ARRAY_ELEMS(filters_video),
+    .conversion_filters = filters_video,
 };
 
 static const AVFilterNegotiation negotiate_audio = {
     .nb_mergers = FF_ARRAY_ELEMS(mergers_audio),
     .mergers = mergers_audio,
-    .conversion_filter = "aresample",
-    .conversion_opts_offset = offsetof(AVFilterGraph, aresample_swr_opts),
+    .nb_conversion_filters = FF_ARRAY_ELEMS(filters_audio),
+    .conversion_filters = filters_audio,
 };
 
 const AVFilterNegotiation *ff_filter_get_negotiation(AVFilterLink *link)
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index 22224dce2d..9943fb8aa3 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -330,6 +330,11 @@  typedef struct AVFilterFormatMerger {
     int (*can_merge)(const void *a, const void *b);
 } AVFilterFormatsMerger;
 
+typedef struct AVFilterFormatFilter {
+    const char *conversion_filter;
+    unsigned conversion_opts_offset;
+} AVFilterFormatsFilter;
+
 /**
  * Callbacks and properties to describe the steps of a format negotiation.
  *
@@ -418,8 +423,8 @@  typedef struct AVFilterFormatMerger {
 typedef struct AVFilterNegotiation {
     unsigned nb_mergers;
     const AVFilterFormatsMerger *mergers;
-    const char *conversion_filter;
-    unsigned conversion_opts_offset;
+    unsigned nb_conversion_filters;
+    const AVFilterFormatsFilter *conversion_filters;
 } AVFilterNegotiation;
 
 const AVFilterNegotiation *ff_filter_get_negotiation(AVFilterLink *link);