rtc: Test for dynamic temporal layers in RC
Add test coverage to external RC for SVC with
temporal layer changes (3->2->1->3) on the fly.
A few fixes in the SVC code were needed for this:
1) add set_primary_rc_buffer_sizes() and
check_reset_rc_flag() in ctrl_set_svc_params.
This also allows for user to do dynamic update
with only calling AV1E_SET_SVC_PARAMS.
2) Reset the cyclic refresh parameters for layers
whose map is NULL, now done in the function
av1_update_layer_context_change_config().
Change-Id: I6545564a9fe14ca31030817ea74b80f421f24bcd
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index eb09f26..bf6dd82 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -27,6 +27,7 @@
#include "av1/encoder/ethread.h"
#include "av1/encoder/external_partition.h"
#include "av1/encoder/firstpass.h"
+#include "av1/encoder/rc_utils.h"
#include "av1/arg_defs.h"
#include "common/args_helper.h"
@@ -3417,6 +3418,7 @@
AV1_PRIMARY *const ppi = ctx->ppi;
AV1_COMP *const cpi = ppi->cpi;
AV1_COMMON *const cm = &cpi->common;
+ AV1EncoderConfig *oxcf = &cpi->oxcf;
aom_svc_params_t *const params = va_arg(args, aom_svc_params_t *);
int64_t target_bandwidth = 0;
ppi->number_spatial_layers = params->number_spatial_layers;
@@ -3453,7 +3455,10 @@
}
av1_init_layer_context(cpi);
}
+ oxcf->rc_cfg.target_bandwidth = target_bandwidth;
+ set_primary_rc_buffer_sizes(oxcf, cpi->ppi);
av1_update_layer_context_change_config(cpi, target_bandwidth);
+ check_reset_rc_flag(cpi);
}
av1_check_fpmt_config(ctx->ppi, &ctx->ppi->cpi->oxcf);
return AOM_CODEC_OK;
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c
index 919bc89..a2f6cbb 100644
--- a/av1/encoder/svc_layercontext.c
+++ b/av1/encoder/svc_layercontext.c
@@ -105,6 +105,9 @@
int layer = 0;
int64_t spatial_layer_target = 0;
float bitrate_alloc = 1.0;
+ AV1_COMMON *const cm = &cpi->common;
+ const int mi_rows = cm->mi_params.mi_rows;
+ const int mi_cols = cm->mi_params.mi_cols;
for (int sl = 0; sl < svc->number_spatial_layers; ++sl) {
for (int tl = 0; tl < svc->number_temporal_layers; ++tl) {
layer = LAYER_IDS_TO_IDX(sl, tl, svc->number_temporal_layers);
@@ -136,6 +139,17 @@
lrc->rtc_external_ratectrl = rc->rtc_external_ratectrl;
lrc->worst_quality = av1_quantizer_to_qindex(lc->max_q);
lrc->best_quality = av1_quantizer_to_qindex(lc->min_q);
+ // Reset the cyclic refresh parameters, if needed (map is NULL).
+ // Cyclic refresh is only applied on base temporal layer.
+ if (svc->number_spatial_layers > 1 && tl == 0 && lc->map == NULL) {
+ lc->sb_index = 0;
+ lc->actual_num_seg1_blocks = 0;
+ lc->actual_num_seg2_blocks = 0;
+ lc->counter_encode_maxq_scene_change = 0;
+ if (lc->map) aom_free(lc->map);
+ CHECK_MEM_ERROR(cm, lc->map,
+ aom_calloc(mi_rows * mi_cols, sizeof(*lc->map)));
+ }
}
}
}
diff --git a/test/ratectrl_rtc_test.cc b/test/ratectrl_rtc_test.cc
index 07a4e1d..ceef487 100644
--- a/test/ratectrl_rtc_test.cc
+++ b/test/ratectrl_rtc_test.cc
@@ -37,7 +37,8 @@
public:
RcInterfaceTest()
: EncoderTest(GET_PARAM(0)), aq_mode_(GET_PARAM(1)), key_interval_(3000),
- encoder_exit_(false), layer_frame_cnt_(0) {
+ encoder_exit_(false), layer_frame_cnt_(0), superframe_cnt_(0),
+ dynamic_temporal_layers_(false) {
memset(&svc_params_, 0, sizeof(svc_params_));
memset(&layer_id_, 0, sizeof(layer_id_));
}
@@ -68,9 +69,11 @@
frame_params_.spatial_layer_id =
layer_frame_cnt_ % rc_cfg_.ss_number_layers;
if (rc_cfg_.ts_number_layers == 3)
- frame_params_.temporal_layer_id = kTemporalId3Layer[video->frame() % 4];
+ frame_params_.temporal_layer_id =
+ kTemporalId3Layer[superframe_cnt_ % 4];
else if (rc_cfg_.ts_number_layers == 2)
- frame_params_.temporal_layer_id = kTemporalId2Layer[video->frame() % 2];
+ frame_params_.temporal_layer_id =
+ kTemporalId2Layer[superframe_cnt_ % 2];
else
frame_params_.temporal_layer_id = 0;
layer_id_.spatial_layer_id = frame_params_.spatial_layer_id;
@@ -81,6 +84,25 @@
frame_params_.frame_type =
layer_frame_cnt_ % key_int == 0 ? aom::kKeyFrame : aom::kInterFrame;
encoder_exit_ = video->frame() == kNumFrames;
+
+ if (dynamic_temporal_layers_) {
+ if (superframe_cnt_ == 100 && layer_id_.spatial_layer_id == 0) {
+ // Go down to 2 temporal layers.
+ SetConfigSvc(3, 2);
+ encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_);
+ rc_api_->UpdateRateControl(rc_cfg_);
+ } else if (superframe_cnt_ == 200 && layer_id_.spatial_layer_id == 0) {
+ // Go down to 1 temporal layer.
+ SetConfigSvc(3, 1);
+ encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_);
+ rc_api_->UpdateRateControl(rc_cfg_);
+ } else if (superframe_cnt_ == 300 && layer_id_.spatial_layer_id == 0) {
+ // Go back up to 3 temporal layers.
+ SetConfigSvc(3, 3);
+ encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_);
+ rc_api_->UpdateRateControl(rc_cfg_);
+ }
+ }
}
void PostEncodeFrameHook(::libaom_test::Encoder *encoder) override {
@@ -88,6 +110,8 @@
return;
}
layer_frame_cnt_++;
+ if (layer_id_.spatial_layer_id == rc_cfg_.ss_number_layers - 1)
+ superframe_cnt_++;
int qp;
encoder->Control(AOME_GET_LAST_QUANTIZER, &qp);
rc_api_->ComputeQP(frame_params_);
@@ -158,6 +182,20 @@
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
}
+ void RunSvcDynamicTemporal() {
+ dynamic_temporal_layers_ = true;
+ key_interval_ = 10000;
+ SetConfigSvc(3, 3);
+ rc_api_ = aom::AV1RateControlRTC::Create(rc_cfg_);
+ frame_params_.spatial_layer_id = 0;
+ frame_params_.temporal_layer_id = 0;
+
+ ::libaom_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30,
+ 1, 0, kNumFrames);
+
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+ }
+
private:
void SetConfig() {
rc_cfg_.width = 640;
@@ -334,6 +372,8 @@
aom_svc_params_t svc_params_;
aom_svc_layer_id_t layer_id_;
int layer_frame_cnt_;
+ int superframe_cnt_;
+ bool dynamic_temporal_layers_;
};
TEST_P(RcInterfaceTest, OneLayer) { RunOneLayer(); }
@@ -344,6 +384,8 @@
TEST_P(RcInterfaceTest, SvcPeriodicKey) { RunSvcPeriodicKey(); }
+TEST_P(RcInterfaceTest, SvcDynamicTemporal) { RunSvcDynamicTemporal(); }
+
AV1_INSTANTIATE_TEST_SUITE(RcInterfaceTest, ::testing::Values(0, 3));
} // namespace