Commit b917a0fe1e for ffmpeg
commit b917a0fe1e36cedef3d9d8a14f00853f84c92bd0
Author: Niklas Haas <git@haasn.dev>
Date: Wed Sep 30 14:37:26 2026 +0200
avutil/gain_map: add AVGainMapParams and side data type
This commit just adds the public facing struct definitions and side data.
The struct follows ISO 21496-1 exactly, with a validation function to check
for representable and legal values.
Signed-off-by: Niklas Haas <git@haasn.dev>
diff --git a/doc/APIchanges b/doc/APIchanges
index a653e4e945..f0cfa8ac23 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,13 @@ The last version increases of all libraries were on 2026-06-23.
API changes, most recent first:
+2026-09-30 - xxxxxxxxxx - lavu 61.10.100 - frame.h gain_map.h
+ Add AV_FRAME_DATA_GAIN_MAP_PARAMS.
+ Add AV_ISO21496_VERSION, AV_ISO21496_IDENTIFIER.
+ Add AVGainMapParams.
+ Add av_gain_map_params_validate(), av_gain_map_channels_identical().
+ Add av_gain_map_params_alloc(), av_gain_map_params_create_side_data().
+
2026-09-27 - xxxxxxxxxx - lavc 63.15.100 - defs.h
Add AV_PROFILE_MPEGH_3DA_MAIN, AV_PROFILE_MPEGH_3DA_HIGH
AV_PROFILE_MPEGH_3DA_LC, AV_PROFILE_MPEGH_3DA_BL.
diff --git a/libavutil/Makefile b/libavutil/Makefile
index d12030688f..6e6246cb97 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -37,6 +37,7 @@ HEADERS = adler32.h \
file.h \
film_grain_params.h \
frame.h \
+ gain_map.h \
hash.h \
hdr_dynamic_metadata.h \
hdr_dynamic_vivid_metadata.h \
@@ -146,6 +147,7 @@ OBJS = adler32.o \
film_grain_params.o \
fixed_dsp.o \
frame.o \
+ gain_map.o \
hash.o \
hdr_dynamic_metadata.o \
hdr_dynamic_vivid_metadata.o \
diff --git a/libavutil/frame.h b/libavutil/frame.h
index e8cc765e5d..1fd296aa83 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -305,6 +305,13 @@ enum AVFrameSideDataType {
* The data is the AVDownmixMatrix struct defined in libavutil/downmix_info.h.
*/
AV_FRAME_DATA_DOWNMIX_MATRIX,
+
+ /**
+ * Parameters describing how to combine this gain map with its base image
+ * to form an alternate rendition. The payload is the AVGainMapParams
+ * struct defined in libavutil/gain_map.h.
+ */
+ AV_FRAME_DATA_GAIN_MAP_PARAMS,
};
enum AVActiveFormatDescription {
diff --git a/libavutil/gain_map.c b/libavutil/gain_map.c
new file mode 100644
index 0000000000..83c5bd31cf
--- /dev/null
+++ b/libavutil/gain_map.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "gain_map.h"
+#include "mem.h"
+
+static void gain_map_params_default(AVGainMapParams *p)
+{
+ *p = (AVGainMapParams) {
+ .base_hdr_headroom = { 0, 1 },
+ .alternate_hdr_headroom = { 0, 1 },
+ .nb_channels = 1,
+ };
+
+ /* Pre-fill all three channels as a convenience */
+ for (int c = 0; c < 3; c++) {
+ p->channels[c] = (struct AVGainMapChannel) {
+ .gain_map_min = { 0, 1 },
+ .gain_map_max = { 0, 1 },
+ .gamma = { 1, 1 },
+ .base_offset = { 1, 64 },
+ .alternate_offset = { 1, 64 },
+ };
+ }
+}
+
+#define CHECK(cond) do { if (!(cond)) return AVERROR_INVALIDDATA; } while (0)
+#define CHECK_SIGNED(q) CHECK((q).den > 0);
+#define CHECK_UNSIGNED(q) CHECK((q).num >= 0 && (q).den > 0);
+
+int av_gain_map_params_validate(const AVGainMapParams *p)
+{
+ CHECK(0 <= p->version && p->version <= AV_ISO21496_VERSION);
+ CHECK(p->nb_channels == 1 || p->nb_channels == 3);
+ CHECK_UNSIGNED(p->base_hdr_headroom);
+ CHECK_UNSIGNED(p->alternate_hdr_headroom);
+
+ for (int c = 0; c < p->nb_channels; c++) {
+ const struct AVGainMapChannel *const ch = &p->channels[c];
+ CHECK_SIGNED(ch->gain_map_min);
+ CHECK_SIGNED(ch->gain_map_max);
+ CHECK_UNSIGNED(ch->gamma);
+ CHECK(ch->gamma.num != 0);
+ /**
+ * Note: The ISO spec declares these as signed, but a negative value
+ * makes no numeric sense and would result in infinities in the
+ * middle of the valid signal range; so just constrain them to be
+ * unsigned as a safeguard. Note that the Adobe hdrgm spec explicitly
+ * requires them to be nonnegative as well, so this avoids an
+ * inconsistency.
+ */
+ CHECK_UNSIGNED(ch->base_offset);
+ CHECK_UNSIGNED(ch->alternate_offset);
+ CHECK(av_cmp_q(ch->gain_map_min, ch->gain_map_max) <= 0);
+ }
+
+ return 0;
+}
+
+int av_gain_map_channels_identical(const AVGainMapParams *p)
+{
+ static_assert(sizeof(p->channels[0]) == sizeof(int[2]) * 5,
+ "AVGainMapChannel struct is tightly packed");
+
+ return !memcmp(&p->channels[0], &p->channels[1], sizeof(p->channels[0])) &&
+ !memcmp(&p->channels[0], &p->channels[2], sizeof(p->channels[0]));
+}
+
+AVGainMapParams *av_gain_map_params_alloc(size_t *size)
+{
+ AVGainMapParams *p = av_malloc(sizeof(AVGainMapParams));
+ if (!p)
+ return NULL;
+
+ gain_map_params_default(p);
+
+ if (size)
+ *size = sizeof(*p);
+
+ return p;
+}
+
+AVGainMapParams *av_gain_map_params_create_side_data(AVFrameSideData ***fsd, int *nb_sd)
+{
+ AVFrameSideData *sd;
+ sd = av_frame_side_data_new(fsd, nb_sd, AV_FRAME_DATA_GAIN_MAP_PARAMS,
+ sizeof(AVGainMapParams),
+ AV_FRAME_SIDE_DATA_FLAG_REPLACE);
+ if (!sd)
+ return NULL;
+
+ AVGainMapParams *const p = (AVGainMapParams *) sd->data;
+ gain_map_params_default(p);
+ return p;
+}
diff --git a/libavutil/gain_map.h b/libavutil/gain_map.h
new file mode 100644
index 0000000000..7d922d7b40
--- /dev/null
+++ b/libavutil/gain_map.h
@@ -0,0 +1,115 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVUTIL_GAIN_MAP_H
+#define AVUTIL_GAIN_MAP_H
+
+#include <stddef.h>
+
+#include "rational.h"
+#include "frame.h"
+
+#define AV_ISO21496_VERSION 0
+#define AV_ISO21496_IDENTIFIER "urn:iso:std:iso:ts:21496:-1"
+
+/**
+ * Parameters describing how to combine a gain map rendition with its base
+ * rendition to recover the alternate rendition, as defined by
+ * ISO 21496-1:2025 "Digital photography - Gain map metadata for image
+ * conversion".
+ *
+ * Note: sizeof(AVGainMapParams) is not part of the ABI. New fields may be
+ * added at the end of the struct.
+ */
+typedef struct AVGainMapParams {
+ /**
+ * The version of this gain map struct. Reserved to allow future
+ * extensions of the struct. Must be <= AV_ISO21496_VERSION.
+ */
+ int version;
+
+ /**
+ * If non-zero, the gain map is applied in the colour space of the base
+ * rendition; otherwise in that of the alternate rendition.
+ */
+ int use_base_color_space;
+
+ /**
+ * log2 of the display headroom at which the base rendition is shown
+ * unmodified, and at which the map is applied in full.
+ */
+ AVRational base_hdr_headroom;
+ AVRational alternate_hdr_headroom;
+
+ struct AVGainMapChannel {
+ /**
+ * log2-domain minimum and maximum gain applied by the map, i.e. the
+ * values the map's 0.0 and 1.0 endpoints decode to.
+ */
+ AVRational gain_map_min;
+ AVRational gain_map_max;
+
+ /**
+ * Encoding gamma of the stored map values. Must be > 0.
+ */
+ AVRational gamma;
+
+ /**
+ * Small constants added before the log-domain math to avoid a
+ * singularity at zero.
+ */
+ AVRational base_offset;
+ AVRational alternate_offset;
+ } channels[3]; /* R, G, B */
+
+ /**
+ * Must be 1 or 3. If 1, all channels share the same set of parameters.
+ */
+ int nb_channels;
+} AVGainMapParams;
+
+/**
+ * Returns >= 0 if the given AVGainMapParams struct contains valid data;
+ * or a negative AVERROR otherwise.
+ */
+int av_gain_map_params_validate(const AVGainMapParams *p);
+
+/**
+ * Returns 1 if all channels of the gain map have identical parameters,
+ * 0 otherwise.
+ */
+int av_gain_map_channels_identical(const AVGainMapParams *p);
+
+/**
+ * Allocate an AVGainMapParams structure and initialize it to default values.
+ * The resulting pointer must be freed using av_free().
+ *
+ * @param size if non-NULL, set to sizeof(AVGainMapParams)
+ * @return the newly allocated struct, or NULL on failure
+ */
+AVGainMapParams *av_gain_map_params_alloc(size_t *size);
+
+/**
+ * Allocate and add an AVGainMapParams structure to an existing AVFrameSideData
+ * array as AV_FRAME_DATA_GAIN_MAP_PARAMS side data.
+ *
+ * @return the newly allocated struct, or NULL on failure
+ */
+AVGainMapParams *av_gain_map_params_create_side_data(AVFrameSideData ***sd, int *nb_sd);
+
+#endif /* AVUTIL_GAIN_MAP_H */
diff --git a/libavutil/side_data.c b/libavutil/side_data.c
index 0b4869c941..0d0289a9b6 100644
--- a/libavutil/side_data.c
+++ b/libavutil/side_data.c
@@ -63,6 +63,7 @@ static const AVSideDataDescriptor sd_props[] = {
[AV_FRAME_DATA_IAMF_RECON_GAIN_INFO_PARAM] = { "IAMF Recon Gain Info Parameter Data" },
[AV_FRAME_DATA_RAW_COLOR_PARAMS] = { "RAW camera color parameters", AV_SIDE_DATA_PROP_GLOBAL | AV_SIDE_DATA_PROP_COLOR_DEPENDENT },
[AV_FRAME_DATA_DOWNMIX_MATRIX] = { "Downmix Matrix", AV_SIDE_DATA_PROP_CHANNEL_DEPENDENT },
+ [AV_FRAME_DATA_GAIN_MAP_PARAMS] = { "Gain map parameters", AV_SIDE_DATA_PROP_GLOBAL | AV_SIDE_DATA_PROP_COLOR_DEPENDENT },
};
const AVSideDataDescriptor *av_frame_side_data_desc(enum AVFrameSideDataType type)
diff --git a/libavutil/version.h b/libavutil/version.h
index 1145f23acd..1a2406e75e 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
*/
#define LIBAVUTIL_VERSION_MAJOR 61
-#define LIBAVUTIL_VERSION_MINOR 9
+#define LIBAVUTIL_VERSION_MINOR 10
#define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \