From f079a706eda06d0ee9501a390dc47db321a599c9 Mon Sep 17 00:00:00 2001 From: Lynne Date: Wed, 23 Sep 2026 11:32:52 +0900 Subject: [PATCH] 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. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01KuYRzL6QFTyjMZHpBB1VLP --- doc/encoders.texi | 4 +++- libavcodec/ffv1enc.c | 17 ++++++++++++++++- libavcodec/ffv1enc_vulkan.c | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/doc/encoders.texi b/doc/encoders.texi index a3ce15fcff..0a5fc9e4d6 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -1395,7 +1395,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 14f7d2e5dc..826f0ee4c0 100644 --- a/libavcodec/ffv1enc_vulkan.c +++ b/libavcodec/ffv1enc_vulkan.c @@ -1632,7 +1632,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, -- 2.55.0