PR #24859 opened by rnissl
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24859
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24859.patch

Baseline JPEG has no color-matrix marker. The MJPEG decoder reports bt470bg, 
while the encoder context keeps the source matrix (bt709 on GoPro HEVC). The 
stream dump then names a matrix the file does not contain, which sent color 
debugging the wrong way.

Warn once per encoder when a YUV frame carries a matrix other than bt470bg or 
smpte170m. Unspecified and RGB input stay quiet. The samples are written 
unchanged: no APP, COLR, or ICC marker is added for the matrix.

The software MJPEG and lossless JPEG encoders, AMV, mjpeg_qsv, and mjpeg_vaapi 
share the check. mjpeg_nvjpeg is not in this tree.



From 0a48b5cebf0a7f337d106c4689391387bba4cf52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= <[email protected]>
Date: Sat, 3 Oct 2026 10:02:47 +0200
Subject: [PATCH] avcodec/mjpeg: warn when the YUV matrix is not stored
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Baseline JPEG has no color-matrix marker. The MJPEG decoder reports
bt470bg, while the encoder context keeps the source matrix (bt709 on
GoPro HEVC). The stream dump then names a matrix the file does not
contain.

Warn once per encoder when a YUV frame carries a matrix other than
bt470bg or smpte170m. Unspecified and RGB input stay quiet. The
samples are written unchanged.

Co-authored-by: Cursor <[email protected]>
Signed-off-by: Reinhard Nißl <[email protected]>
---
 Changelog                       |  1 +
 doc/encoders.texi               |  8 ++++++
 libavcodec/ljpegenc.c           |  2 ++
 libavcodec/mjpegenc.c           |  4 +++
 libavcodec/mjpegenc.h           |  2 ++
 libavcodec/mjpegenc_common.h    | 44 +++++++++++++++++++++++++++++++++
 libavcodec/qsvenc_jpeg.c        |  3 +++
 libavcodec/vaapi_encode_mjpeg.c |  5 ++++
 8 files changed, 69 insertions(+)

diff --git a/Changelog b/Changelog
index 1cfa9db1b6..08b9c686dc 100644
--- a/Changelog
+++ b/Changelog
@@ -14,6 +14,7 @@ version <next>:
 - NVIDIA optical flow accelerated interpolation filter (vf_fruc_vulkan)
 - H.264 data partitioning support
 - DSD (dsd_msbf) encoder
+- MJPEG encoders warn when a YUV matrix other than bt470bg or smpte170m is not 
stored in the file
 
 
 version 9.0:
diff --git a/doc/encoders.texi b/doc/encoders.texi
index a3ce15fcff..2a12012c60 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -1252,6 +1252,14 @@ transient response is a higher bitrate.
 
 Motion JPEG encoder.
 
+Baseline JPEG does not store a YUV color matrix. When the input frame
+names one, or the encoder context does and the frame leaves it
+unspecified, and that matrix is neither @samp{bt470bg} nor
+@samp{smpte170m}, the encoder logs a warning once. The same check is
+used by the lossless JPEG, AMV, @code{mjpeg_qsv} and @code{mjpeg_vaapi}
+encoders. The bitstream is written unchanged, and a decoder reports
+@samp{bt470bg}.
+
 @subsection Options
 
 @table @option
diff --git a/libavcodec/ljpegenc.c b/libavcodec/ljpegenc.c
index ab37ab0d5c..766583cfae 100644
--- a/libavcodec/ljpegenc.c
+++ b/libavcodec/ljpegenc.c
@@ -54,6 +54,7 @@ typedef struct LJpegEncContext {
 
     uint16_t (*scratch)[4];
     int pred;
+    int csp_warned;
 } LJpegEncContext;
 
 static int ljpeg_encode_bgr(AVCodecContext *avctx, PutBitContext *pb,
@@ -234,6 +235,7 @@ static int ljpeg_encode_frame(AVCodecContext *avctx, 
AVPacket *pkt,
 
     init_put_bits(&pb, pkt->data, pkt->size);
 
+    ff_mjpeg_warn_colorspace(avctx, pict, &s->csp_warned);
     ff_mjpeg_encode_picture_header(avctx, &pb, pict, NULL, NULL,
                                    s->pred, NULL, NULL, 0);
 
diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c
index b9ebe3d769..969daebe35 100644
--- a/libavcodec/mjpegenc.c
+++ b/libavcodec/mjpegenc.c
@@ -94,6 +94,10 @@ static av_cold void init_uni_ac_vlc(const uint8_t 
huff_size_ac[256],
 
 static void mjpeg_encode_picture_header(MPVEncContext *const s)
 {
+    if (s->mjpeg_ctx && s->c.cur_pic.ptr)
+        ff_mjpeg_warn_colorspace(s->c.avctx, s->c.cur_pic.ptr->f,
+                                 &s->mjpeg_ctx->csp_warned);
+
     ff_mjpeg_encode_picture_header(s->c.avctx, &s->pb, s->c.cur_pic.ptr->f, 
s->mjpeg_ctx,
                                    s->c.intra_scantable.permutated, 0,
                                    s->c.intra_matrix, s->c.chroma_intra_matrix,
diff --git a/libavcodec/mjpegenc.h b/libavcodec/mjpegenc.h
index 92feed28b4..4acb91ce20 100644
--- a/libavcodec/mjpegenc.h
+++ b/libavcodec/mjpegenc.h
@@ -75,6 +75,8 @@ typedef struct MJpegContext {
 
     size_t huff_ncode;               ///< Number of current entries in the 
buffer.
     struct MJpegHuffmanCode *huff_buffer; ///< Buffer for Huffman code values.
+
+    int csp_warned;                  ///< YUV-matrix warning already emitted.
 } MJpegContext;
 
 /**
diff --git a/libavcodec/mjpegenc_common.h b/libavcodec/mjpegenc_common.h
index 0cf5a72706..b06e6d910e 100644
--- a/libavcodec/mjpegenc_common.h
+++ b/libavcodec/mjpegenc_common.h
@@ -23,6 +23,8 @@
 
 #include <stdint.h>
 
+#include "libavutil/pixdesc.h"
+
 #include "avcodec.h"
 #include "put_bits.h"
 
@@ -49,4 +51,46 @@ void ff_mjpeg_encode_dc(PutBitContext *pb, int val,
 
 int ff_mjpeg_encode_check_pix_fmt(AVCodecContext *avctx);
 
+/**
+ * Baseline JPEG does not store a YUV matrix, and a decoder reports
+ * bt470bg. Warn once when the frame, or the encoder context if the
+ * frame leaves the matrix unspecified, names a different YUV matrix.
+ * RGB and unspecified input stay quiet. The bitstream is unchanged.
+ */
+static inline void ff_mjpeg_warn_colorspace(AVCodecContext *avctx,
+                                            const AVFrame *frame,
+                                            int *warned)
+{
+    const AVPixFmtDescriptor *desc;
+    const char *name;
+    enum AVColorSpace csp;
+    enum AVPixelFormat fmt;
+
+    if (!avctx || !frame || !warned || *warned)
+        return;
+
+    fmt = frame->format != AV_PIX_FMT_NONE ? frame->format : avctx->pix_fmt;
+    desc = av_pix_fmt_desc_get(fmt);
+    if (desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) &&
+        avctx->sw_pix_fmt != AV_PIX_FMT_NONE) {
+        fmt = avctx->sw_pix_fmt;
+        desc = av_pix_fmt_desc_get(fmt);
+    }
+    if (!desc || (desc->flags & AV_PIX_FMT_FLAG_RGB) || desc->nb_components < 
3)
+        return;
+
+    csp = frame->colorspace != AVCOL_SPC_UNSPECIFIED ?
+          frame->colorspace : avctx->colorspace;
+    if (csp == AVCOL_SPC_UNSPECIFIED || csp == AVCOL_SPC_RGB ||
+        csp == AVCOL_SPC_BT470BG || csp == AVCOL_SPC_SMPTE170M)
+        return;
+
+    *warned = 1;
+    name = av_color_space_name(csp);
+    av_log(avctx, AV_LOG_WARNING,
+           "YUV color matrix %s is not stored in JPEG; "
+           "decoders report bt470bg.\n",
+           name ? name : "unknown");
+}
+
 #endif /* AVCODEC_MJPEGENC_COMMON_H */
diff --git a/libavcodec/qsvenc_jpeg.c b/libavcodec/qsvenc_jpeg.c
index 0e04c2025d..b010f61a36 100644
--- a/libavcodec/qsvenc_jpeg.c
+++ b/libavcodec/qsvenc_jpeg.c
@@ -29,12 +29,14 @@
 
 #include "avcodec.h"
 #include "codec_internal.h"
+#include "mjpegenc_common.h"
 #include "qsv.h"
 #include "qsvenc.h"
 
 typedef struct QSVMJPEGEncContext {
     AVClass *class;
     QSVEncContext qsv;
+    int csp_warned;
 } QSVMJPEGEncContext;
 
 static av_cold int qsv_enc_init(AVCodecContext *avctx)
@@ -49,6 +51,7 @@ static int qsv_enc_frame(AVCodecContext *avctx, AVPacket *pkt,
 {
     QSVMJPEGEncContext *q = avctx->priv_data;
 
+    ff_mjpeg_warn_colorspace(avctx, frame, &q->csp_warned);
     return ff_qsv_encode(avctx, &q->qsv, pkt, frame, got_packet);
 }
 
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 2eef5bf090..b79c2cae8d 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -32,6 +32,7 @@
 #include "codec_internal.h"
 #include "jpegtables.h"
 #include "mjpeg.h"
+#include "mjpegenc_common.h"
 #include "put_bits.h"
 #include "vaapi_encode.h"
 
@@ -78,6 +79,8 @@ typedef struct VAAPIEncodeMJPEGContext {
 
     CodedBitstreamContext *cbc;
     CodedBitstreamFragment current_fragment;
+
+    int csp_warned;
 } VAAPIEncodeMJPEGContext;
 
 static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
@@ -236,6 +239,8 @@ static int 
vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
 
     av_assert0(pic->type == FF_HW_PICTURE_TYPE_IDR);
 
+    ff_mjpeg_warn_colorspace(avctx, pic->input_image, &priv->csp_warned);
+
     desc = av_pix_fmt_desc_get(base_ctx->input_frames->sw_format);
     av_assert0(desc);
     if (desc->flags & AV_PIX_FMT_FLAG_RGB)
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to