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) {