rtc: Fixes to frame-dropper for SVC and screen
1) For svc: avoid entering frame dropper for
frames whose base layer is key frame.
2) For screen mode: apply lower bound cap to
buffer level for all temporal layers.
Add unittest for 2TL at low bitrates, for screen.
Change-Id: Icc13a9a6bad62d8431e7c9fc2ee7020dbd42c1ff
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 4f0d087..41a14c6 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -3717,9 +3717,11 @@
}
// For 1 pass CBR, check if we are dropping this frame.
- // Never drop on key frame.
+ // Never drop on key frame, or for frame whose base layer is key.
if (has_no_stats_stage(cpi) && oxcf->rc_cfg.mode == AOM_CBR &&
- current_frame->frame_type != KEY_FRAME) {
+ current_frame->frame_type != KEY_FRAME &&
+ !(cpi->ppi->use_svc &&
+ cpi->svc.layer_context[cpi->svc.temporal_layer_id].is_key_frame)) {
FRAME_UPDATE_TYPE update_type =
cpi->ppi->gf_group.update_type[cpi->gf_frame_index];
(void)update_type;
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index f485aba..d9bda1e 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -258,7 +258,8 @@
// Update the buffer level for higher temporal layers, given the encoded current
// temporal layer.
-static void update_layer_buffer_level(SVC *svc, int encoded_frame_size) {
+static void update_layer_buffer_level(SVC *svc, int encoded_frame_size,
+ bool is_screen) {
const int current_temporal_layer = svc->temporal_layer_id;
for (int i = current_temporal_layer + 1; i < svc->number_temporal_layers;
++i) {
@@ -272,6 +273,15 @@
lp_rc->bits_off_target =
AOMMIN(lp_rc->bits_off_target, lp_rc->maximum_buffer_size);
lp_rc->buffer_level = lp_rc->bits_off_target;
+
+ // For screen-content mode: don't let buffer level go below threshold,
+ // given here as -rc->maximum_ buffer_size, to allow buffer to come back
+ // up sooner after slide change with big oveshoot.
+ if (is_screen) {
+ lp_rc->bits_off_target =
+ AOMMAX(lp_rc->bits_off_target, -lp_rc->maximum_buffer_size);
+ lp_rc->buffer_level = lp_rc->bits_off_target;
+ }
}
}
// Update the buffer level: leaky bucket model.
@@ -298,7 +308,8 @@
p_rc->buffer_level = p_rc->bits_off_target;
if (cpi->ppi->use_svc)
- update_layer_buffer_level(&cpi->svc, encoded_frame_size);
+ update_layer_buffer_level(&cpi->svc, encoded_frame_size,
+ cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN);
#if CONFIG_FPMT_TEST
/* The variable temp_buffer_level is introduced for quality
diff --git a/test/svc_datarate_test.cc b/test/svc_datarate_test.cc
index c50fdfc..69e867e 100644
--- a/test/svc_datarate_test.cc
+++ b/test/svc_datarate_test.cc
@@ -815,6 +815,44 @@
EXPECT_LE((int)GetMismatchFrames(), 30);
}
+ virtual void BasicRateTargetingSVC2TL1SLScreenDropFrameTest() {
+ cfg_.rc_buf_initial_sz = 500;
+ cfg_.rc_buf_optimal_sz = 500;
+ cfg_.rc_buf_sz = 1000;
+ cfg_.rc_dropframe_thresh = 30;
+ cfg_.rc_min_quantizer = 0;
+ cfg_.rc_max_quantizer = 52;
+ cfg_.rc_end_usage = AOM_CBR;
+ cfg_.g_lag_in_frames = 0;
+ cfg_.g_error_resilient = 0;
+
+ ::libaom_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352,
+ 288, 30, 1, 0, 300);
+
+ const int bitrate_array[2] = { 60, 100 };
+ cfg_.rc_target_bitrate = bitrate_array[GET_PARAM(4)];
+ ResetModel();
+ screen_mode_ = 1;
+ number_temporal_layers_ = 2;
+ number_spatial_layers_ = 1;
+ target_layer_bitrate_[0] = 60 * cfg_.rc_target_bitrate / 100;
+ target_layer_bitrate_[1] = cfg_.rc_target_bitrate;
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+ for (int i = 0; i < number_temporal_layers_ * number_spatial_layers_; i++) {
+ ASSERT_GE(effective_datarate_tl[i], target_layer_bitrate_[i] * 0.75)
+ << " The datarate for the file is lower than target by too much!";
+ ASSERT_LE(effective_datarate_tl[i], target_layer_bitrate_[i] * 1.5)
+ << " The datarate for the file is greater than target by too much!";
+ }
+ // Top temporal layers are non_reference, so exlcude them from
+ // mismatch count, since loopfilter/cdef is not applied for these on
+ // encoder side, but is always applied on decoder.
+ // This means 300 = #frames(300) - #TL2_frames(150).
+ // We use LE for screen since loopfilter level can become very small
+ // or zero and then the frame is not a mismatch.
+ EXPECT_LE((int)GetMismatchFrames(), 150);
+ }
+
virtual void BasicRateTargetingSVC1TL3SLScreenTest() {
cfg_.rc_buf_initial_sz = 500;
cfg_.rc_buf_optimal_sz = 500;
@@ -2119,6 +2157,11 @@
BasicRateTargetingSVC3TL1SLScreenTest();
}
+// Check basic rate targeting for CBR, for 2 temporal layers, 1 spatial
+// for screen mode, with frame dropper on at low bitrates
+TEST_P(DatarateTestSVC, BasicRateTargetingSVC2TL1SLScreenDropFrame) {
+ BasicRateTargetingSVC2TL1SLScreenDropFrameTest();
+}
// Check basic rate targeting for CBR, for 3 spatial layers, 1 temporal
// for screen mode.
TEST_P(DatarateTestSVC, BasicRateTargetingSVC1TL3SLScreen) {