From: James Almer via ffmpeg-devel <ffmpeg-devel@ffmpeg.org> To: ffmpeg-devel@ffmpeg.org Cc: James Almer <code@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH] avutil/samplefmt: add a sample format descriptor API (PR #20461) Date: Mon, 08 Sep 2025 02:57:12 -0000 Message-ID: <175730023276.25.7939212881029223868@463a07221176> (raw) PR #20461 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20461 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20461.patch Including some basic helpers. This will be useful among other things to ensure API users don't use AV_SAMPLE_FMT_NB. >From 064806c6a83c3ef2d95174901767026252b922ca Mon Sep 17 00:00:00 2001 From: James Almer <jamrial@gmail.com> Date: Sat, 6 Sep 2025 21:14:06 -0300 Subject: [PATCH 1/4] avutil/samplefmt: add a sample format descriptor API Including some basic helpers. This will be useful among other things to ensure API users don't use AV_SAMPLE_FMT_NB. Signed-off-by: James Almer <jamrial@gmail.com> --- libavutil/samplefmt.c | 160 +++++++++++++++++++++++++++++++++--------- libavutil/samplefmt.h | 53 ++++++++++++++ 2 files changed, 178 insertions(+), 35 deletions(-) diff --git a/libavutil/samplefmt.c b/libavutil/samplefmt.c index e1be5f0547..918ac301a1 100644 --- a/libavutil/samplefmt.c +++ b/libavutil/samplefmt.c @@ -25,29 +25,98 @@ #include <stdio.h> #include <string.h> -typedef struct SampleFmtInfo { - char name[8]; - int bits; - int planar; - enum AVSampleFormat altform; ///< planar<->packed alternative form -} SampleFmtInfo; - /** this table gives more information about formats */ -static const SampleFmtInfo sample_fmt_info[AV_SAMPLE_FMT_NB] = { - [AV_SAMPLE_FMT_U8] = { .name = "u8", .bits = 8, .planar = 0, .altform = AV_SAMPLE_FMT_U8P }, - [AV_SAMPLE_FMT_S16] = { .name = "s16", .bits = 16, .planar = 0, .altform = AV_SAMPLE_FMT_S16P }, - [AV_SAMPLE_FMT_S32] = { .name = "s32", .bits = 32, .planar = 0, .altform = AV_SAMPLE_FMT_S32P }, - [AV_SAMPLE_FMT_S64] = { .name = "s64", .bits = 64, .planar = 0, .altform = AV_SAMPLE_FMT_S64P }, - [AV_SAMPLE_FMT_FLT] = { .name = "flt", .bits = 32, .planar = 0, .altform = AV_SAMPLE_FMT_FLTP }, - [AV_SAMPLE_FMT_DBL] = { .name = "dbl", .bits = 64, .planar = 0, .altform = AV_SAMPLE_FMT_DBLP }, - [AV_SAMPLE_FMT_U8P] = { .name = "u8p", .bits = 8, .planar = 1, .altform = AV_SAMPLE_FMT_U8 }, - [AV_SAMPLE_FMT_S16P] = { .name = "s16p", .bits = 16, .planar = 1, .altform = AV_SAMPLE_FMT_S16 }, - [AV_SAMPLE_FMT_S32P] = { .name = "s32p", .bits = 32, .planar = 1, .altform = AV_SAMPLE_FMT_S32 }, - [AV_SAMPLE_FMT_S64P] = { .name = "s64p", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_S64 }, - [AV_SAMPLE_FMT_FLTP] = { .name = "fltp", .bits = 32, .planar = 1, .altform = AV_SAMPLE_FMT_FLT }, - [AV_SAMPLE_FMT_DBLP] = { .name = "dblp", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_DBL }, +static const AVSampleFmtDescriptor sample_fmt_info[AV_SAMPLE_FMT_NB] = { + [AV_SAMPLE_FMT_U8] = { + .name = "u8", + .bits = 8, + .flags = AV_SAMPLE_FMT_FLAG_UNSIGNED, + }, + [AV_SAMPLE_FMT_S16] = { + .name = "s16", + .bits = 16, + .flags = 0, + }, + [AV_SAMPLE_FMT_S32] = { + .name = "s32", + .bits = 32, + .flags = 0, + }, + [AV_SAMPLE_FMT_S64] = { + .name = "s64", + .bits = 64, + .flags = 0, + }, + [AV_SAMPLE_FMT_FLT] = { + .name = "flt", + .bits = 32, + .flags = AV_SAMPLE_FMT_FLAG_FLOAT, + }, + [AV_SAMPLE_FMT_DBL] = { + .name = "dbl", + .bits = 64, + .flags = AV_SAMPLE_FMT_FLAG_FLOAT, + }, + [AV_SAMPLE_FMT_U8P] = { + .name = "u8p", + .bits = 8, + .flags = AV_SAMPLE_FMT_FLAG_PLANAR | AV_SAMPLE_FMT_FLAG_UNSIGNED, + }, + [AV_SAMPLE_FMT_S16P] = { + .name = "s16p", + .bits = 16, + .flags = AV_SAMPLE_FMT_FLAG_PLANAR, + }, + [AV_SAMPLE_FMT_S32P] = { + .name = "s32p", + .bits = 32, + .flags = AV_SAMPLE_FMT_FLAG_PLANAR, + }, + [AV_SAMPLE_FMT_S64P] = { + .name = "s64p", + .bits = 64, + .flags = AV_SAMPLE_FMT_FLAG_PLANAR, + }, + [AV_SAMPLE_FMT_FLTP] = { + .name = "fltp", + .bits = 32, + .flags = AV_SAMPLE_FMT_FLAG_PLANAR | AV_SAMPLE_FMT_FLAG_FLOAT, + }, + [AV_SAMPLE_FMT_DBLP] = { + .name = "dblp", + .bits = 64, + .flags = AV_SAMPLE_FMT_FLAG_PLANAR | AV_SAMPLE_FMT_FLAG_FLOAT, + }, }; +const AVSampleFmtDescriptor *av_sample_fmt_desc_get(enum AVSampleFormat sample_fmt) +{ + if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB) + return NULL; + return &sample_fmt_info[sample_fmt]; +} + +const AVSampleFmtDescriptor *av_sample_fmt_desc_next(const AVSampleFmtDescriptor *prev) +{ + if (!prev) + return &sample_fmt_info[0]; + while (prev - sample_fmt_info < FF_ARRAY_ELEMS(sample_fmt_info) - 1) { + prev++; + if (prev->name) + return prev; + } + return NULL; +} + +enum AVSampleFormat av_sample_fmt_desc_get_id(const AVSampleFmtDescriptor *desc) +{ + if (desc < sample_fmt_info || + desc >= sample_fmt_info + FF_ARRAY_ELEMS(sample_fmt_info)) + return AV_SAMPLE_FMT_NONE; + + return desc - sample_fmt_info; +} + const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt) { if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB) @@ -65,31 +134,52 @@ enum AVSampleFormat av_get_sample_fmt(const char *name) return AV_SAMPLE_FMT_NONE; } +static enum AVSampleFormat get_alt_sample_fmt(const AVSampleFmtDescriptor *desc, uint64_t flags) +{ + const AVSampleFmtDescriptor *iter = NULL; + + while ((iter = av_sample_fmt_desc_next(iter))) { + if (iter->bits == desc->bits && iter->flags == flags) + return av_sample_fmt_desc_get_id(iter); + } + + return AV_SAMPLE_FMT_NONE; +} + enum AVSampleFormat av_get_alt_sample_fmt(enum AVSampleFormat sample_fmt, int planar) { - if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB) + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); + uint64_t flags; + + if (!desc) return AV_SAMPLE_FMT_NONE; - if (sample_fmt_info[sample_fmt].planar == planar) - return sample_fmt; - return sample_fmt_info[sample_fmt].altform; + flags = desc->flags ^ (!!planar * AV_SAMPLE_FMT_FLAG_PLANAR); + + return get_alt_sample_fmt(desc, flags); } enum AVSampleFormat av_get_packed_sample_fmt(enum AVSampleFormat sample_fmt) { - if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB) + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); + uint64_t flags; + + if (!desc) return AV_SAMPLE_FMT_NONE; - if (sample_fmt_info[sample_fmt].planar) - return sample_fmt_info[sample_fmt].altform; - return sample_fmt; + flags = desc->flags & ~AV_SAMPLE_FMT_FLAG_PLANAR; + + return get_alt_sample_fmt(desc, flags); } enum AVSampleFormat av_get_planar_sample_fmt(enum AVSampleFormat sample_fmt) { - if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB) + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); + uint64_t flags; + + if (!desc) return AV_SAMPLE_FMT_NONE; - if (sample_fmt_info[sample_fmt].planar) - return sample_fmt; - return sample_fmt_info[sample_fmt].altform; + flags = desc->flags | AV_SAMPLE_FMT_FLAG_PLANAR; + + return get_alt_sample_fmt(desc, flags); } char *av_get_sample_fmt_string (char *buf, int buf_size, enum AVSampleFormat sample_fmt) @@ -98,8 +188,8 @@ char *av_get_sample_fmt_string (char *buf, int buf_size, enum AVSampleFormat sam if (sample_fmt < 0) snprintf(buf, buf_size, "name " " depth"); else if (sample_fmt < AV_SAMPLE_FMT_NB) { - SampleFmtInfo info = sample_fmt_info[sample_fmt]; - snprintf (buf, buf_size, "%-6s" " %2d ", info.name, info.bits); + const AVSampleFmtDescriptor *info = &sample_fmt_info[sample_fmt]; + snprintf (buf, buf_size, "%-6s" " %2d ", info->name, info->bits); } return buf; @@ -115,7 +205,7 @@ int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt) { if (sample_fmt < 0 || sample_fmt >= AV_SAMPLE_FMT_NB) return 0; - return sample_fmt_info[sample_fmt].planar; + return !!(sample_fmt_info[sample_fmt].flags & AV_SAMPLE_FMT_FLAG_PLANAR); } int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples, diff --git a/libavutil/samplefmt.h b/libavutil/samplefmt.h index 43a57a422c..13d5694817 100644 --- a/libavutil/samplefmt.h +++ b/libavutil/samplefmt.h @@ -71,6 +71,59 @@ enum AVSampleFormat { AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically }; +/** + * Descriptor containing essential information about a sample format. + */ +typedef struct AVSampleFmtDescriptor { + const char *name; + + /** + * Bits per sample. + */ + int bits; + + /** + * Combination of AV_SAMPLE_FMT_FLAG_... flags. + */ + uint64_t flags; +} AVSampleFmtDescriptor; + +/** + * Format is planar (each audio channel is in a separate data plane). + */ +#define AV_SAMPLE_FMT_FLAG_PLANAR (1 << 0) + +/** + * The sample format contains unsinged values. + */ +#define AV_SAMPLE_FMT_FLAG_UNSIGNED (1 << 1) + +/** + * The sample format contains IEEE-754 floating point values. + */ +#define AV_SAMPLE_FMT_FLAG_FLOAT (1 << 2) + +/** + * @return a sample format descriptor for provided sample format or NULL if + * this sample format is unknown. + */ +const AVSampleFmtDescriptor *av_sample_fmt_desc_get(enum AVSampleFormat sample_fmt); + +/** + * Iterate over all sample format descriptors known to libavutil. + * + * @param prev previous descriptor. NULL to get the first descriptor. + * + * @return next descriptor or NULL after the last descriptor + */ +const AVSampleFmtDescriptor *av_sample_fmt_desc_next(const AVSampleFmtDescriptor *prev); + +/** + * @return an AVSampleFormat id described by desc, or AV_SAMPLE_FMT_NONE if desc + * is not a valid pointer to a sample format descriptor. + */ +enum AVSampleFormat av_sample_fmt_desc_get_id(const AVSampleFmtDescriptor *desc); + /** * Return the name of sample_fmt, or NULL if sample_fmt is not * recognized. -- 2.49.1 >From ce21f7e2756eb37df8d5d661fb1df0201df6ef0a Mon Sep 17 00:00:00 2001 From: James Almer <jamrial@gmail.com> Date: Sat, 6 Sep 2025 21:39:50 -0300 Subject: [PATCH 2/4] avutil/samplefmt: use descriptors were useful This information is now available in each sample format's descriptor. Signed-off-by: James Almer <jamrial@gmail.com> --- libavutil/samplefmt.c | 51 +++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/libavutil/samplefmt.c b/libavutil/samplefmt.c index 918ac301a1..66e9002485 100644 --- a/libavutil/samplefmt.c +++ b/libavutil/samplefmt.c @@ -211,14 +211,16 @@ int av_sample_fmt_is_planar(enum AVSampleFormat sample_fmt) int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align) { + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); int line_size; - int sample_size = av_get_bytes_per_sample(sample_fmt); - int planar = av_sample_fmt_is_planar(sample_fmt); /* validate parameter ranges */ - if (!sample_size || nb_samples <= 0 || nb_channels <= 0) + if (!desc || nb_samples <= 0 || nb_channels <= 0) return AVERROR(EINVAL); + int sample_size = desc->bits >> 3; + int planar = desc->flags & AV_SAMPLE_FMT_FLAG_PLANAR; + /* auto-select alignment if not specified */ if (!align) { if (nb_samples > INT_MAX - 31) @@ -244,9 +246,12 @@ int av_samples_fill_arrays(uint8_t **audio_data, int *linesize, const uint8_t *buf, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align) { + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); int ch, planar, buf_size, line_size; - planar = av_sample_fmt_is_planar(sample_fmt); + if (!desc) + return AVERROR(EINVAL); + buf_size = av_samples_get_buffer_size(&line_size, nb_channels, nb_samples, sample_fmt, align); if (buf_size < 0) @@ -255,6 +260,7 @@ int av_samples_fill_arrays(uint8_t **audio_data, int *linesize, if (linesize) *linesize = line_size; + planar = desc->flags & AV_SAMPLE_FMT_FLAG_PLANAR; memset(audio_data, 0, planar ? sizeof(*audio_data) * nb_channels : sizeof(*audio_data)); @@ -297,8 +303,13 @@ int av_samples_alloc(uint8_t **audio_data, int *linesize, int nb_channels, int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align) { - int ret, nb_planes = av_sample_fmt_is_planar(sample_fmt) ? nb_channels : 1; + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); + int ret; + if (!desc) + return AVERROR(EINVAL); + + int nb_planes = (desc->flags & AV_SAMPLE_FMT_FLAG_PLANAR) ? nb_channels : 1; *audio_data = av_calloc(nb_planes, sizeof(**audio_data)); if (!*audio_data) return AVERROR(ENOMEM); @@ -313,20 +324,24 @@ int av_samples_copy(uint8_t * const *dst, uint8_t * const *src, int dst_offset, int src_offset, int nb_samples, int nb_channels, enum AVSampleFormat sample_fmt) { - int planar = av_sample_fmt_is_planar(sample_fmt); + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); + + if (!desc) + return AVERROR(EINVAL); + + int planar = desc->flags & AV_SAMPLE_FMT_FLAG_PLANAR; int planes = planar ? nb_channels : 1; - int block_align = av_get_bytes_per_sample(sample_fmt) * (planar ? 1 : nb_channels); + int block_align = (desc->bits >> 3) * (planar ? 1 : nb_channels); int data_size = nb_samples * block_align; - int i; dst_offset *= block_align; src_offset *= block_align; if((dst[0] < src[0] ? src[0] - dst[0] : dst[0] - src[0]) >= data_size) { - for (i = 0; i < planes; i++) + for (int i = 0; i < planes; i++) memcpy(dst[i] + dst_offset, src[i] + src_offset, data_size); } else { - for (i = 0; i < planes; i++) + for (int i = 0; i < planes; i++) memmove(dst[i] + dst_offset, src[i] + src_offset, data_size); } @@ -336,17 +351,21 @@ int av_samples_copy(uint8_t * const *dst, uint8_t * const *src, int dst_offset, int av_samples_set_silence(uint8_t * const *audio_data, int offset, int nb_samples, int nb_channels, enum AVSampleFormat sample_fmt) { - int planar = av_sample_fmt_is_planar(sample_fmt); - int planes = planar ? nb_channels : 1; - int block_align = av_get_bytes_per_sample(sample_fmt) * (planar ? 1 : nb_channels); - int data_size = nb_samples * block_align; + const AVSampleFmtDescriptor *desc = av_sample_fmt_desc_get(sample_fmt); int fill_char = (sample_fmt == AV_SAMPLE_FMT_U8 || sample_fmt == AV_SAMPLE_FMT_U8P) ? 0x80 : 0x00; - int i; + + if (!desc) + return AVERROR(EINVAL); + + int planar = desc->flags & AV_SAMPLE_FMT_FLAG_PLANAR; + int planes = planar ? nb_channels : 1; + int block_align = (desc->bits >> 3) * (planar ? 1 : nb_channels); + int data_size = nb_samples * block_align; offset *= block_align; - for (i = 0; i < planes; i++) + for (int i = 0; i < planes; i++) memset(audio_data[i] + offset, fill_char, data_size); return 0; -- 2.49.1 >From 796925080bec1f8ddb311503129116c14383917d Mon Sep 17 00:00:00 2001 From: James Almer <jamrial@gmail.com> Date: Sat, 6 Sep 2025 21:44:29 -0300 Subject: [PATCH 3/4] fftools/opt_common: remove usage of AV_SAMPLE_FMT_NB Signed-off-by: James Almer <jamrial@gmail.com> --- fftools/opt_common.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fftools/opt_common.c b/fftools/opt_common.c index c2f6b9de2a..575c68b988 100644 --- a/fftools/opt_common.c +++ b/fftools/opt_common.c @@ -1044,10 +1044,13 @@ int show_layouts(void *optctx, const char *opt, const char *arg) int show_sample_fmts(void *optctx, const char *opt, const char *arg) { - int i; + const AVSampleFmtDescriptor *desc = NULL; char fmt_str[128]; - for (i = -1; i < AV_SAMPLE_FMT_NB; i++) - printf("%s\n", av_get_sample_fmt_string(fmt_str, sizeof(fmt_str), i)); + + snprintf(fmt_str, sizeof(fmt_str), "name " " depth"); + while ((desc = av_sample_fmt_desc_next(desc))) + snprintf(fmt_str, sizeof(fmt_str), "%-6s" " %2d ", desc->name, desc->bits); + return 0; } -- 2.49.1 >From 5cd8faa0a539775246d551a4cce8be75e5d33401 Mon Sep 17 00:00:00 2001 From: James Almer <jamrial@gmail.com> Date: Sun, 7 Sep 2025 19:29:46 -0300 Subject: [PATCH 4/4] swresample/swresample:: remove usage of AV_SAMPLE_FMT_NB Signed-off-by: James Almer <jamrial@gmail.com> --- libswresample/swresample.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libswresample/swresample.c b/libswresample/swresample.c index 0a4e216f9b..ff9093670b 100644 --- a/libswresample/swresample.c +++ b/libswresample/swresample.c @@ -143,11 +143,11 @@ av_cold int swr_init(struct SwrContext *s){ clear_context(s); - if((unsigned) s-> in_sample_fmt >= AV_SAMPLE_FMT_NB){ + if(!av_sample_fmt_desc_get(s->in_sample_fmt)){ av_log(s, AV_LOG_ERROR, "Requested input sample format %d is invalid\n", s->in_sample_fmt); return AVERROR(EINVAL); } - if((unsigned) s->out_sample_fmt >= AV_SAMPLE_FMT_NB){ + if(!av_sample_fmt_desc_get(s->out_sample_fmt)){ av_log(s, AV_LOG_ERROR, "Requested output sample format %d is invalid\n", s->out_sample_fmt); return AVERROR(EINVAL); } -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
reply other threads:[~2025-09-08 2:57 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=175730023276.25.7939212881029223868@463a07221176 \ --to=ffmpeg-devel@ffmpeg.org \ --cc=code@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git