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 363521e..238484f 100644 --- a/av1/ratectrl_rtc.cc +++ b/av1/ratectrl_rtc.cc
@@ -122,6 +122,7 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ } 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 3d0807b..0eeaa68 100644 --- a/av1/ratectrl_rtc.h +++ b/av1/ratectrl_rtc.h
@@ -148,6 +148,7 @@ 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 8d7e642..dc204e94 100644 --- a/test/ratectrl_rtc_test.cc +++ b/test/ratectrl_rtc_test.cc
@@ -258,6 +258,34 @@ 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, OneLayerScreen) { RunOneLayerScreen(); } +TEST_P(RcInterfaceTest, SvcInvalidInputs) { RunSvcInvalidInputs(); } + TEST_P(RcInterfaceTest, Svc) { RunSvc(); } TEST_P(RcInterfaceTest, SvcPeriodicKey) { RunSvcPeriodicKey(); }