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;