This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit ebbc9b6600d36608f523d0d97fcb4c52322870e5 Author: Niklas Haas <[email protected]> AuthorDate: Fri Aug 28 16:49:27 2026 +0200 Commit: Niklas Haas <[email protected]> CommitDate: Wed Sep 30 14:37:59 2026 +0200 avcodec/mjpegenc: add -xmp_gain_map option Controls whether or not to serialize the gain map as an XMP blob in addition to an ISO blob. Defaults to -1, meaning to serialize the gain map only if compatible. Signed-off-by: Niklas Haas <[email protected]> --- libavcodec/mjpegenc.c | 3 ++- libavcodec/mjpegenc.h | 3 +++ libavcodec/mjpegenc_common.c | 53 ++++++++++++++++++++++++++++++++++------- libavcodec/mjpegenc_common.h | 4 ++-- libavcodec/mpegvideo_enc.c | 2 +- tests/ref/fate/jpeg-mpf-gainmap | 2 +- 6 files changed, 53 insertions(+), 14 deletions(-) diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c index b9ebe3d769..8dc3217acf 100644 --- a/libavcodec/mjpegenc.c +++ b/libavcodec/mjpegenc.c @@ -644,11 +644,12 @@ static int amv_encode_picture(AVCodecContext *avctx, AVPacket *pkt, #define OFFSET(x) offsetof(MJPEGEncContext, mjpeg.x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { -#define AMV_OPTIONS_OFFSET 4 +#define AMV_OPTIONS_OFFSET 5 { "huffman", "Huffman table strategy", OFFSET(huffman), AV_OPT_TYPE_INT, { .i64 = HUFFMAN_TABLE_OPTIMAL }, 0, NB_HUFFMAN_TABLE_OPTION - 1, VE, .unit = "huffman" }, { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = HUFFMAN_TABLE_DEFAULT }, INT_MIN, INT_MAX, VE, .unit = "huffman" }, { "optimal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = HUFFMAN_TABLE_OPTIMAL }, INT_MIN, INT_MAX, VE, .unit = "huffman" }, { "force_duplicated_matrix", "Always write luma and chroma matrix for mjpeg, useful for rtp streaming.", OFFSET(force_duplicated_matrix), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE }, +{ "xmp_gain_map", "Write Ultra HDR gain map metadata as an XMP packet", OFFSET(xmp_gain_map), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, FF_MPV_COMMON_OPTS { NULL}, }; diff --git a/libavcodec/mjpegenc.h b/libavcodec/mjpegenc.h index 92feed28b4..865f13f50e 100644 --- a/libavcodec/mjpegenc.h +++ b/libavcodec/mjpegenc.h @@ -45,6 +45,9 @@ typedef struct MJpegContext { int huffman; /* Force duplication of mjpeg matrices, useful for rtp streaming */ int force_duplicated_matrix; + /* Tristate controlling the Ultra HDR gain map XMP packet */ + int xmp_gain_map; + int xmp_warned; //FIXME use array [3] instead of lumi / chroma, for easier addressing uint8_t huff_size_dc_luminance[12]; ///< DC luminance Huffman table size. uint16_t huff_code_dc_luminance[12]; ///< DC luminance Huffman table codes. diff --git a/libavcodec/mjpegenc_common.c b/libavcodec/mjpegenc_common.c index a4464c3f90..235a46799a 100644 --- a/libavcodec/mjpegenc_common.c +++ b/libavcodec/mjpegenc_common.c @@ -160,8 +160,14 @@ int ff_mjpeg_add_icc_profile_size(AVCodecContext *avctx, const AVFrame *frame, return 0; } -int ff_mjpeg_add_gain_map_size(AVCodecContext *avctx, const AVFrame *frame, - size_t *max_pkt_size) +static int want_gain_map_xmp(const struct MJpegContext *m, + const AVGainMapParams *p) +{ + return m && m->xmp_gain_map && ff_gain_map_params_check_xmp(p); +} + +int ff_mjpeg_add_gain_map_size(AVCodecContext *avctx, struct MJpegContext *m, + const AVFrame *frame, size_t *max_pkt_size) { if (avctx->codec_id != AV_CODEC_ID_MJPEG) return 0; @@ -176,8 +182,20 @@ int ff_mjpeg_add_gain_map_size(AVCodecContext *avctx, const AVFrame *frame, if (ret < 0) return ret; - size_t app2_size = 4 + sizeof(AV_ISO21496_IDENTIFIER) + FF_GAIN_MAP_MAX_PAYLOAD_SIZE; - size_t new_pkt_size = *max_pkt_size + app2_size; + size_t extra_size = 4 + sizeof(AV_ISO21496_IDENTIFIER) + FF_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; + av_log_once(avctx, level, AV_LOG_VERBOSE, &m->xmp_warned, + "Gain map metadata is incompatible with Ultra HDR XMP\n"); + if (m->xmp_gain_map > 0) + return AVERROR(ENOTSUP); + } else { + extra_size += 4 + sizeof(FF_GAIN_MAP_XMP_IDENT) + FF_GAIN_MAP_XMP_MAX_LEN; + } + } + + size_t new_pkt_size = *max_pkt_size + extra_size; if (new_pkt_size < *max_pkt_size) /* overflow */ return AVERROR_INVALIDDATA; *max_pkt_size = new_pkt_size; @@ -185,7 +203,7 @@ int ff_mjpeg_add_gain_map_size(AVCodecContext *avctx, const AVFrame *frame, } static void jpeg_put_comments(AVCodecContext *avctx, PutBitContext *p, - const AVFrame *frame) + const AVFrame *frame, const struct MJpegContext *m) { const AVFrameSideData *sd = NULL; int size; @@ -246,9 +264,27 @@ static void jpeg_put_comments(AVCodecContext *avctx, PutBitContext *p, av_assert1(!remaining); } - /* ISO 21496-1 gain map metadata */ + /* Gain map metadata; the ISO 21496-1 blob is written directly after the + * XMP packet, as recommended by the Ultra HDR specification */ sd = avctx->codec_id == AV_CODEC_ID_MJPEG ? av_frame_get_side_data(frame, AV_FRAME_DATA_GAIN_MAP_PARAMS) : NULL; + + const AVGainMapParams *gmp = sd ? (const AVGainMapParams *) sd->data : NULL; + if (sd && sd->size >= sizeof(AVGainMapParams) && want_gain_map_xmp(m, gmp)) { + put_marker(p, APP1); + flush_put_bits(p); + ptr = put_bits_ptr(p); + put_bits(p, 16, 0); /* patched later */ + ff_put_string(p, FF_GAIN_MAP_XMP_IDENT, 1); + flush_put_bits(p); + + /* pre-validated by ff_mjpeg_add_gain_map_size() */ + size = ff_gain_map_params_to_xmp(gmp, put_bits_ptr(p)); + av_assert0(size >= 0); + skip_put_bytes(p, size); + AV_WB16(ptr, 2 + sizeof(FF_GAIN_MAP_XMP_IDENT) + size); + } + if (sd && sd->size >= sizeof(AVGainMapParams)) { put_marker(p, APP2); flush_put_bits(p); @@ -258,8 +294,7 @@ static void jpeg_put_comments(AVCodecContext *avctx, PutBitContext *p, flush_put_bits(p); /* pre-validated by ff_mjpeg_add_gain_map_size() */ - const AVGainMapParams *params = (const AVGainMapParams *) sd->data; - size = ff_gain_map_params_to_iso21496(params, put_bits_ptr(p)); + size = ff_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); @@ -336,7 +371,7 @@ void ff_mjpeg_encode_picture_header(AVCodecContext *avctx, PutBitContext *pb, if (avctx->codec_id == AV_CODEC_ID_AMV) return; - jpeg_put_comments(avctx, pb, frame); + jpeg_put_comments(avctx, pb, frame, m); chroma_matrix = !lossless && !!memcmp(luma_intra_matrix, chroma_intra_matrix, diff --git a/libavcodec/mjpegenc_common.h b/libavcodec/mjpegenc_common.h index 4d1e2a5477..20e3902c03 100644 --- a/libavcodec/mjpegenc_common.h +++ b/libavcodec/mjpegenc_common.h @@ -30,8 +30,8 @@ struct MJpegContext; int ff_mjpeg_add_icc_profile_size(AVCodecContext *avctx, const AVFrame *frame, size_t *max_pkt_size); -int ff_mjpeg_add_gain_map_size(AVCodecContext *avctx, const AVFrame *frame, - size_t *max_pkt_size); +int ff_mjpeg_add_gain_map_size(AVCodecContext *avctx, struct MJpegContext *m, + const AVFrame *frame, size_t *max_pkt_size); void ff_mjpeg_encode_picture_header(AVCodecContext *avctx, PutBitContext *pb, const AVFrame *frame, const struct MJpegContext *m, const uint8_t intra_matrix_permutation[64], diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c index 592334473a..3908628504 100644 --- a/libavcodec/mpegvideo_enc.c +++ b/libavcodec/mpegvideo_enc.c @@ -1939,7 +1939,7 @@ int ff_mpv_encode_picture(AVCodecContext *avctx, AVPacket *pkt, ret = ff_mjpeg_add_icc_profile_size(avctx, s->new_pic, &pkt_size); if (ret < 0) return ret; - ret = ff_mjpeg_add_gain_map_size(avctx, s->new_pic, &pkt_size); + ret = ff_mjpeg_add_gain_map_size(avctx, s->mjpeg_ctx, s->new_pic, &pkt_size); if (ret < 0) return ret; } diff --git a/tests/ref/fate/jpeg-mpf-gainmap b/tests/ref/fate/jpeg-mpf-gainmap index 2fee05c244..999852b117 100644 --- a/tests/ref/fate/jpeg-mpf-gainmap +++ b/tests/ref/fate/jpeg-mpf-gainmap @@ -1 +1 @@ -50dc4be69672174d4ff3ca8dbdaaa9d3 +e7c134ebd17c6d040e8eeaaada4633f2 -- 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]
