Commit 63c0d2ccfe for ffmpeg

commit 63c0d2ccfede962d5704fe15deb8b915766983b9
Author: Satvik Upadhyaya <futureiitianisme@gmail.com>
Date:   Thu Oct 1 00:56:24 2026 +0530

    avcodec/mjpegenc: add intra_dc_precision option

    Fixes #23910

diff --git a/doc/encoders.texi b/doc/encoders.texi
index 5ccd72d212..2c173ac916 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -1334,6 +1334,17 @@ Use the default huffman tables. This is the default strategy.
 Compute and use optimal huffman tables.

 @end table
+
+@item intra_dc_precision @var{integer}
+Set the precision of the DC coefficient minus 8. Valid range is 0 to 3.
+Higher values quantize DC more finely as the DC entry of the written
+quantization table is 8 >> @var{intra_dc_precision} so 3 gives a DC
+quantizer of 1. Default is 0 which keeps the previous fixed DC
+quantizer of 8.
+
+Combined with @code{-q:v 1} and an @code{intra_matrix} of all 8s,
+a value of 3 produces a quantization table of all ones.
+
 @end table

 @anchor{wavpackenc}
diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c
index 8dc3217acf..4305f11774 100644
--- a/libavcodec/mjpegenc.c
+++ b/libavcodec/mjpegenc.c
@@ -279,7 +279,7 @@ int ff_mjpeg_encode_stuffing(MPVEncContext *const s)

 fail:
     for (int i = 0; i < 3; i++)
-        s->last_dc[i] = 128;
+        s->last_dc[i] = 128 << s->c.intra_dc_precision;

     return ret;
 }
@@ -644,12 +644,13 @@ 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 5
+#define AMV_OPTIONS_OFFSET 6
 { "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 },
+{ "intra_dc_precision", "Precision of the DC coefficient - 8", FF_MPV_OFFSET(c.intra_dc_precision), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 3, VE },
 FF_MPV_COMMON_OPTS
 { NULL},
 };
diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c
index 3908628504..08d7aa59b7 100644
--- a/libavcodec/mpegvideo_enc.c
+++ b/libavcodec/mpegvideo_enc.c
@@ -858,6 +858,10 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx)
         m->intra_only = 1; /* force intra only for jpeg */
         avctx->delay = 0;
         s->c.low_delay = 1;
+        if (avctx->codec_id == AV_CODEC_ID_MJPEG) {
+            s->c.y_dc_scale_table =
+            s->c.c_dc_scale_table = ff_mpeg12_dc_scale_table[s->c.intra_dc_precision];
+        }
         break;
 #endif
     case AV_CODEC_ID_SPEEDHQ:
@@ -3863,9 +3867,7 @@ static int encode_picture(MPVMainEncContext *const m, const AVPacket *pkt)
                 s->c.chroma_intra_matrix[j] = av_clip_uint8((chroma_matrix[i] * s->c.qscale) >> 3);
                 s->c.       intra_matrix[j] = av_clip_uint8((  luma_matrix[i] * s->c.qscale) >> 3);
             }
-            s->c.y_dc_scale_table =
-            s->c.c_dc_scale_table = ff_mpeg12_dc_scale_table[0];
-            s->c.chroma_intra_matrix[0] = s->c.intra_matrix[0] = 8;
+            s->c.chroma_intra_matrix[0] = s->c.intra_matrix[0] = 8 >> s->c.intra_dc_precision;
         } else {
             static const uint8_t y[32] = {13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13};
             static const uint8_t c[32] = {14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14};
diff --git a/tests/fate/vcodec.mak b/tests/fate/vcodec.mak
index 68270766e1..a5abace91f 100644
--- a/tests/fate/vcodec.mak
+++ b/tests/fate/vcodec.mak
@@ -245,13 +245,14 @@ fate-vsynth3-magicyuv:                ENCOPTS = -pix_fmt yuv444p -pred median \
 fate-vsynth_lena-magicyuv:            ENCOPTS = -slices 3 -pix_fmt gray -pred left
 fate-vsynth%-magicyuv:                DECOPTS = -sws_flags neighbor+bitexact

-FATE_VCODEC_SCALE-$(call ENCDEC, MJPEG, AVI) += mjpeg mjpeg-422 mjpeg-444 mjpeg-trell mjpeg-huffman mjpeg-trell-huffman
+FATE_VCODEC_SCALE-$(call ENCDEC, MJPEG, AVI) += mjpeg mjpeg-422 mjpeg-444 mjpeg-trell mjpeg-huffman mjpeg-trell-huffman mjpeg-dcprec
 fate-vsynth%-mjpeg:                   ENCOPTS = -qscale 9 -pix_fmt yuvj420p -huffman default -threads 5 -thread_type slice
 fate-vsynth%-mjpeg-422:               ENCOPTS = -qscale 9 -pix_fmt yuvj422p
 fate-vsynth%-mjpeg-444:               ENCOPTS = -qscale 9 -pix_fmt yuvj444p
 fate-vsynth%-mjpeg-trell:             ENCOPTS = -qscale 9 -pix_fmt yuvj420p -trellis 1 -huffman default
 fate-vsynth%-mjpeg-huffman:           ENCOPTS = -qscale 9 -pix_fmt yuvj420p -huffman optimal
 fate-vsynth%-mjpeg-trell-huffman:     ENCOPTS = -qscale 9 -pix_fmt yuvj420p -trellis 1 -huffman optimal
+fate-vsynth%-mjpeg-dcprec:            ENCOPTS = -qscale 9 -pix_fmt yuvj420p -intra_dc_precision 3 -huffman default -threads 5 -thread_type slice

 FATE_VCODEC-$(call ENCDEC, MPEG1VIDEO, MPEG1VIDEO MPEGVIDEO) += mpeg1 mpeg1b
 fate-vsynth%-mpeg1:              FMT     = mpeg1video
diff --git a/tests/ref/vsynth/vsynth1-mjpeg-dcprec b/tests/ref/vsynth/vsynth1-mjpeg-dcprec
new file mode 100644
index 0000000000..6abbb8f563
--- /dev/null
+++ b/tests/ref/vsynth/vsynth1-mjpeg-dcprec
@@ -0,0 +1,4 @@
+72e1112ae1d98a3b906a53b7fcf71bee *tests/data/fate/vsynth1-mjpeg-dcprec.avi
+1599208 tests/data/fate/vsynth1-mjpeg-dcprec.avi
+17e84ca2a6876daee3d086e960fe2ea8 *tests/data/fate/vsynth1-mjpeg-dcprec.out.rawvideo
+stddev:    7.87 PSNR: 30.21 MAXDIFF:   62 bytes:  7603200/  7603200
diff --git a/tests/ref/vsynth/vsynth2-mjpeg-dcprec b/tests/ref/vsynth/vsynth2-mjpeg-dcprec
new file mode 100644
index 0000000000..2175fa451c
--- /dev/null
+++ b/tests/ref/vsynth/vsynth2-mjpeg-dcprec
@@ -0,0 +1,4 @@
+ba9b7af5c2946269e4ccf5a6ca22d2b9 *tests/data/fate/vsynth2-mjpeg-dcprec.avi
+910622 tests/data/fate/vsynth2-mjpeg-dcprec.avi
+e05824ca581ae95bdd544ba0d5bc97dc *tests/data/fate/vsynth2-mjpeg-dcprec.out.rawvideo
+stddev:    4.86 PSNR: 34.38 MAXDIFF:   55 bytes:  7603200/  7603200
diff --git a/tests/ref/vsynth/vsynth3-mjpeg-dcprec b/tests/ref/vsynth/vsynth3-mjpeg-dcprec
new file mode 100644
index 0000000000..8deced67e2
--- /dev/null
+++ b/tests/ref/vsynth/vsynth3-mjpeg-dcprec
@@ -0,0 +1,4 @@
+72e395ca9005268c746de2d2c7ac4b66 *tests/data/fate/vsynth3-mjpeg-dcprec.avi
+67142 tests/data/fate/vsynth3-mjpeg-dcprec.avi
+b6c7b383aa0686e1e3d81ab1b875ecd8 *tests/data/fate/vsynth3-mjpeg-dcprec.out.rawvideo
+stddev:    8.60 PSNR: 29.43 MAXDIFF:   58 bytes:    86700/    86700
diff --git a/tests/ref/vsynth/vsynth_lena-mjpeg-dcprec b/tests/ref/vsynth/vsynth_lena-mjpeg-dcprec
new file mode 100644
index 0000000000..6f6c490cab
--- /dev/null
+++ b/tests/ref/vsynth/vsynth_lena-mjpeg-dcprec
@@ -0,0 +1,4 @@
+d90a396e4a5fb576adf45ea3ce763af6 *tests/data/fate/vsynth_lena-mjpeg-dcprec.avi
+751986 tests/data/fate/vsynth_lena-mjpeg-dcprec.avi
+1562a1e5f00e923e08fe7e3acf3a44c7 *tests/data/fate/vsynth_lena-mjpeg-dcprec.out.rawvideo
+stddev:    4.32 PSNR: 35.42 MAXDIFF:   49 bytes:  7603200/  7603200