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]
