* [FFmpeg-devel] [PATCH] avutil/samplefmt: add a sample format descriptor API (PR #20461)
@ 2025-09-08 2:57 James Almer via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: James Almer via ffmpeg-devel @ 2025-09-08 2:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: James Almer
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
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-09-08 2:57 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-08 2:57 [FFmpeg-devel] [PATCH] avutil/samplefmt: add a sample format descriptor API (PR #20461) James Almer via ffmpeg-devel
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