Commit 888312c00d for aom

commit 888312c00dbf80aecd962545dbd1390788be0d34
Author: Marco Paniconi <marpan@google.com>
Date:   Wed Apr 29 14:23:25 2026 -0700

    rtc: Fix usage and allocation for source_last_TL0

    Add checks to prevent source_last_TL0 from being
    used unallocated. In the issue below is was being
    passed unallocated to the scaler, which caused
    divide-by-zero in the resizer.

    Fix the logic to allocate source_last_TL0 on the
    very first frame, regardless of temporal_layer_id,
    and add additional checks for allocation..

    Unittest to reproduce.

    Bug: 505976409
    Change-Id: I6dbba45d8add65042a53981c60319d8dc4cd8a6f

diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 2d492dd74d..0337baf7a1 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -2975,10 +2975,14 @@ static int encode_without_recode(AV1_COMP *cpi) {
   av1_set_size_dependent_vars(cpi, &q, &bottom_index, &top_index);
   av1_set_mv_search_params(cpi);

-  if (cm->current_frame.frame_number == 0 &&
-      (cpi->ppi->use_svc || cpi->oxcf.rc_cfg.drop_frames_water_mark > 0) &&
-      cpi->svc.temporal_layer_id == 0) {
-    const SequenceHeader *seq_params = cm->seq_params;
+  const SequenceHeader *seq_params = cm->seq_params;
+  if ((cpi->svc.source_last_TL0.buffer_alloc_sz == 0 ||
+       cpi->svc.source_last_TL0.y_width != cpi->oxcf.frm_dim_cfg.width ||
+       cpi->svc.source_last_TL0.y_height != cpi->oxcf.frm_dim_cfg.height ||
+       cpi->svc.source_last_TL0.subsampling_x != seq_params->subsampling_x ||
+       cpi->svc.source_last_TL0.subsampling_y != seq_params->subsampling_y ||
+       cpi->svc.source_last_TL0.flags != cpi->source->flags) &&
+      (cpi->ppi->use_svc || cpi->oxcf.rc_cfg.drop_frames_water_mark > 0)) {
     if (aom_alloc_frame_buffer(
             &cpi->svc.source_last_TL0, cpi->oxcf.frm_dim_cfg.width,
             cpi->oxcf.frm_dim_cfg.height, seq_params->subsampling_x,
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c
index 5d8de75642..8b8d4c51e9 100644
--- a/av1/encoder/svc_layercontext.c
+++ b/av1/encoder/svc_layercontext.c
@@ -653,6 +653,7 @@ void av1_svc_check_reset_layer_rc_flag(AV1_COMP *const cpi) {
 void av1_svc_set_last_source(AV1_COMP *const cpi, EncodeFrameInput *frame_input,
                              YV12_BUFFER_CONFIG *prev_source) {
   frame_input->last_source = prev_source != NULL ? prev_source : NULL;
+  if (cpi->svc.source_last_TL0.buffer_alloc_sz == 0) return;
   if (!cpi->ppi->use_svc && cpi->rc.prev_frame_is_dropped &&
       cpi->rc.frame_number_encoded > 0) {
     frame_input->last_source = &cpi->svc.source_last_TL0;
diff --git a/test/encode_api_test.cc b/test/encode_api_test.cc
index ba482bcfbc..858c3a444d 100644
--- a/test/encode_api_test.cc
+++ b/test/encode_api_test.cc
@@ -2490,4 +2490,68 @@ TEST(EncodeAPI, Buganizer504317456) {
 }
 #endif  // !CONFIG_REALTIME_ONLY

+// Test for issue: 505976409.
+TEST(EncodeAPI, SvcSourceLastTL0Uninitialized) {
+  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 = 64;
+  cfg.g_h = 64;
+  cfg.rc_end_usage = AOM_CBR;
+  cfg.g_lag_in_frames = 0;
+  cfg.rc_target_bitrate = 400;
+
+  aom_codec_ctx_t codec;
+  ASSERT_EQ(aom_codec_enc_init(&codec, iface, &cfg, 0), AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_control(&codec, AOME_SET_CPUUSED, 9), AOM_CODEC_OK);
+
+  aom_svc_params_t svc = {};
+  svc.number_spatial_layers = 1;
+  svc.number_temporal_layers = 2;
+  svc.scaling_factor_num[0] = 1;
+  svc.scaling_factor_den[0] = 4;
+  for (int i = 0; i < 32; ++i) svc.layer_target_bitrate[i] = 200;
+  for (int i = 0; i < 8; ++i) svc.framerate_factor[i] = 1;
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_PARAMS, &svc), AOM_CODEC_OK);
+
+  aom_image_t *img = aom_img_alloc(nullptr, AOM_IMG_FMT_I420, 64, 64, 1);
+  ASSERT_NE(img, nullptr);
+  FillImage(img, 128);
+
+  // Frame 0: TL1 (Starts with non-zero temporal layer).
+  aom_svc_layer_id_t layer_id = { 0, 1 };
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id),
+            AOM_CODEC_OK);
+
+  aom_svc_ref_frame_config_t ref_cfg = {};
+  for (int r = 0; r < 7; ++r) {
+    ref_cfg.reference[r] = 1;
+    ref_cfg.ref_idx[r] = 7;
+  }
+  ref_cfg.refresh[2] = 1;
+  ref_cfg.refresh[6] = 1;
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_REF_FRAME_CONFIG, &ref_cfg),
+            AOM_CODEC_OK);
+
+  ASSERT_EQ(aom_codec_encode(&codec, img, 0, 1, AOM_EFLAG_FORCE_KF),
+            AOM_CODEC_OK);
+
+  // Frame 1: TL0.
+  layer_id.temporal_layer_id = 0;
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id),
+            AOM_CODEC_OK);
+  ASSERT_EQ(aom_codec_encode(&codec, img, 1, 1, 0), AOM_CODEC_OK);
+
+  // Frame 2: TL1 (Triggers use of source_last_TL0).
+  layer_id.temporal_layer_id = 1;
+  ASSERT_EQ(aom_codec_control(&codec, AV1E_SET_SVC_LAYER_ID, &layer_id),
+            AOM_CODEC_OK);
+  // This frame used to crash or hang before the fix.
+  ASSERT_EQ(aom_codec_encode(&codec, img, 2, 1, 0), AOM_CODEC_OK);
+
+  aom_img_free(img);
+  ASSERT_EQ(aom_codec_destroy(&codec), AOM_CODEC_OK);
+}
 }  // namespace