Commit b69f65affa for ffmpeg

commit b69f65affa5148e5bfb6c551f0bb64f202d517bc
Author: Lynne <dev@lynne.ee>
Date:   Wed Sep 23 11:32:52 2026 +0900

    ffv1enc: add a minimal context model

    Add a third context model, selected with -context 2, using only the two
    nearest gradients coarsely quantized, for 13 contexts per plane.

    Every slice starts from fresh probability states, and with many slices
    there is too little data per slice for the larger context sets to adapt.
    On a 6464x4852 16-bit RGB film scan coded with 1024 slices, this codes
    87.99 MB per frame, against 90.29 MB with -context 0 and 100.79 MB with
    -context 1, while needing 832 bytes of probability state per slice
    instead of 23 KB and 324 KB. The same holds on 10-bit and 8-bit content
    at high slice counts; with few slices, -context 0 remains best.

    The table is stored in the extradata like the existing ones, so decoders
    need no changes.

diff --git a/doc/encoders.texi b/doc/encoders.texi
index 3a07b9d3d7..b36d7dee21 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -1401,7 +1401,9 @@ The following options are supported by FFmpeg's FFv1 encoder.

 @table @option
 @item context
-Sets the context size, 0 (default) is small, 1 is big.
+Sets the context size, 0 (default) is small, 1 is big, 2 is minimal.
+The minimal context set compresses better when coding many slices, as each
+slice has too little data for larger context sets to adapt to.

 @item coder
 Set the coder,
diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c
index e6b7506557..1d18f10f05 100644
--- a/libavcodec/ffv1enc.c
+++ b/libavcodec/ffv1enc.c
@@ -691,6 +691,21 @@ av_cold int ff_ffv1_encode_init(AVCodecContext *avctx)
         }
     }

+    /* Small context set: only the two nearest gradients, coarsely quantized.
+     * With many slices there is too little data per slice for large context
+     * sets to adapt, and this codes smaller while using a fraction of the
+     * state. */
+    if (s->context_model == 2) {
+        const int8_t *q5 = (s->qtable == -1 && s->bits_per_raw_sample <= 8) || s->qtable == 1 ?
+                           quant5 : quant5_10bit;
+        s->quant_table_count = 3;
+        for (i = 0; i < 256; i++) {
+            s->quant_tables[2][0][i] =   q5[i];
+            s->quant_tables[2][1][i] = 5*q5[i];
+        }
+        s->context_count[2] = (5 * 5 + 1) / 2;
+    }
+
     if ((ret = ff_ffv1_allocate_initial_states(s)) < 0)
         return ret;

@@ -2059,7 +2074,7 @@ static const AVOption options[] = {
         { "ac", "Range with custom table (the ac option exists for compatibility and is deprecated)", 0, AV_OPT_TYPE_CONST,
             { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" },
     { "context", "Context model", OFFSET(context_model), AV_OPT_TYPE_INT,
-            { .i64 = 0 }, 0, 1, VE },
+            { .i64 = 0 }, 0, 2, VE },
     { "qtable", "Quantization table", OFFSET(qtable), AV_OPT_TYPE_INT,
             { .i64 = -1 }, -1, 2, VE , .unit = "qtable"},
         { "default", NULL, 0, AV_OPT_TYPE_CONST,
diff --git a/libavcodec/ffv1enc_vulkan.c b/libavcodec/ffv1enc_vulkan.c
index e00d4313f6..c7b090c5b2 100644
--- a/libavcodec/ffv1enc_vulkan.c
+++ b/libavcodec/ffv1enc_vulkan.c
@@ -1471,7 +1471,7 @@ static const AVOption vulkan_encode_ffv1_options[] = {
     { "slicecrc", "Protect slices with CRCs", OFFSET(ctx.ec), AV_OPT_TYPE_INT,
             { .i64 = -1 }, -1, 2, VE },
     { "context", "Context model", OFFSET(ctx.context_model), AV_OPT_TYPE_INT,
-            { .i64 = 0 }, 0, 1, VE },
+            { .i64 = 0 }, 0, 2, VE },
     { "coder", "Coder type", OFFSET(ctx.ac), AV_OPT_TYPE_INT,
             { .i64 = AC_RANGE_CUSTOM_TAB }, -2, 2, VE, .unit = "coder" },
         { "rice", "Golomb rice", 0, AV_OPT_TYPE_CONST,