This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 782d60e3a5af615ab3d43b2a76008646b186dfd2 Author: Niklas Haas <[email protected]> AuthorDate: Fri Aug 21 13:23:56 2026 +0200 Commit: Niklas Haas <[email protected]> CommitDate: Wed Sep 30 14:37:59 2026 +0200 avcodec/gain_map: add ff_gain_map_params_(to/from)_iso21496 I tested it locally by ensuring it round trips, though I did not yet test the output against the libultrahdr reference blob. A proper FATE test will be included implicitly once we have a corresponding muxer. Signed-off-by: Niklas Haas <[email protected]> --- libavcodec/gain_map.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/gain_map.h | 65 +++++++++++++++++++ 2 files changed, 233 insertions(+) diff --git a/libavcodec/gain_map.c b/libavcodec/gain_map.c new file mode 100644 index 0000000000..bdb455adca --- /dev/null +++ b/libavcodec/gain_map.c @@ -0,0 +1,168 @@ +/* + * 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 "libavutil/avassert.h" +#include "libavutil/error.h" +#include "libavutil/gain_map.h" +#include "libavutil/mem.h" +#include "libavutil/rational.h" + +#include "bytestream.h" +#include "gain_map.h" + +#define ISO21496_HEADER_SIZE 5 + +#define ISO21496_FLAG_MULTICHANNEL (1 << 7) +#define ISO21496_FLAG_USE_BASE_CG (1 << 6) +#define ISO21496_FLAG_RESERVED 0x3f + +static size_t iso21496_payload_size(int nb_channels) +{ + return 4 * 4 + (size_t) nb_channels * 10 * 4; +} + +static int make_q(int64_t num, uint32_t den, AVRational *out) +{ + if (!den) + return AVERROR_INVALIDDATA; + + /* Ensure the result fits in AVRational */ + av_reduce(&out->num, &out->den, num, den, INT32_MAX); + return 0; +} + +static int get_sq(GetByteContext *gb, AVRational *out) +{ + int32_t num = (int32_t) bytestream2_get_be32(gb); + uint32_t den = bytestream2_get_be32u(gb); + return make_q(num, den, out); +} + +static int get_uq(GetByteContext *gb, AVRational *out) +{ + uint32_t num = bytestream2_get_be32u(gb); + uint32_t den = bytestream2_get_be32u(gb); + return make_q(num, den, out); +} + +int ff_gain_map_params_from_iso21496(AVGainMapParams *restrict p, + const uint8_t *data, size_t size) +{ + int ret; + if (!p || !data) + return AVERROR(EINVAL); + + if (size < ISO21496_HEADER_SIZE || size > INT_MAX) + return AVERROR_INVALIDDATA; + + GetByteContext gb; + bytestream2_init(&gb, data, size); + + /* Parse header */ + const uint16_t minimum_version = bytestream2_get_be16(&gb); + const uint16_t writer_version = bytestream2_get_be16(&gb); + if (minimum_version > FF_GAIN_MAP_VERSION || writer_version < minimum_version) + return AVERROR_PATCHWELCOME; + const uint8_t flags = bytestream2_get_byte(&gb); + + /** + * While the spec does not require reserved bits to be 0, older versions + * of the ISO 21496-1 draft specification had extra flags that got removed + * from the final publication, so conservatively error out just in case; + * unless the writer version is >0 in which case these may be genuine + * additions to later revisions of the published spec, the validity of + * ignoring which is already guarded by the `minimum_version` check. + */ + if (writer_version == 0 && (flags & ISO21496_FLAG_RESERVED)) + return AVERROR_INVALIDDATA; + + p->version = FF_GAIN_MAP_VERSION; + p->nb_channels = (flags & ISO21496_FLAG_MULTICHANNEL) ? 3 : 1; + p->use_base_color_space = !!(flags & ISO21496_FLAG_USE_BASE_CG); + + /* Parse payload */ + if (bytestream2_get_bytes_left(&gb) < iso21496_payload_size(p->nb_channels)) + return AVERROR_INVALIDDATA; + + if ((ret = get_uq(&gb, &p->base_hdr_headroom)) < 0 || + (ret = get_uq(&gb, &p->alternate_hdr_headroom)) < 0) + return ret; + + for (int c = 0; c < p->nb_channels; c++) { + struct AVGainMapChannel *const ch = &p->channels[c]; + if ((ret = get_sq(&gb, &ch->gain_map_min)) < 0 || + (ret = get_sq(&gb, &ch->gain_map_max)) < 0 || + (ret = get_uq(&gb, &ch->gamma)) < 0 || + (ret = get_sq(&gb, &ch->base_offset)) < 0 || + (ret = get_sq(&gb, &ch->alternate_offset)) < 0) + return ret; + } + + /* nb_channels == 1 means every channel shares the same parameters */ + for (int c = p->nb_channels; c < 3; c++) + p->channels[c] = p->channels[0]; + + return av_gain_map_params_validate(p); +} + +static void put_q(PutByteContext *pb, AVRational q) +{ + bytestream2_put_be32(pb, (unsigned) q.num); + bytestream2_put_be32(pb, (unsigned) q.den); +} + +int ff_gain_map_params_to_iso21496(const AVGainMapParams *p, uint8_t *buf) +{ + if (!p || !buf) + return AVERROR(EINVAL); + + /* Ensures representability in the bitstream */ + int ret = av_gain_map_params_validate(p); + if (ret < 0) + return ret; + + if (p->version > FF_GAIN_MAP_VERSION) + return AVERROR_PATCHWELCOME; /* e.g. libraries out of sync */ + + PutByteContext pb; + bytestream2_init_writer(&pb, buf, FF_GAIN_MAP_MAX_PAYLOAD_SIZE); + + int channels = av_gain_map_channels_identical(p) ? 1 : p->nb_channels; + uint8_t flags = 0; + if (channels == 3) + flags |= ISO21496_FLAG_MULTICHANNEL; + if (p->use_base_color_space) + flags |= ISO21496_FLAG_USE_BASE_CG; + + bytestream2_put_be16(&pb, p->version); /* minimum_version */ + bytestream2_put_be16(&pb, p->version); /* writer_version */ + bytestream2_put_byte(&pb, flags); + put_q(&pb, p->base_hdr_headroom); + put_q(&pb, p->alternate_hdr_headroom); + + for (int c = 0; c < channels; c++) { + const struct AVGainMapChannel *const ch = &p->channels[c]; + put_q(&pb, ch->gain_map_min); + put_q(&pb, ch->gain_map_max); + put_q(&pb, ch->gamma); + put_q(&pb, ch->base_offset); + put_q(&pb, ch->alternate_offset); + } + + return bytestream2_tell_p(&pb); +} diff --git a/libavcodec/gain_map.h b/libavcodec/gain_map.h new file mode 100644 index 0000000000..0889939e35 --- /dev/null +++ b/libavcodec/gain_map.h @@ -0,0 +1,65 @@ +/* + * 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 + */ + +/** + * @file + * Serialization of AVGainMapParams into the metadata blobs that image formats + * carry it in, and back. Kept separate from the codecs so that an encoder + * writing the blob and a bitstream filter splicing it into an existing + * bitstream can share the same code. + */ + +#ifndef AVCODEC_GAIN_MAP_H +#define AVCODEC_GAIN_MAP_H + +#include "libavutil/gain_map.h" + +/* May differ from AV_ISO21496_VERSION if libraries are not in sync */ +#define FF_GAIN_MAP_VERSION 0 +#define FF_GAIN_MAP_MAX_PAYLOAD_SIZE 141 /* = 5 + 2*8 + 3*5*8 */ + +/** + * Parse the payload of an ISO 21496-1 gain map metadata blob. + * + * @param p struct to fill; overwritten on success, untouched on failure + * @param data payload, excluding container-specific identifiers + * @param size size of `data` in bytes + * + * @return >= 0 on success, or a negative AVERROR on failure + */ +int ff_gain_map_params_from_iso21496(AVGainMapParams *p, const uint8_t *data, + size_t size); + +/** + * Serialize an AVGainMapParams as an ISO 21496-1 gain map metadata blob. + * + * The output starts at the minimum_version field; the caller should prepend + * whatever identifier its container requires. + * + * @param p parameters to serialize + * @param buf Byte buffer to fill with the serialized data. Must contain at + * least FF_GAIN_MAP_MAX_PAYLOAD_SIZE bytes. + * + * @return Number of bytes written on success, or a negative AVERROR on failure + * + * @note Always succeeds if av_gain_map_params_validate() returns >= 0, and + * p->version <= AV_ISO21496_VERSION. + */ +int ff_gain_map_params_to_iso21496(const AVGainMapParams *p, uint8_t *buf); + +#endif /* AVCODEC_GAIN_MAP_H */ -- To stop receiving notification emails like this one, please contact [email protected]. _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
