[FFmpeg-devel] [PATCH v2 02/17] avcodec: add avcodec_get_supported_config()

Andreas Rheinhardt andreas.rheinhardt at outlook.com
Thu Aug 1 04:48:17 EEST 2024


Niklas Haas:
> From: Niklas Haas <git at haasn.dev>
> 
> This replaces the myriad of existing lists in AVCodec by a unified API
> call, allowing us to (ultimately) trim down the sizeof(AVCodec) quite
> substantially, while also making this more trivially extensible.
> 
> In addition to the already covered lists, add two new entries for color
> space and color range, mirroring the newly added negotiable fields in
> libavfilter.
> 
> I decided to drop the explicit length field from the API proposed by
> Andreas Rheinhardt, because having it in place ended up complicating
> both the codec side and the client side implementations, while also
> being strictly less flexible (it's trivial to recover a length given
> a terminator, but requires allocation to add a terminator given
> a length). Using a terminator also presents less of a porting challenge
> for existing users of the current API.
> 
> Once the deprecation period passes for the existing public fields, the
> rough plan is to move the commonly used fields (such as
> pix_fmt/sample_fmt) into FFCodec, possibly as a union of audio and video
> configuration types, and then implement the rarely used fields with
> custom callbacks.
> ---
>  doc/APIchanges              |  5 +++
>  libavcodec/avcodec.c        | 75 +++++++++++++++++++++++++++++++++++++
>  libavcodec/avcodec.h        | 27 +++++++++++++
>  libavcodec/codec.h          | 19 ++++++++--
>  libavcodec/codec_internal.h | 24 ++++++++++++
>  libavcodec/version.h        |  4 +-
>  6 files changed, 148 insertions(+), 6 deletions(-)
> 
> diff --git a/doc/APIchanges b/doc/APIchanges
> index 0a39b6d7ab8..fdeae67159d 100644
> --- a/doc/APIchanges
> +++ b/doc/APIchanges
> @@ -2,6 +2,11 @@ The last version increases of all libraries were on 2024-03-07
>  
>  API changes, most recent first:
>  
> +2024-04-xx - xxxxxxxxxx - lavc 59.6.100 - avcodec.h
> +  Add avcodec_get_supported_config() and enum AVCodecConfig; deprecate
> +  AVCodec.pix_fmts, AVCodec.sample_fmts, AVCodec.supported_framerates,
> +  AVCodec.supported_samplerates and AVCodec.ch_layouts.
> +
>  2024-04-03 - xxxxxxxxxx - lavu 59.13.100 - pixfmt.h
>    Add AVCOL_SPC_IPT_C2, AVCOL_SPC_YCGCO_RE and AVCOL_SPC_YCGCO_RO
>    to map new matrix coefficients defined by H.273 v3.
> diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c
> index 525fe516bd2..96728546d6c 100644
> --- a/libavcodec/avcodec.c
> +++ b/libavcodec/avcodec.c
> @@ -700,3 +700,78 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
>          return ff_decode_receive_frame(avctx, frame);
>      return ff_encode_receive_frame(avctx, frame);
>  }
> +
> +#define WRAP_CONFIG(allowed_type, field)    \
> +    do {                                    \
> +        if (codec->type != (allowed_type))  \
> +            return AVERROR(EINVAL);         \
> +        *out_configs = (field);             \
> +        return 0;                           \
> +    } while (0)
> +
> +static const enum AVColorRange color_range_jpeg[] = {
> +    AVCOL_RANGE_JPEG, AVCOL_RANGE_UNSPECIFIED
> +};
> +
> +static const enum AVColorRange color_range_mpeg[] = {
> +    AVCOL_RANGE_MPEG, AVCOL_RANGE_UNSPECIFIED
> +};
> +
> +static const enum AVColorRange color_range_all[] = {
> +    AVCOL_RANGE_MPEG, AVCOL_RANGE_JPEG, AVCOL_RANGE_UNSPECIFIED
> +};
> +
> +static const enum AVColorRange *color_range_table[] = {
> +    [AVCOL_RANGE_MPEG] = color_range_mpeg,
> +    [AVCOL_RANGE_JPEG] = color_range_jpeg,
> +    [AVCOL_RANGE_MPEG | AVCOL_RANGE_JPEG] = color_range_all,
> +};
> +
> +int ff_default_get_supported_config(const AVCodecContext *avctx,
> +                                    const AVCodec *codec,
> +                                    enum AVCodecConfig config,
> +                                    unsigned flags,
> +                                    const void **out_configs)
> +{
> +    switch (config) {
> +FF_DISABLE_DEPRECATION_WARNINGS
> +    case AV_CODEC_CONFIG_PIX_FORMAT:
> +        WRAP_CONFIG(AVMEDIA_TYPE_VIDEO, codec->pix_fmts);
> +    case AV_CODEC_CONFIG_FRAME_RATE:
> +        WRAP_CONFIG(AVMEDIA_TYPE_VIDEO, codec->supported_framerates);
> +    case AV_CODEC_CONFIG_SAMPLE_RATE:
> +        WRAP_CONFIG(AVMEDIA_TYPE_AUDIO, codec->supported_samplerates);
> +    case AV_CODEC_CONFIG_SAMPLE_FORMAT:
> +        WRAP_CONFIG(AVMEDIA_TYPE_AUDIO, codec->sample_fmts);
> +    case AV_CODEC_CONFIG_CHANNEL_LAYOUT:
> +        WRAP_CONFIG(AVMEDIA_TYPE_AUDIO, codec->ch_layouts);
> +FF_ENABLE_DEPRECATION_WARNINGS
> +
> +    case AV_CODEC_CONFIG_COLOR_RANGE:
> +        if (codec->type != AVMEDIA_TYPE_VIDEO)
> +            return AVERROR(EINVAL);
> +        *out_configs = color_range_table[ffcodec(codec)->color_ranges];
> +        return 0;
> +
> +    case AV_CODEC_CONFIG_COLOR_SPACE:
> +        *out_configs = NULL;
> +        return 0;
> +    default:
> +        return AVERROR(EINVAL);
> +    }
> +}
> +
> +int avcodec_get_supported_config(const AVCodecContext *avctx, const AVCodec *codec,
> +                                 enum AVCodecConfig config, unsigned flags,
> +                                 const void **out)
> +{
> +    const FFCodec *codec2;
> +    if (!codec)
> +        codec = avctx->codec;
> +    codec2 = ffcodec(codec);
> +    if (codec2->get_supported_config) {
> +        return codec2->get_supported_config(avctx, codec, config, flags, out);
> +    } else {
> +        return ff_default_get_supported_config(avctx, codec, config, flags, out);
> +    }
> +}
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 83dc487251c..64f31375fc6 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -2690,6 +2690,33 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx,
>                                       enum AVPixelFormat hw_pix_fmt,
>                                       AVBufferRef **out_frames_ref);
>  
> +enum AVCodecConfig {
> +    AV_CODEC_CONFIG_PIX_FORMAT,     ///< AVPixelFormat, terminated by AV_PIX_FMT_NONE
> +    AV_CODEC_CONFIG_FRAME_RATE,     ///< AVRational, terminated by {0, 0}
> +    AV_CODEC_CONFIG_SAMPLE_RATE,    ///< int, terminated by 0
> +    AV_CODEC_CONFIG_SAMPLE_FORMAT,  ///< AVSampleFormat, terminated by AV_SAMPLE_FMT_NONE
> +    AV_CODEC_CONFIG_CHANNEL_LAYOUT, ///< AVChannelLayout, terminated by {0}
> +    AV_CODEC_CONFIG_COLOR_RANGE,    ///< AVColorRange, terminated by AVCOL_RANGE_UNSPECIFIED
> +    AV_CODEC_CONFIG_COLOR_SPACE,    ///< AVColorSpace, terminated by AVCOL_SPC_UNSPECIFIED
> +};
> +
> +/**
> + * Retrieve a list of all supported values for a given configuration type.
> + *
> + * @param avctx An optional context to use. Values such as
> + *              `strict_std_compliance` may affect the result. If NULL,
> + *              default values are used.
> + * @param codec The codec to query, or NULL to use avctx->codec.
> + * @param config The configuration to query.
> + * @param flags Currently unused; should be set to zero.
> + * @param out_configs On success, set to a list of configurations, terminated
> + *                    by a config-specific terminator, or NULL if all
> + *                    possible values are supported.
> + */
> +int avcodec_get_supported_config(const AVCodecContext *avctx,
> +                                 const AVCodec *codec, enum AVCodecConfig config,
> +                                 unsigned flags, const void **out_configs);

1. This approach constrains us in several ways, most notably it forces
us to always have static lists for every possible combination. E.g. take
a look at the wavpack encoder: It accepts certain native channel layouts
or certain unspecified channel counts within a range (the encoder
accepts up to 255, the format allows up to 4096). It makes no sense to
have a static list of 4096 unspecified channel layouts. Getting rid of
this requirement seems good, even if it causes allocations.
2. A length field is not "strictly less flexible", to the contrary: It
allows to keep our options open. The length field should be optional and
for the convenience of the user (meaning the list would still be
terminated by a sentinel). But it would allow to e.g. add a flag in the
future that says that *out_configs is already set to some array whose
length is given by the length field which would allow to avoid the
allocation that I just proposed in 1. (e.g. the number of sample formats
is very small and can be put into a stack array; the number of pixel
formats is also small and can be queried at runtime, so that an array of
maximum size can be allocated once to query multiple codecs).

> +
>  
>  
>  /**
> diff --git a/libavcodec/codec.h b/libavcodec/codec.h
> index 6f9b42760d7..f7541ffc42b 100644
> --- a/libavcodec/codec.h
> +++ b/libavcodec/codec.h
> @@ -205,10 +205,19 @@ typedef struct AVCodec {
>       */
>      int capabilities;
>      uint8_t max_lowres;                     ///< maximum value for lowres supported by the decoder
> -    const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
> -    const enum AVPixelFormat *pix_fmts;     ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1
> -    const int *supported_samplerates;       ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
> -    const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
> +
> +    /**
> +     * Deprecated codec capabilities.
> +     */
> +    attribute_deprecated
> +    const AVRational *supported_framerates; ///< @deprecated use avcodec_get_supported_config()
> +    attribute_deprecated
> +    const enum AVPixelFormat *pix_fmts;     ///< @deprecated use avcodec_get_supported_config()
> +    attribute_deprecated
> +    const int *supported_samplerates;       ///< @deprecated use avcodec_get_supported_config()
> +    attribute_deprecated
> +    const enum AVSampleFormat *sample_fmts; ///< @deprecated use avcodec_get_supported_config()
> +
>      const AVClass *priv_class;              ///< AVClass for the private context
>      const AVProfile *profiles;              ///< array of recognized profiles, or NULL if unknown, array is terminated by {AV_PROFILE_UNKNOWN}
>  
> @@ -226,7 +235,9 @@ typedef struct AVCodec {
>  
>      /**
>       * Array of supported channel layouts, terminated with a zeroed layout.
> +     * @deprecated use avcodec_get_supported_config()
>       */
> +    attribute_deprecated
>      const AVChannelLayout *ch_layouts;
>  } AVCodec;
>  
> diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h
> index 2134f184094..bac3e30ba2c 100644
> --- a/libavcodec/codec_internal.h
> +++ b/libavcodec/codec_internal.h
> @@ -22,6 +22,7 @@
>  #include <stdint.h>
>  
>  #include "libavutil/attributes.h"
> +#include "avcodec.h"
>  #include "codec.h"
>  #include "config.h"
>  
> @@ -270,8 +271,31 @@ typedef struct FFCodec {
>       * List of supported codec_tags, terminated by FF_CODEC_TAGS_END.
>       */
>      const uint32_t *codec_tags;
> +
> +    /**
> +     * Custom callback for avcodec_get_supported_config(). If absent,
> +     * ff_default_get_supported_config() will be used.
> +     */
> +    int (*get_supported_config)(const AVCodecContext *avctx,
> +                                const AVCodec *codec,
> +                                enum AVCodecConfig config,
> +                                unsigned flags,
> +                                const void **out_configs);
>  } FFCodec;
>  
> +/**
> + * Default implementation for avcodec_get_supported_config(). Will return the
> + * relevant fields from AVCodec if present, or NULL otherwise.
> + *
> + * For AVCODEC_CONFIG_COLOR_RANGE, the output will depend on the bitmask in
> + * FFCodec.color_ranges, with a value of 0 returning NULL.
> + */
> +int ff_default_get_supported_config(const AVCodecContext *avctx,
> +                                    const AVCodec *codec,
> +                                    enum AVCodecConfig config,
> +                                    unsigned flags,
> +                                    const void **out_configs);
> +
>  #if CONFIG_SMALL
>  #define CODEC_LONG_NAME(str) .p.long_name = NULL
>  #else
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index 84a1c02ce4a..da54f878874 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -29,8 +29,8 @@
>  
>  #include "version_major.h"
>  
> -#define LIBAVCODEC_VERSION_MINOR   5
> -#define LIBAVCODEC_VERSION_MICRO 101
> +#define LIBAVCODEC_VERSION_MINOR   6
> +#define LIBAVCODEC_VERSION_MICRO 100
>  
>  #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
>                                                 LIBAVCODEC_VERSION_MINOR, \



More information about the ffmpeg-devel mailing list