This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 2ac7367f2ff82dc6f43d4577c89d59f7831d70ed Author: James Almer <[email protected]> AuthorDate: Mon Oct 5 17:05:57 2026 -0300 Commit: James Almer <[email protected]> CommitDate: Mon Oct 12 10:39:10 2026 -0300 avutil/gain_map: move ISO 21496-1 gain map payload parsing from libavcodec And export both functions. This is needed for a following change that will make use of these functions from lavf. Signed-off-by: James Almer <[email protected]> --- doc/APIchanges | 4 ++ libavcodec/gain_map.c | 142 ------------------------------------------- libavcodec/gain_map.h | 30 --------- libavcodec/mjpegenc_common.c | 4 +- libavutil/gain_map.c | 140 ++++++++++++++++++++++++++++++++++++++++++ libavutil/gain_map.h | 30 +++++++++ libavutil/version.h | 2 +- 7 files changed, 177 insertions(+), 175 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 7d7956eac3..f7f9f5d9e1 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,10 @@ The last version increases of all libraries were on 2026-06-23. API changes, most recent first: +2026-10-xx - xxxxxxxxxx - lavu 61.14.100 - gain_map.h + Add av_gain_map_params_from_iso21496() and av_gain_map_params_to_iso21496(). + Add AV_GAIN_MAP_MAX_PAYLOAD_SIZE. + 2026-10-12 - xxxxxxxxxx - lavc 63.19.100 - packet.h Add AV_PKT_DATA_GAIN_MAP_PARAMS. diff --git a/libavcodec/gain_map.c b/libavcodec/gain_map.c index 172adc218b..e85e120634 100644 --- a/libavcodec/gain_map.c +++ b/libavcodec/gain_map.c @@ -28,148 +28,6 @@ #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); -} - int ff_gain_map_params_check_xmp(const AVGainMapParams *p) { if (av_gain_map_params_validate(p) < 0) diff --git a/libavcodec/gain_map.h b/libavcodec/gain_map.h index 53207e448b..7d98723ef5 100644 --- a/libavcodec/gain_map.h +++ b/libavcodec/gain_map.h @@ -31,42 +31,12 @@ /* 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 */ /* For XMP-based encoding */ #define FF_GAIN_MAP_XMP_IDENT "http://ns.adobe.com/xap/1.0/" #define FF_GAIN_MAP_XMP_NAMESPACE "http://ns.adobe.com/hdr-gain-map/1.0/" #define FF_GAIN_MAP_XMP_MAX_LEN 1024 -/** - * 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); - /** * Returns 1 if the gain map parameters are compatible with the XMP-based * encoding, or 0 otherwise. diff --git a/libavcodec/mjpegenc_common.c b/libavcodec/mjpegenc_common.c index 235a46799a..bc58ca9903 100644 --- a/libavcodec/mjpegenc_common.c +++ b/libavcodec/mjpegenc_common.c @@ -182,7 +182,7 @@ int ff_mjpeg_add_gain_map_size(AVCodecContext *avctx, struct MJpegContext *m, if (ret < 0) return ret; - size_t extra_size = 4 + sizeof(AV_ISO21496_IDENTIFIER) + FF_GAIN_MAP_MAX_PAYLOAD_SIZE; + size_t extra_size = 4 + sizeof(AV_ISO21496_IDENTIFIER) + AV_GAIN_MAP_MAX_PAYLOAD_SIZE; if (m && m->xmp_gain_map) { if (!ff_gain_map_params_check_xmp(params)) { const int level = m->xmp_gain_map > 0 ? AV_LOG_ERROR : AV_LOG_WARNING; @@ -294,7 +294,7 @@ static void jpeg_put_comments(AVCodecContext *avctx, PutBitContext *p, flush_put_bits(p); /* pre-validated by ff_mjpeg_add_gain_map_size() */ - size = ff_gain_map_params_to_iso21496(gmp, put_bits_ptr(p)); + size = av_gain_map_params_to_iso21496(gmp, put_bits_ptr(p)); av_assert0(size >= 0); skip_put_bytes(p, size); AV_WB16(ptr, 2 + sizeof(AV_ISO21496_IDENTIFIER) + size); diff --git a/libavutil/gain_map.c b/libavutil/gain_map.c index 83c5bd31cf..03cb4ca578 100644 --- a/libavutil/gain_map.c +++ b/libavutil/gain_map.c @@ -18,6 +18,7 @@ #include "gain_map.h" #include "mem.h" +#include "libavcodec/bytestream.h" static void gain_map_params_default(AVGainMapParams *p) { @@ -72,6 +73,145 @@ int av_gain_map_params_validate(const AVGainMapParams *p) return 0; } +#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 av_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 > AV_ISO21496_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 = AV_ISO21496_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 av_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; + + PutByteContext pb; + bytestream2_init_writer(&pb, buf, AV_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); +} + int av_gain_map_channels_identical(const AVGainMapParams *p) { static_assert(sizeof(p->channels[0]) == sizeof(int[2]) * 5, diff --git a/libavutil/gain_map.h b/libavutil/gain_map.h index 7d922d7b40..12499cdd52 100644 --- a/libavutil/gain_map.h +++ b/libavutil/gain_map.h @@ -83,6 +83,36 @@ typedef struct AVGainMapParams { int nb_channels; } AVGainMapParams; +/** + * 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 av_gain_map_params_from_iso21496(AVGainMapParams *p, const uint8_t *data, + size_t size); + +#define AV_GAIN_MAP_MAX_PAYLOAD_SIZE 141 /* = 5 + 2*8 + 3*5*8 */ + +/** + * 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 AV_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. + */ +int av_gain_map_params_to_iso21496(const AVGainMapParams *p, uint8_t *buf); + /** * Returns >= 0 if the given AVGainMapParams struct contains valid data; * or a negative AVERROR otherwise. diff --git a/libavutil/version.h b/libavutil/version.h index de614c9866..ed6abeab78 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 61 -#define LIBAVUTIL_VERSION_MINOR 13 +#define LIBAVUTIL_VERSION_MINOR 14 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ -- 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]
