Commit b089902f6d for ffmpeg

commit b089902f6d109b1dacd18ccea82610031a68cb92
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Sun Nov 8 16:43:38 2026 +0100

    avcodec/ffv1enc: accumulate the RCT coefficient statistics in 64bit

    The per slice sum of residual magnitudes exceeds INT_MAX for noisy
    32bit float slices.

    Accumulating directly in 64bit costs 5-14% encode time on AArch64, so
    the hot loops keep adding to 32bit partial sums, which are flushed into
    the 64bit totals every 4096 pixels and at the end of each row. Each
    contribution is at most 98303, so a partial sum stays below INT_MAX.

    The ffv1-float32-slices and ffv1-golomb-float32-26bit references were
    generated with overflowing sums and change with the corrected
    coefficient choice.

    Fixes: signed integer overflow: 2147482002 + 34432 cannot be represented in type 'int'
    Assisted-by: Fairy

diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c
index 869834c2e8..e03154b415 100644
--- a/libavcodec/ffv1enc.c
+++ b/libavcodec/ffv1enc.c
@@ -1161,10 +1161,22 @@ static void encode_slice_header(FFV1Context *f, FFV1SliceContext *sc)
     }
 }

+#define NB_Y_COEFF 15
+
+/* Each contribution to the partial sums is at most 98303,
+ * so up to 4096 of them fit in an int. */
+static av_always_inline void accumulate_rct_stats(int64_t stat[NB_Y_COEFF],
+                                                 int partial[NB_Y_COEFF])
+{
+    for (int i = 0; i < NB_Y_COEFF; i++) {
+        stat[i] += partial[i];
+        partial[i] = 0;
+    }
+}
+
 static void choose_rct_params(const FFV1Context *f, FFV1SliceContext *sc,
                               const uint8_t *src[3], const int stride[3], int w, int h)
 {
-#define NB_Y_COEFF 15
     static const int rct_y_coeff[15][2] = {
         {0, 0}, //      4G
         {1, 1}, //  R + 2G + B
@@ -1184,7 +1196,8 @@ static void choose_rct_params(const FFV1Context *f, FFV1SliceContext *sc,
         {1, 0}, //  R + 3G
     };

-    int stat[NB_Y_COEFF] = {0};
+    int64_t stat[NB_Y_COEFF] = {0};
+    int partial[NB_Y_COEFF] = {0};
     int x, y, i, p, best;
     int16_t *sample[3];
     int lbd = f->bits_per_raw_sample <= 8;
@@ -1232,7 +1245,7 @@ static void choose_rct_params(const FFV1Context *f, FFV1SliceContext *sc,
                 bb -= bg;

                 for (i = 0; i<NB_Y_COEFF; i++) {
-                    stat[i] += FFABS(bg + ((br*rct_y_coeff[i][0] + bb*rct_y_coeff[i][1])>>2));
+                    partial[i] += FFABS(bg + ((br*rct_y_coeff[i][0] + bb*rct_y_coeff[i][1])>>2));
                 }

             }
@@ -1243,7 +1256,10 @@ static void choose_rct_params(const FFV1Context *f, FFV1SliceContext *sc,
             lastr = r;
             lastg = g;
             lastb = b;
+            if ((x & 4095) == 4095)
+                accumulate_rct_stats(stat, partial);
         }
+        accumulate_rct_stats(stat, partial);
     }

     best = 0;
@@ -1264,7 +1280,8 @@ static void choose_rct_params_bayer(const FFV1Context *f, FFV1SliceContext *sc,
         { 0, 0 }, { 1, 1 }, { 2, 2 }, { 0, 2 }, { 2, 0 }, { 4, 0 }, { 0, 4 }, { 0, 3 },
         { 3, 0 }, { 3, 1 }, { 1, 3 }, { 1, 2 }, { 2, 1 }, { 0, 1 }, { 1, 0 },
     };
-    int stat[NB_Y_COEFF] = {0};
+    int64_t stat[NB_Y_COEFF] = {0};
+    int partial[NB_Y_COEFF] = {0};
     int16_t *sample[3];
     int i, best;

@@ -1298,7 +1315,7 @@ static void choose_rct_params_bayer(const FFV1Context *f, FFV1SliceContext *sc,
                 bb -= bgm;

                 for (i = 0; i < NB_Y_COEFF; i++)
-                    stat[i] += FFABS(bgm + ((br*rct_y_coeff[i][0] + bb*rct_y_coeff[i][1]) >> 2));
+                    partial[i] += FFABS(bgm + ((br*rct_y_coeff[i][0] + bb*rct_y_coeff[i][1]) >> 2));
             }
             sample[0][x] = agm;
             sample[1][x] = ab;
@@ -1307,7 +1324,10 @@ static void choose_rct_params_bayer(const FFV1Context *f, FFV1SliceContext *sc,
             last_gm = gm;
             last_b  = b;
             last_r  = r;
+            if ((x & 4095) == 4095)
+                accumulate_rct_stats(stat, partial);
         }
+        accumulate_rct_stats(stat, partial);
     }

     best = 0;
diff --git a/tests/ref/fate/ffv1-float32-slices b/tests/ref/fate/ffv1-float32-slices
index 084e88a7f7..d4d60e10c0 100644
--- a/tests/ref/fate/ffv1-float32-slices
+++ b/tests/ref/fate/ffv1-float32-slices
@@ -1,5 +1,5 @@
-33045e83befdf2085c63bfaea7c60006 *tests/data/fate/ffv1-float32-slices.nut
-9585 tests/data/fate/ffv1-float32-slices.nut
+60daee80a14cbe3678a3082310d4b3d1 *tests/data/fate/ffv1-float32-slices.nut
+9507 tests/data/fate/ffv1-float32-slices.nut
 #tb 0: 1/1
 #media_type 0: video
 #codec_id 0: rawvideo
diff --git a/tests/ref/fate/ffv1-golomb-float32-26bit b/tests/ref/fate/ffv1-golomb-float32-26bit
index 3fe457cdcb..dafb11e4ed 100644
--- a/tests/ref/fate/ffv1-golomb-float32-26bit
+++ b/tests/ref/fate/ffv1-golomb-float32-26bit
@@ -1,5 +1,5 @@
-451a0326abe6e7a074184d3410eef4ce *tests/data/fate/ffv1-golomb-float32-26bit.nut
-163524 tests/data/fate/ffv1-golomb-float32-26bit.nut
+823f1949055b53c21f21cb5413aac9cb *tests/data/fate/ffv1-golomb-float32-26bit.nut
+169112 tests/data/fate/ffv1-golomb-float32-26bit.nut
 #tb 0: 1/1
 #media_type 0: video
 #codec_id 0: rawvideo