Commit 1b4ca126d8 for ffmpeg

commit 1b4ca126d814f95e3ec546cbe8839275f5e5def5
Author: Niklas Haas <git@haasn.dev>
Date:   Fri Aug 21 16:29:40 2026 +0200

    avcodec/mjpegenc: add support for writing ISO 21496-1 gain map metadata

    This is automatically emitted as an APP2 blob following the ISO 21496-1
    specification. To ensure that writing the gain map will succeed, we validate
    the paramaters when allocating space, and error out if invalid data is
    encountered.

    Signed-off-by: Niklas Haas <git@haasn.dev>

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 7785638c77..5ee0c00bc6 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -239,7 +239,7 @@ OBJS-$(CONFIG_AMRWB_DECODER)           += amrwbdec.o celp_filters.o   \
                                           acelp_pitch_delay.o
 OBJS-$(CONFIG_AMRNB_MEDIACODEC_DECODER) += mediacodecdec.o
 OBJS-$(CONFIG_AMRWB_MEDIACODEC_DECODER) += mediacodecdec.o
-OBJS-$(CONFIG_AMV_ENCODER)             += mjpegenc.o mjpegenc_common.o
+OBJS-$(CONFIG_AMV_ENCODER)             += mjpegenc.o mjpegenc_common.o gain_map.o
 OBJS-$(CONFIG_ANM_DECODER)             += anm.o
 OBJS-$(CONFIG_ANULL_DECODER)           += null.o
 OBJS-$(CONFIG_ANULL_ENCODER)           += null.o
@@ -520,7 +520,7 @@ OBJS-$(CONFIG_KGV1_DECODER)            += kgv1dec.o
 OBJS-$(CONFIG_KMVC_DECODER)            += kmvc.o
 OBJS-$(CONFIG_LAGARITH_DECODER)        += lagarith.o lagarithrac.o
 OBJS-$(CONFIG_LEAD_DECODER)            += leaddec.o jpegquanttables.o
-OBJS-$(CONFIG_LJPEG_ENCODER)           += ljpegenc.o mjpegenc_common.o
+OBJS-$(CONFIG_LJPEG_ENCODER)           += ljpegenc.o mjpegenc_common.o gain_map.o
 OBJS-$(CONFIG_LOCO_DECODER)            += loco.o
 OBJS-$(CONFIG_LSCR_DECODER)            += lscrdec.o png.o pngdec.o pngdsp.o
 OBJS-$(CONFIG_M101_DECODER)            += m101.o
@@ -537,7 +537,7 @@ OBJS-$(CONFIG_MISC4_DECODER)           += misc4.o
 OBJS-$(CONFIG_MJPEG_DECODER)           += mjpegdec.o mjpegdec_common.o
 OBJS-$(CONFIG_MJPEG_QSV_DECODER)       += qsvdec.o
 OBJS-$(CONFIG_MJPEG_ENCODER)           += mjpegenc.o mjpegenc_common.o \
-                                          mjpegenc_huffman.o
+                                          mjpegenc_huffman.o gain_map.o
 OBJS-$(CONFIG_MJPEGB_DECODER)          += mjpegbdec.o
 OBJS-$(CONFIG_MJPEG_CUVID_DECODER)     += cuviddec.o
 OBJS-$(CONFIG_MJPEG_QSV_ENCODER)       += qsvenc_jpeg.o
diff --git a/libavcodec/mjpegenc_common.c b/libavcodec/mjpegenc_common.c
index 5effbdbc32..a4464c3f90 100644
--- a/libavcodec/mjpegenc_common.c
+++ b/libavcodec/mjpegenc_common.c
@@ -31,6 +31,7 @@
 #include "jpegtables.h"
 #include "put_bits.h"
 #include "mjpegenc.h"
+#include "gain_map.h"
 #include "mjpegenc_common.h"
 #include "mjpeg.h"
 #include "version.h"
@@ -159,6 +160,30 @@ 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)
+{
+    if (avctx->codec_id != AV_CODEC_ID_MJPEG)
+        return 0;
+
+    AVFrameSideData *sd;
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_GAIN_MAP_PARAMS);
+    if (!sd || sd->size < sizeof(AVGainMapParams))
+        return 0;
+
+    const AVGainMapParams *params = (const AVGainMapParams *) sd->data;
+    int ret = av_gain_map_params_validate(params);
+    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;
+    if (new_pkt_size < *max_pkt_size) /* overflow */
+        return AVERROR_INVALIDDATA;
+    *max_pkt_size = new_pkt_size;
+    return 0;
+}
+
 static void jpeg_put_comments(AVCodecContext *avctx, PutBitContext *p,
                               const AVFrame *frame)
 {
@@ -221,6 +246,25 @@ static void jpeg_put_comments(AVCodecContext *avctx, PutBitContext *p,
         av_assert1(!remaining);
     }

+    /* ISO 21496-1 gain map metadata */
+    sd = avctx->codec_id == AV_CODEC_ID_MJPEG ?
+         av_frame_get_side_data(frame, AV_FRAME_DATA_GAIN_MAP_PARAMS) : NULL;
+    if (sd && sd->size >= sizeof(AVGainMapParams)) {
+        put_marker(p, APP2);
+        flush_put_bits(p);
+        ptr = put_bits_ptr(p);
+        put_bits(p, 16, 0); /* patched later */
+        ff_put_string(p, AV_ISO21496_IDENTIFIER, 1);
+        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));
+        av_assert0(size >= 0);
+        skip_put_bytes(p, size);
+        AV_WB16(ptr, 2 + sizeof(AV_ISO21496_IDENTIFIER) + size);
+    }
+
     /* comment */
     if (!(avctx->flags & AV_CODEC_FLAG_BITEXACT)) {
         put_marker(p, COM);
diff --git a/libavcodec/mjpegenc_common.h b/libavcodec/mjpegenc_common.h
index 0cf5a72706..4d1e2a5477 100644
--- a/libavcodec/mjpegenc_common.h
+++ b/libavcodec/mjpegenc_common.h
@@ -30,6 +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);
 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 2bbbe01b82..592334473a 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -1939,6 +1939,9 @@ 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);
+            if (ret < 0)
+                return ret;
         }
         if ((ret = ff_alloc_packet(avctx, pkt, pkt_size)) < 0)
             return ret;