Commit 20e8e3df1b for aom

commit 20e8e3df1b70c6c373f37770089145ac6045b471
Author: Cheng Chen <chengchen@google.com>
Date:   Mon Apr 27 11:45:51 2026 -0700

    Fix NaN and divided by zero issues in film grain

    Also clamp the error to avoid error drifting.

    BUG=aomedia:503810640

    Change-Id: Idaf1eeb2f8ce435c7feb17c5c86a4fdca15c2b69

diff --git a/aom_dsp/noise_model.c b/aom_dsp/noise_model.c
index aa8e6218db..562252b3bb 100644
--- a/aom_dsp/noise_model.c
+++ b/aom_dsp/noise_model.c
@@ -144,6 +144,11 @@ static int equation_system_solve(aom_equation_system_t *eqns) {
   if (ret == 0) {
     return 0;
   }
+  for (int i = 0; i < n; ++i) {
+    if (isnan((float)eqns->x[i])) {
+      return 0;
+    }
+  }
   return 1;
 }

@@ -482,7 +487,8 @@ int aom_flat_block_finder_init(aom_flat_block_finder_t *block_finder,
   for (i = 0; i < kLowPolyNumParams; ++i) {
     memset(eqns.b, 0, sizeof(*eqns.b) * kLowPolyNumParams);
     eqns.b[i] = 1;
-    equation_system_solve(&eqns);
+    const int ret = equation_system_solve(&eqns);
+    if (!ret) return ret;

     for (j = 0; j < kLowPolyNumParams; ++j) {
       AtA_inv[j * kLowPolyNumParams + i] = eqns.x[j];
@@ -1259,7 +1265,10 @@ int aom_noise_model_get_grain_parameters(aom_noise_model_t *const noise_model,
     if (c == 0) {
       avg_luma_strength = average_strength;
     } else {
-      y_corr[c - 1] = avg_luma_strength * eqns->x[n_coeff] / average_strength;
+      y_corr[c - 1] =
+          average_strength > 1e-6
+              ? avg_luma_strength * eqns->x[n_coeff] / average_strength
+              : 0;
       max_coeff = AOMMAX(max_coeff, y_corr[c - 1]);
       min_coeff = AOMMIN(min_coeff, y_corr[c - 1]);
     }
@@ -1336,8 +1345,11 @@ static float *get_half_cos_window(int block_size) {
         INT_TYPE new_val = (INT_TYPE)AOMMIN(                                \
             AOMMAX(result[result_idx] * block_normalization + 0.5f, 0),     \
             block_normalization);                                           \
-        const float err =                                                   \
+        float err =                                                         \
             -(((float)new_val) / block_normalization - result[result_idx]); \
+        if (fabsf(err) < 1e-6f) {                                           \
+          err = 0.0f;                                                       \
+        }                                                                   \
         denoised[y * stride + x] = new_val;                                 \
         if (x + 1 < (w >> chroma_sub_w)) {                                  \
           result[result_idx + 1] += err * 7.0f / 16.0f;                     \
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index 858c3a444d..19bedc657a 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -49,12 +49,28 @@ static void *Memset16(void *dest, int val, size_t length) {
 }

 static void FillImage(aom_image_t *img, uint8_t val) {
+  for (int p = 0; p < 3; ++p) {
+    uint8_t *buf = img->planes[p];
+    const int w = aom_img_plane_width(img, p);
+    const int h = aom_img_plane_height(img, p);
+    for (int r = 0; r < h; ++r) {
+      memset(buf, val, w);
+      buf += img->stride[p];
+    }
+  }
+}
+
+static void FillImageRandom(aom_image_t *img) {
+  ::libaom_test::ACMRandom rnd;
+  rnd.Reset(::libaom_test::ACMRandom::DeterministicSeed());
   for (int p = 0; p < 3; ++p) {
     uint8_t *buf = img->planes[p];
     const int h = p == 0 ? (int)img->d_h : (int)((img->d_h + 1) / 2);
     const int w = p == 0 ? (int)img->d_w : (int)((img->d_w + 1) / 2);
     for (int r = 0; r < h; ++r) {
-      memset(buf, val, w);
+      for (int c = 0; c < w; ++c) {
+        buf[c] = rnd.Rand8();
+      }
       buf += img->stride[p];
     }
   }
@@ -2554,4 +2570,62 @@ TEST(EncodeAPI, SvcSourceLastTL0Uninitialized) {
   aom_img_free(img);
   ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
 }
+
+TEST(EncodeAPI, Buganizer503810640) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 176;
+  cfg.g_h = 144;
+
+  aom_codec_ctx_t codec;
+  ASSERT_EQ(aom_codec_enc_init(&codec, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_DENOISE_NOISE_LEVEL, 29),
+            AOM_CODEC_OK);
+
+  aom_image_t *raw = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 176, 144, 1);
+  ASSERT_NE(raw, nullptr);
+
+  FillImage(raw, 128);
+  ::libaom_test::ACMRandom rnd;
+  rnd.Reset(::libaom_test::ACMRandom::DeterministicSeed());
+  for (int y = 0; y < 144; ++y) {
+    for (int x = 0; x < 176; ++x) {
+      raw->planes[AOM_PLANE_Y][y * raw->stride[AOM_PLANE_Y] + x] = rnd.Rand8();
+    }
+  }
+
+  ASSERT_EQ(aom_codec_encode(&codec, raw, 0, 1, 0), AOM_CODEC_OK);
+
+  aom_img_free(raw);
+  ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
+}
+
+TEST(EncodeAPI, Buganizer503810640V2) {
+  aom_codec_iface_t *iface = aom_codec_av1_cx();
+  aom_codec_enc_cfg_t cfg;
+  ASSERT_EQ(aom_codec_enc_config_default(iface, &cfg, AOM_USAGE_REALTIME),
+            AOM_CODEC_OK);
+
+  cfg.g_w = 437;
+  cfg.g_h = 1121;
+
+  aom_codec_ctx_t codec;
+  ASSERT_EQ(aom_codec_enc_init(&codec, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_DENOISE_NOISE_LEVEL, 29),
+            AOM_CODEC_OK);
+
+  aom_image_t *raw = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 437, 1121, 1);
+  ASSERT_NE(raw, nullptr);
+
+  FillImageRandom(raw);
+
+  ASSERT_EQ(aom_codec_encode(&codec, raw, 0, 1, 0), AOM_CODEC_OK);
+
+  aom_img_free(raw);
+  ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
+}
+
 }  // namespace