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