diff mbox series

[FFmpeg-devel,v2,1/8] lavfi: add query_func2()

Message ID 20240831074659.8909-1-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,v2,1/8] lavfi: add query_func2() | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Anton Khirnov Aug. 31, 2024, 7:45 a.m. UTC
It differs from query_func() in accepting arrays of input/output format
configurations to be filled as callback parameters. This allows to mark
the filter context as const, ensuring it is not modified by this
function, as it is not supposed to have any side effects beyond
returning the supported formats.
---
Now handling FF_FILTER_FORMATS_QUERY_FUNC2 in the switch in
ff_default_query_formats(), avoiding an assertion failure

 libavfilter/avfilter.h       |  85 +++++++++++---------
 libavfilter/avfiltergraph.c  |  43 ++++++++++
 libavfilter/filters.h        |   4 +
 libavfilter/formats.c        | 148 +++++++++++++++++++++++++++++++++++
 libavfilter/formats.h        |  84 ++++++++++++++++++++
 libavfilter/tests/filtfmts.c |  19 ++++-
 6 files changed, 347 insertions(+), 36 deletions(-)
diff mbox series

Patch

diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 549fe6ce3a..1401577c50 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -98,6 +98,41 @@  const char *avfilter_pad_get_name(const AVFilterPad *pads, int pad_idx);
  */
 enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx);
 
+/**
+ * Lists of formats / etc. supported by an end of a link.
+ *
+ * This structure is directly part of AVFilterLink, in two copies:
+ * one for the source filter, one for the destination filter.
+
+ * These lists are used for negotiating the format to actually be used,
+ * which will be loaded into the format and channel_layout members of
+ * AVFilterLink, when chosen.
+ */
+typedef struct AVFilterFormatsConfig {
+
+    /**
+     * List of supported formats (pixel or sample).
+     */
+    AVFilterFormats *formats;
+
+    /**
+     * Lists of supported sample rates, only for audio.
+     */
+    AVFilterFormats  *samplerates;
+
+    /**
+     * Lists of supported channel layouts, only for audio.
+     */
+    AVFilterChannelLayouts  *channel_layouts;
+
+    /**
+     * Lists of supported YUV color metadata, only for YUV video.
+     */
+    AVFilterFormats *color_spaces;  ///< AVColorSpace
+    AVFilterFormats *color_ranges;  ///< AVColorRange
+
+} AVFilterFormatsConfig;
+
 /**
  * The number of the filter inputs is not determined just by AVFilter.inputs.
  * The filter might add additional inputs during initialization depending on the
@@ -324,6 +359,21 @@  typedef struct AVFilter {
          * AVERROR code otherwise
          */
         int (*query_func)(AVFilterContext *);
+
+        /**
+         * Same as query_func(), except this function writes the results into
+         * provided arrays.
+         *
+         * @param cfg_in  array of input format configurations with as many
+         *                members as the filters has inputs (NULL when there are
+         *                no inputs);
+         * @param cfg_out array of output format configurations with as many
+         *                members as the filters has outputs (NULL when there
+         *                are no outputs);
+         */
+        int (*query_func2)(const AVFilterContext *,
+                           struct AVFilterFormatsConfig **cfg_in,
+                           struct AVFilterFormatsConfig **cfg_out);
         /**
          * A pointer to an array of admissible pixel formats delimited
          * by AV_PIX_FMT_NONE. The generic code will use this list
@@ -492,41 +542,6 @@  struct AVFilterContext {
     int extra_hw_frames;
 };
 
-/**
- * Lists of formats / etc. supported by an end of a link.
- *
- * This structure is directly part of AVFilterLink, in two copies:
- * one for the source filter, one for the destination filter.
-
- * These lists are used for negotiating the format to actually be used,
- * which will be loaded into the format and channel_layout members of
- * AVFilterLink, when chosen.
- */
-typedef struct AVFilterFormatsConfig {
-
-    /**
-     * List of supported formats (pixel or sample).
-     */
-    AVFilterFormats *formats;
-
-    /**
-     * Lists of supported sample rates, only for audio.
-     */
-    AVFilterFormats  *samplerates;
-
-    /**
-     * Lists of supported channel layouts, only for audio.
-     */
-    AVFilterChannelLayouts  *channel_layouts;
-
-    /**
-     * Lists of supported YUV color metadata, only for YUV video.
-     */
-    AVFilterFormats *color_spaces;  ///< AVColorSpace
-    AVFilterFormats *color_ranges;  ///< AVColorRange
-
-} AVFilterFormatsConfig;
-
 /**
  * A link between two filters. This contains pointers to the source and
  * destination filters between which this link exists, and the indexes of
diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c
index 68e392b826..215e2e7d6e 100644
--- a/libavfilter/avfiltergraph.c
+++ b/libavfilter/avfiltergraph.c
@@ -352,6 +352,49 @@  static int filter_query_formats(AVFilterContext *ctx)
                        ctx->name, av_err2str(ret));
             return ret;
         }
+    } else if (ctx->filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC2) {
+        AVFilterFormatsConfig *cfg_in_stack[64], *cfg_out_stack[64];
+        AVFilterFormatsConfig **cfg_in_dyn = NULL, **cfg_out_dyn = NULL;
+        AVFilterFormatsConfig **cfg_in, **cfg_out;
+
+        if (ctx->nb_inputs > FF_ARRAY_ELEMS(cfg_in_stack)) {
+            cfg_in_dyn = av_malloc_array(ctx->nb_inputs, sizeof(*cfg_in_dyn));
+            if (!cfg_in_dyn)
+                return AVERROR(ENOMEM);
+            cfg_in = cfg_in_dyn;
+        } else
+            cfg_in = ctx->nb_inputs ? cfg_in_stack : NULL;
+
+        for (unsigned i = 0; i < ctx->nb_inputs; i++) {
+            AVFilterLink *l = ctx->inputs[i];
+            cfg_in[i] = &l->outcfg;
+        }
+
+        if (ctx->nb_outputs > FF_ARRAY_ELEMS(cfg_out_stack)) {
+            cfg_out_dyn = av_malloc_array(ctx->nb_outputs, sizeof(*cfg_out_dyn));
+            if (!cfg_out_dyn)
+                return AVERROR(ENOMEM);
+            cfg_out = cfg_out_dyn;
+        } else
+            cfg_out = ctx->nb_outputs ? cfg_out_stack : NULL;
+
+        for (unsigned i = 0; i < ctx->nb_outputs; i++) {
+            AVFilterLink *l = ctx->outputs[i];
+            cfg_out[i] = &l->incfg;
+        }
+
+        ret = ctx->filter->formats.query_func2(ctx, cfg_in, cfg_out);
+        av_freep(&cfg_in_dyn);
+        av_freep(&cfg_out_dyn);
+        if (ret < 0) {
+            if (ret != AVERROR(EAGAIN))
+                av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': %s\n",
+                       ctx->name, av_err2str(ret));
+            return ret;
+        }
+    }
+
+    if (ctx->filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC) {
 
         ret = filter_check_formats(ctx);
         if (ret < 0)
diff --git a/libavfilter/filters.h b/libavfilter/filters.h
index 0053ad4303..fdc7a95cee 100644
--- a/libavfilter/filters.h
+++ b/libavfilter/filters.h
@@ -226,6 +226,7 @@  enum FilterFormatsState {
      */
     FF_FILTER_FORMATS_PASSTHROUGH = 0,
     FF_FILTER_FORMATS_QUERY_FUNC,       ///< formats.query active.
+    FF_FILTER_FORMATS_QUERY_FUNC2,      ///< formats.query_func2 active.
     FF_FILTER_FORMATS_PIXFMT_LIST,      ///< formats.pixels_list active.
     FF_FILTER_FORMATS_SAMPLEFMTS_LIST,  ///< formats.samples_list active.
     FF_FILTER_FORMATS_SINGLE_PIXFMT,    ///< formats.pix_fmt active
@@ -235,6 +236,9 @@  enum FilterFormatsState {
 #define FILTER_QUERY_FUNC(func)        \
         .formats.query_func   = func,  \
         .formats_state        = FF_FILTER_FORMATS_QUERY_FUNC
+#define FILTER_QUERY_FUNC2(func)       \
+        .formats.query_func2  = func,  \
+        .formats_state        = FF_FILTER_FORMATS_QUERY_FUNC2
 #define FILTER_PIXFMTS_ARRAY(array)    \
         .formats.pixels_list  = array, \
         .formats_state        = FF_FILTER_FORMATS_PIXFMT_LIST
diff --git a/libavfilter/formats.c b/libavfilter/formats.c
index eabc3ec946..e22d400ab9 100644
--- a/libavfilter/formats.c
+++ b/libavfilter/formats.c
@@ -876,6 +876,153 @@  int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts)
     return ff_set_common_formats(ctx, ff_make_format_list(fmts));
 }
 
+#define SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, fmts, media_type, \
+                            ref_fn, unref_fn)                       \
+    if (!fmts)                                                      \
+        return AVERROR(ENOMEM);                                     \
+                                                                    \
+    for (unsigned i = 0; i < ctx->nb_inputs; i++) {                 \
+        AVFilterLink *const link = ctx->inputs[i];                  \
+        if (!cfg_in[i]->fmts &&                                     \
+            (media_type == AVMEDIA_TYPE_UNKNOWN ||                  \
+             link->type == media_type)) {                           \
+            int ret = ref_fn(fmts, &cfg_in[i]->fmts);               \
+            if (ret < 0) {                                          \
+                return ret;                                         \
+            }                                                       \
+        }                                                           \
+    }                                                               \
+    for (unsigned i = 0; i < ctx->nb_outputs; i++) {                \
+        AVFilterLink *const link = ctx->outputs[i];                 \
+        if (!cfg_out[i]->fmts &&                                    \
+            (media_type == AVMEDIA_TYPE_UNKNOWN ||                  \
+             link->type == media_type)) {                           \
+            int ret = ref_fn(fmts, &cfg_out[i]->fmts);              \
+            if (ret < 0) {                                          \
+                return ret;                                         \
+            }                                                       \
+        }                                                           \
+    }                                                               \
+                                                                    \
+    if (!fmts->refcount)                                            \
+        unref_fn(&fmts);                                            \
+                                                                    \
+    return 0;
+
+int ff_set_common_channel_layouts2(const AVFilterContext *ctx,
+                                   AVFilterFormatsConfig **cfg_in,
+                                   AVFilterFormatsConfig **cfg_out,
+                                   AVFilterChannelLayouts *channel_layouts)
+{
+    SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, channel_layouts, AVMEDIA_TYPE_AUDIO,
+                        ff_channel_layouts_ref, ff_channel_layouts_unref);
+}
+
+int ff_set_common_channel_layouts_from_list2(const AVFilterContext *ctx,
+                                             AVFilterFormatsConfig **cfg_in,
+                                             AVFilterFormatsConfig **cfg_out,
+                                             const AVChannelLayout *fmts)
+{
+    return ff_set_common_channel_layouts2(ctx, cfg_in, cfg_out, ff_make_channel_layout_list(fmts));
+}
+
+int ff_set_common_all_channel_counts2(const AVFilterContext *ctx,
+                                      AVFilterFormatsConfig **cfg_in,
+                                      AVFilterFormatsConfig **cfg_out)
+{
+    return ff_set_common_channel_layouts2(ctx, cfg_in, cfg_out, ff_all_channel_counts());
+}
+
+int ff_set_common_samplerates2(const AVFilterContext *ctx,
+                               AVFilterFormatsConfig **cfg_in,
+                               AVFilterFormatsConfig **cfg_out,
+                               AVFilterFormats *samplerates)
+{
+    SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, samplerates, AVMEDIA_TYPE_AUDIO,
+                        ff_formats_ref, ff_formats_unref);
+}
+
+int ff_set_common_samplerates_from_list2(const AVFilterContext *ctx,
+                                         AVFilterFormatsConfig **cfg_in,
+                                         AVFilterFormatsConfig **cfg_out,
+                                         const int *samplerates)
+{
+    return ff_set_common_samplerates2(ctx, cfg_in, cfg_out, ff_make_format_list(samplerates));
+}
+
+int ff_set_common_all_samplerates2(const AVFilterContext *ctx,
+                                   AVFilterFormatsConfig **cfg_in,
+                                   AVFilterFormatsConfig **cfg_out)
+{
+    return ff_set_common_samplerates2(ctx, cfg_in, cfg_out, ff_all_samplerates());
+}
+
+int ff_set_common_color_spaces2(const AVFilterContext *ctx,
+                                AVFilterFormatsConfig **cfg_in,
+                                AVFilterFormatsConfig **cfg_out,
+                                AVFilterFormats *color_spaces)
+{
+    SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, color_spaces, AVMEDIA_TYPE_VIDEO,
+                        ff_formats_ref, ff_formats_unref);
+}
+
+int ff_set_common_color_spaces_from_list2(const AVFilterContext *ctx,
+                                          AVFilterFormatsConfig **cfg_in,
+                                          AVFilterFormatsConfig **cfg_out,
+                                          const int *color_ranges)
+{
+    return ff_set_common_color_spaces2(ctx, cfg_in, cfg_out, ff_make_format_list(color_ranges));
+}
+
+int ff_set_common_all_color_spaces2(const AVFilterContext *ctx,
+                                    AVFilterFormatsConfig **cfg_in,
+                                    AVFilterFormatsConfig **cfg_out)
+{
+    return ff_set_common_color_spaces2(ctx, cfg_in, cfg_out, ff_all_color_spaces());
+}
+
+int ff_set_common_color_ranges2(const AVFilterContext *ctx,
+                                AVFilterFormatsConfig **cfg_in,
+                                AVFilterFormatsConfig **cfg_out,
+                                AVFilterFormats *color_ranges)
+{
+    SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, color_ranges, AVMEDIA_TYPE_VIDEO,
+                        ff_formats_ref, ff_formats_unref);
+}
+
+int ff_set_common_color_ranges_from_list2(const AVFilterContext *ctx,
+                                          AVFilterFormatsConfig **cfg_in,
+                                          AVFilterFormatsConfig **cfg_out,
+                                          const int *color_ranges)
+{
+    return ff_set_common_color_ranges2(ctx, cfg_in, cfg_out, ff_make_format_list(color_ranges));
+}
+
+int ff_set_common_all_color_ranges2(const AVFilterContext *ctx,
+                                    AVFilterFormatsConfig **cfg_in,
+                                    AVFilterFormatsConfig **cfg_out)
+{
+    return ff_set_common_color_ranges2(ctx, cfg_in, cfg_out, ff_all_color_ranges());
+}
+
+int ff_set_common_formats2(const AVFilterContext *ctx,
+                           AVFilterFormatsConfig **cfg_in,
+                           AVFilterFormatsConfig **cfg_out,
+                           AVFilterFormats *formats)
+{
+    SET_COMMON_FORMATS2(ctx, cfg_in, cfg_out, formats, AVMEDIA_TYPE_UNKNOWN,
+                        ff_formats_ref, ff_formats_unref);
+}
+
+int ff_set_common_formats_from_list2(const AVFilterContext *ctx,
+                                     AVFilterFormatsConfig **cfg_in,
+                                     AVFilterFormatsConfig **cfg_out,
+                                     const int *fmts)
+{
+    return ff_set_common_formats2(ctx, cfg_in, cfg_out, ff_make_format_list(fmts));
+}
+
+
 int ff_default_query_formats(AVFilterContext *ctx)
 {
     const AVFilter *const f = ctx->filter;
@@ -905,6 +1052,7 @@  int ff_default_query_formats(AVFilterContext *ctx)
     /* Intended fallthrough */
     case FF_FILTER_FORMATS_PASSTHROUGH:
     case FF_FILTER_FORMATS_QUERY_FUNC:
+    case FF_FILTER_FORMATS_QUERY_FUNC2:
         type = AVMEDIA_TYPE_UNKNOWN;
         formats = ff_all_formats(ctx->nb_inputs  ? ctx->inputs [0]->type :
                                  ctx->nb_outputs ? ctx->outputs[0]->type :
diff --git a/libavfilter/formats.h b/libavfilter/formats.h
index 82b3af4be1..380e9dfd00 100644
--- a/libavfilter/formats.h
+++ b/libavfilter/formats.h
@@ -225,6 +225,90 @@  int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats);
 av_warn_unused_result
 int ff_set_common_formats_from_list(AVFilterContext *ctx, const int *fmts);
 
+/**
+ * Helpers for query_formats2() which set all free audio links to the same list
+ * of channel layouts/sample rates. If there are no links hooked to this list,
+ * the list is freed.
+ */
+av_warn_unused_result
+int ff_set_common_channel_layouts2(const AVFilterContext *ctx,
+                                   AVFilterFormatsConfig **cfg_in,
+                                   AVFilterFormatsConfig **cfg_out,
+                                   AVFilterChannelLayouts *channel_layouts);
+
+av_warn_unused_result
+int ff_set_common_channel_layouts_from_list2(const AVFilterContext *ctx,
+                                             AVFilterFormatsConfig **cfg_in,
+                                             AVFilterFormatsConfig **cfg_out,
+                                             const AVChannelLayout *fmts);
+av_warn_unused_result
+int ff_set_common_all_channel_counts2(const AVFilterContext *ctx,
+                                      AVFilterFormatsConfig **cfg_in,
+                                      AVFilterFormatsConfig **cfg_out);
+
+av_warn_unused_result
+int ff_set_common_samplerates2(const AVFilterContext *ctx,
+                               AVFilterFormatsConfig **cfg_in,
+                               AVFilterFormatsConfig **cfg_out,
+                               AVFilterFormats *samplerates);
+
+av_warn_unused_result
+int ff_set_common_samplerates_from_list2(const AVFilterContext *ctx,
+                                         AVFilterFormatsConfig **cfg_in,
+                                         AVFilterFormatsConfig **cfg_out,
+                                         const int *samplerates);
+
+av_warn_unused_result
+int ff_set_common_all_samplerates2(const AVFilterContext *ctx,
+                                   AVFilterFormatsConfig **cfg_in,
+                                   AVFilterFormatsConfig **cfg_out);
+
+av_warn_unused_result
+int ff_set_common_color_spaces2(const AVFilterContext *ctx,
+                                AVFilterFormatsConfig **cfg_in,
+                                AVFilterFormatsConfig **cfg_out,
+                                AVFilterFormats *color_spaces);
+
+av_warn_unused_result
+int ff_set_common_color_spaces_from_list2(const AVFilterContext *ctx,
+                                          AVFilterFormatsConfig **cfg_in,
+                                          AVFilterFormatsConfig **cfg_out,
+                                          const int *color_ranges);
+
+av_warn_unused_result
+int ff_set_common_all_color_spaces2(const AVFilterContext *ctx,
+                                    AVFilterFormatsConfig **cfg_in,
+                                    AVFilterFormatsConfig **cfg_out);
+
+av_warn_unused_result
+int ff_set_common_color_ranges2(const AVFilterContext *ctx,
+                                AVFilterFormatsConfig **cfg_in,
+                                AVFilterFormatsConfig **cfg_out,
+                                AVFilterFormats *color_ranges);
+
+av_warn_unused_result
+int ff_set_common_color_ranges_from_list2(const AVFilterContext *ctx,
+                                          AVFilterFormatsConfig **cfg_in,
+                                          AVFilterFormatsConfig **cfg_out,
+                                          const int *color_ranges);
+
+av_warn_unused_result
+int ff_set_common_all_color_ranges2(const AVFilterContext *ctx,
+                                    AVFilterFormatsConfig **cfg_in,
+                                    AVFilterFormatsConfig **cfg_out);
+
+av_warn_unused_result
+int ff_set_common_formats2(const AVFilterContext *ctx,
+                           AVFilterFormatsConfig **cfg_in,
+                           AVFilterFormatsConfig **cfg_out,
+                           AVFilterFormats *formats);
+
+av_warn_unused_result
+int ff_set_common_formats_from_list2(const AVFilterContext *ctx,
+                                     AVFilterFormatsConfig **cfg_in,
+                                     AVFilterFormatsConfig **cfg_out,
+                                     const int *fmts);
+
 av_warn_unused_result
 int ff_add_channel_layout(AVFilterChannelLayouts **l,
                           const AVChannelLayout *channel_layout);
diff --git a/libavfilter/tests/filtfmts.c b/libavfilter/tests/filtfmts.c
index bc8c65c0ad..4ef6968826 100644
--- a/libavfilter/tests/filtfmts.c
+++ b/libavfilter/tests/filtfmts.c
@@ -143,7 +143,24 @@  int main(int argc, char **argv)
 
     if (filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC)
         ret = filter->formats.query_func(filter_ctx);
-    else
+    else if (filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC2) {
+        AVFilterFormatsConfig **cfg_in = NULL, **cfg_out = NULL;
+
+        if (filter_ctx->nb_inputs) {
+            cfg_in = av_malloc_array(filter_ctx->nb_inputs, sizeof(*cfg_in));
+            for (unsigned i = 0; i < filter_ctx->nb_inputs; i++)
+                cfg_in[i] = &filter_ctx->inputs[i]->outcfg;
+        }
+        if (filter_ctx->nb_outputs) {
+            cfg_out = av_malloc_array(filter_ctx->nb_outputs, sizeof(*cfg_out));
+            for (unsigned i = 0; i < filter_ctx->nb_outputs; i++)
+                cfg_out[i] = &filter_ctx->outputs[i]->incfg;
+        }
+
+        ret = filter->formats.query_func2(filter_ctx, cfg_in, cfg_out);
+        av_freep(&cfg_in);
+        av_freep(&cfg_out);
+    } else
         ret = ff_default_query_formats(filter_ctx);
 
     print_formats(filter_ctx);