Commit 40b50c6f55 for aom

commit 40b50c6f556df8df4e9876ffcdf842fbfd0d25b4
Author: Marco Paniconi <marpan@google.com>
Date:   Wed Apr 15 21:02:04 2026 -0700

    rtc-rc: Checks limts on inputs to external rc

    Add checks to inputs and add rc_is_valid_ flag
    to disallow invalid inputs/updates to the
    external rc.

    Added unittest to check some invalid inputs.

    Bug: 501657371

    Change-Id: Ib5fd66b121a347be97aaf434fe42d3cce0bd744e

diff --git a/av1/ratectrl_rtc.cc b/av1/ratectrl_rtc.cc
index 363521eabc..238484f4db 100644
--- a/av1/ratectrl_rtc.cc
+++ b/av1/ratectrl_rtc.cc
@@ -122,6 +122,7 @@ std::unique_ptr<AV1RateControlRTC> AV1RateControlRTC::Create(
         cpi->common.mi_params.mi_rows, cpi->common.mi_params.mi_cols);
     if (!cpi->cyclic_refresh) return nullptr;
   }
+  rc_api->rc_is_valid_ = true;
   return rc_api;
 }

@@ -193,6 +194,7 @@ bool AV1RateControlRTC::InitRateControl(const AV1RateControlRtcConfig &rc_cfg) {
   // Enable external rate control.
   cpi_->rc.rtc_external_ratectrl = 1;
   cpi_->sf.rt_sf.use_nonrd_pick_mode = 1;
+  rc_is_valid_ = true;
   return true;
 }

@@ -202,6 +204,7 @@ bool AV1RateControlRTC::UpdateRateControl(
       rc_cfg.ss_number_layers > AOM_MAX_SS_LAYERS ||
       rc_cfg.ts_number_layers < 1 ||
       rc_cfg.ts_number_layers > AOM_MAX_TS_LAYERS) {
+    rc_is_valid_ = false;
     return false;
   }
   if (cpi_->svc.number_spatial_layers != rc_cfg.ss_number_layers ||
@@ -209,11 +212,18 @@ bool AV1RateControlRTC::UpdateRateControl(
     av1_free_svc_cyclic_refresh(cpi_);
   const int num_layers = rc_cfg.ss_number_layers * rc_cfg.ts_number_layers;
   if (num_layers > 1 && !av1_alloc_layer_context(cpi_, num_layers)) {
+    rc_is_valid_ = false;
     return false;
   }
   AV1_COMMON *cm = &cpi_->common;
   AV1EncoderConfig *oxcf = &cpi_->oxcf;
   RATE_CONTROL *const rc = &cpi_->rc;
+  if (oxcf->frm_dim_cfg.width && oxcf->frm_dim_cfg.height &&
+      (rc_cfg.width > oxcf->frm_dim_cfg.width ||
+       rc_cfg.height > oxcf->frm_dim_cfg.height)) {
+    rc_is_valid_ = false;
+    return false;
+  }
   initial_width_ = rc_cfg.width;
   initial_height_ = rc_cfg.height;
   cm->width = rc_cfg.width;
@@ -260,14 +270,26 @@ bool AV1RateControlRTC::UpdateRateControl(
         LAYER_CONTEXT *lc = &cpi_->svc.layer_context[layer];
         RATE_CONTROL *const lrc = &lc->rc;
         lc->layer_target_bitrate = 1000 * rc_cfg.layer_target_bitrate[layer];
+        if (rc_cfg.max_quantizers[layer] > 63 ||
+            rc_cfg.min_quantizers[layer] < 0 ||
+            rc_cfg.min_quantizers[layer] > rc_cfg.max_quantizers[layer]) {
+          rc_is_valid_ = false;
+          return false;
+        }
         lc->max_q = rc_cfg.max_quantizers[layer];
         lc->min_q = rc_cfg.min_quantizers[layer];
         lrc->worst_quality =
             av1_quantizer_to_qindex(rc_cfg.max_quantizers[layer]);
         lrc->best_quality =
             av1_quantizer_to_qindex(rc_cfg.min_quantizers[layer]);
-        lc->scaling_factor_num = rc_cfg.scaling_factor_num[sl];
-        lc->scaling_factor_den = rc_cfg.scaling_factor_den[sl];
+        // spatial scaling (scaling_factor_num[]/den[]) is always to a lower
+        // resolution, so den must be >= num.
+        if (rc_cfg.scaling_factor_den[sl] < rc_cfg.scaling_factor_num[sl]) {
+          rc_is_valid_ = false;
+          return false;
+        }
+        lc->scaling_factor_num = AOMMAX(1, rc_cfg.scaling_factor_num[sl]);
+        lc->scaling_factor_den = AOMMAX(1, rc_cfg.scaling_factor_den[sl]);
         lc->framerate_factor = rc_cfg.ts_rate_decimator[tl];
         if (tl == cpi_->svc.number_temporal_layers - 1)
           target_bandwidth_svc += lc->layer_target_bitrate;
@@ -284,11 +306,20 @@ bool AV1RateControlRTC::UpdateRateControl(
     DISABLE_SETJMP(cpi_);
   }
   check_reset_rc_flag(cpi_);
+  rc_is_valid_ = true;
   return true;
 }

 FrameDropDecision AV1RateControlRTC::ComputeQP(
     const AV1FrameParamsRTC &frame_params) {
+  if (!rc_is_valid_) return kFrameDropDecisionDrop;
+  if (frame_params.spatial_layer_id < 0 ||
+      frame_params.spatial_layer_id >= cpi_->svc.number_spatial_layers ||
+      frame_params.temporal_layer_id < 0 ||
+      frame_params.temporal_layer_id >= cpi_->svc.number_temporal_layers ||
+      initial_width_ == 0 || initial_height_ == 0) {
+    return kFrameDropDecisionDrop;
+  }
   AV1_COMMON *const cm = &cpi_->common;
   int width, height;
   GF_GROUP *const gf_group = &cpi_->ppi->gf_group;
@@ -380,31 +411,47 @@ FrameDropDecision AV1RateControlRTC::ComputeQP(
 }

 int AV1RateControlRTC::GetQP() const {
-  return cpi_->common.quant_params.base_qindex;
+  if (rc_is_valid_) return cpi_->common.quant_params.base_qindex;
+  return 0;
 }

 AV1LoopfilterLevel AV1RateControlRTC::GetLoopfilterLevel() const {
-  av1_pick_filter_level(nullptr, cpi_, LPF_PICK_FROM_Q);
   AV1LoopfilterLevel lpf_level;
-  lpf_level.filter_level[0] = cpi_->common.lf.filter_level[0];
-  lpf_level.filter_level[1] = cpi_->common.lf.filter_level[1];
-  lpf_level.filter_level_u = cpi_->common.lf.filter_level_u;
-  lpf_level.filter_level_v = cpi_->common.lf.filter_level_v;
+  if (rc_is_valid_) {
+    av1_pick_filter_level(nullptr, cpi_, LPF_PICK_FROM_Q);
+    lpf_level.filter_level[0] = cpi_->common.lf.filter_level[0];
+    lpf_level.filter_level[1] = cpi_->common.lf.filter_level[1];
+    lpf_level.filter_level_u = cpi_->common.lf.filter_level_u;
+    lpf_level.filter_level_v = cpi_->common.lf.filter_level_v;
+  } else {
+    lpf_level.filter_level[0] = 0;
+    lpf_level.filter_level[1] = 0;
+    lpf_level.filter_level_u = 0;
+    lpf_level.filter_level_v = 0;
+  }
   return lpf_level;
 }

 AV1CdefInfo AV1RateControlRTC::GetCdefInfo() const {
-  av1_pick_cdef_from_qp(&cpi_->common, /*skip_cdef=*/0, /*is_screen_content=*/0,
-                        /*avoid_uv_cdef=*/false);
   AV1CdefInfo cdef_level;
-  cdef_level.cdef_strength_y = cpi_->common.cdef_info.cdef_strengths[0];
-  cdef_level.cdef_strength_uv = cpi_->common.cdef_info.cdef_uv_strengths[0];
-  cdef_level.damping = cpi_->common.cdef_info.cdef_damping;
+  if (rc_is_valid_) {
+    av1_pick_cdef_from_qp(&cpi_->common, /*skip_cdef=*/0,
+                          /*is_screen_content=*/0,
+                          /*avoid_uv_cdef=*/false);
+    cdef_level.cdef_strength_y = cpi_->common.cdef_info.cdef_strengths[0];
+    cdef_level.cdef_strength_uv = cpi_->common.cdef_info.cdef_uv_strengths[0];
+    cdef_level.damping = cpi_->common.cdef_info.cdef_damping;
+  } else {
+    cdef_level.cdef_strength_y = 0;
+    cdef_level.cdef_strength_uv = 0;
+    cdef_level.damping = 0;
+  }
   return cdef_level;
 }

 bool AV1RateControlRTC::GetSegmentationData(
     AV1SegmentationData *segmentation_data) const {
+  if (!rc_is_valid_) return false;
   if (cpi_->oxcf.q_cfg.aq_mode == 0) {
     return false;
   }
@@ -421,6 +468,7 @@ bool AV1RateControlRTC::GetSegmentationData(
 }

 void AV1RateControlRTC::PostEncodeUpdate(uint64_t encoded_frame_size) {
+  if (!rc_is_valid_) return;
   cpi_->common.current_frame.frame_number++;
   av1_rc_postencode_update(cpi_, encoded_frame_size);
   if (cpi_->svc.spatial_layer_id == cpi_->svc.number_spatial_layers - 1) {
diff --git a/av1/ratectrl_rtc.h b/av1/ratectrl_rtc.h
index 3d0807b604..0eeaa68797 100644
--- a/av1/ratectrl_rtc.h
+++ b/av1/ratectrl_rtc.h
@@ -148,6 +148,7 @@ class AV1RateControlRTC {
   AV1_COMP *cpi_;
   int initial_width_;
   int initial_height_;
+  bool rc_is_valid_ = false;
 };
 }  // namespace aom
 #endif  // __cplusplus
diff --git a/test/ratectrl_rtc_test.cc b/test/ratectrl_rtc_test.cc
index 8d7e64258e..dc204e9462 100644
--- a/test/ratectrl_rtc_test.cc
+++ b/test/ratectrl_rtc_test.cc
@@ -258,6 +258,34 @@ class RcInterfaceTest : public ::libaom_test::EncoderTest,
     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
   }

+  void RunSvcInvalidInputs() {
+    key_interval_ = 10000;
+    // Initial resolution is set to 640x480.
+    SetConfigSvc(3, 3);
+    rc_api_ = aom::AV1RateControlRTC::Create(rc_cfg_);
+    // Verfiy return false on UpdateRateControl if resolution goes above
+    // configured setting.
+    rc_cfg_.width = 1280;
+    rc_cfg_.height = 720;
+    ASSERT_FALSE(rc_api_->UpdateRateControl(rc_cfg_));
+    // Go back to original resolution.
+    rc_cfg_.width = 640;
+    rc_cfg_.height = 480;
+    ASSERT_TRUE(rc_api_->UpdateRateControl(rc_cfg_));
+    // Set layer_id beyond the range set in init (#spatial=3, #temporal=3).
+    // Expect ComputeQP() to return kFrameDropDecisionDrop.
+    frame_params_.spatial_layer_id = 4;
+    frame_params_.temporal_layer_id = 0;
+    ASSERT_EQ(rc_api_->ComputeQP(frame_params_), aom::kFrameDropDecisionDrop);
+    frame_params_.spatial_layer_id = 0;
+    frame_params_.temporal_layer_id = 4;
+    ASSERT_EQ(rc_api_->ComputeQP(frame_params_), aom::kFrameDropDecisionDrop);
+    // Start with valid (0, 0) layer_id, expect return kFrameDropDecisionOk.
+    frame_params_.spatial_layer_id = 0;
+    frame_params_.temporal_layer_id = 0;
+    ASSERT_EQ(rc_api_->ComputeQP(frame_params_), aom::kFrameDropDecisionOk);
+  }
+
   void RunSvc() {
     key_interval_ = 10000;
     SetConfigSvc(3, 3);
@@ -684,6 +712,8 @@ TEST_P(RcInterfaceTest, OneLayerPeriodicKey) { RunOneLayerPeriodicKey(); }

 TEST_P(RcInterfaceTest, OneLayerScreen) { RunOneLayerScreen(); }

+TEST_P(RcInterfaceTest, SvcInvalidInputs) { RunSvcInvalidInputs(); }
+
 TEST_P(RcInterfaceTest, Svc) { RunSvc(); }

 TEST_P(RcInterfaceTest, SvcPeriodicKey) { RunSvcPeriodicKey(); }