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