Add overshoot of target bitrate for screenshare with temporal layers.

Set the codec target bitrate higher than TL0 but lower than TL1, making
sure frame rate is not too low (but still lower than TL1) and that
overshooting for complex scenes don't overly exceed TL1 bitrates.

BUG=4083
R=stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/34479004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7929 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc
index c31fe14..a678d07 100644
--- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc
@@ -14,11 +14,15 @@
 #include "vpx/vpx_encoder.h"
 #include "vpx/vp8cx.h"
 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
+#include "webrtc/system_wrappers/interface/field_trial.h"
 
 namespace webrtc {
 
 enum { kOneSecond90Khz = 90000 };
 
+const double ScreenshareLayers::kMaxTL0FpsReduction = 2.5;
+const double ScreenshareLayers::kAcceptableTargetOvershoot = 2.0;
+
 ScreenshareLayers::ScreenshareLayers(int num_temporal_layers,
                                      uint8_t initial_tl0_pic_idx,
                                      FrameDropper* tl0_frame_dropper,
@@ -97,6 +101,20 @@
   }
   tl0_frame_dropper_->SetRates(bitrate_kbit, framerate_);
   tl1_frame_dropper_->SetRates(max_bitrate_kbit, framerate_);
+
+  if (cfg != NULL && TargetBitrateExperimentEnabled()) {
+    // Calculate a codec target bitrate. This may be higher than TL0, gaining
+    // quality at the expense of frame rate at TL0. Constraints:
+    // - TL0 frame rate should not be less than framerate / kMaxTL0FpsReduction.
+    // - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate.
+    double target_bitrate =
+        std::min(bitrate_kbit * kMaxTL0FpsReduction,
+                 max_bitrate_kbit / kAcceptableTargetOvershoot);
+    cfg->rc_target_bitrate =
+        std::max(static_cast<unsigned int>(bitrate_kbit),
+                 static_cast<unsigned int>(target_bitrate + 0.5));
+  }
+
   return true;
 }
 
@@ -156,4 +174,11 @@
         timestamp_diff / 2) / timestamp_diff;
   }
 }
+
+bool ScreenshareLayers::TargetBitrateExperimentEnabled() {
+  std::string group =
+      field_trial::FindFullName("WebRTC-ScreencastTargetBitrateOvershoot");
+  return group == "Enabled";
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h
index ce97489..ac8226f 100644
--- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h
+++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h
@@ -24,6 +24,9 @@
 
 class ScreenshareLayers : public TemporalLayers {
  public:
+  static const double kMaxTL0FpsReduction;
+  static const double kAcceptableTargetOvershoot;
+
   ScreenshareLayers(int num_temporal_layers,
                     uint8_t initial_tl0_pic_idx,
                     FrameDropper* tl0_frame_dropper,
@@ -47,6 +50,9 @@
 
   virtual int CurrentLayerId() const;
 
+ protected:
+  virtual bool TargetBitrateExperimentEnabled();
+
  private:
   void CalculateFramerate(uint32_t timestamp);
   bool TimeToSync(uint32_t timestamp) const;
diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc
index c3090e1..5c7b70a 100644
--- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc
@@ -33,6 +33,22 @@
 const int kFlagsTL1Sync = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF |
     VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
 
+class ScreenshareLayersFT : public ScreenshareLayers {
+ public:
+  ScreenshareLayersFT(int num_temporal_layers,
+                      uint8_t initial_tl0_pic_idx,
+                      FrameDropper* tl0_frame_dropper,
+                      FrameDropper* tl1_frame_dropper)
+      : ScreenshareLayers(num_temporal_layers,
+                          initial_tl0_pic_idx,
+                          tl0_frame_dropper,
+                          tl1_frame_dropper) {}
+  virtual ~ScreenshareLayersFT() {}
+
+ protected:
+  virtual bool TargetBitrateExperimentEnabled() OVERRIDE { return true; }
+};
+
 class ScreenshareLayerTest : public ::testing::Test {
  protected:
   void SetEncodeExpectations(bool drop_tl0, bool drop_tl1, int framerate) {
@@ -77,12 +93,12 @@
 
   NiceMock<MockFrameDropper> tl0_frame_dropper_;
   NiceMock<MockFrameDropper> tl1_frame_dropper_;
-  scoped_ptr<ScreenshareLayers> layers_;
+  scoped_ptr<ScreenshareLayersFT> layers_;
 };
 
 TEST_F(ScreenshareLayerTest, 1Layer) {
-  layers_.reset(new ScreenshareLayers(1, 0, &tl0_frame_dropper_,
-                                      &tl1_frame_dropper_));
+  layers_.reset(
+      new ScreenshareLayersFT(1, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
   EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
   int flags = 0;
   uint32_t timestamp = 0;
@@ -116,8 +132,8 @@
 }
 
 TEST_F(ScreenshareLayerTest, 2Layer) {
-  layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_,
-                                      &tl1_frame_dropper_));
+  layers_.reset(
+      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
   EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
   int flags = 0;
   uint32_t timestamp = 0;
@@ -164,8 +180,8 @@
 }
 
 TEST_F(ScreenshareLayerTest, 2LayersPeriodicSync) {
-  layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_,
-                                      &tl1_frame_dropper_));
+  layers_.reset(
+      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
   EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
   int flags = 0;
   uint32_t timestamp = 0;
@@ -188,8 +204,8 @@
 }
 
 TEST_F(ScreenshareLayerTest, 2LayersToggling) {
-  layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_,
-                                      &tl1_frame_dropper_));
+  layers_.reset(
+      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
   EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
   int flags = 0;
   uint32_t timestamp = 0;
@@ -213,8 +229,8 @@
 }
 
 TEST_F(ScreenshareLayerTest, 2LayersBothDrops) {
-  layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_,
-                                      &tl1_frame_dropper_));
+  layers_.reset(
+      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
   EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
   int flags = 0;
   uint32_t timestamp = 0;
@@ -241,4 +257,37 @@
   flags = layers_->EncodeFlags(timestamp);
   EXPECT_EQ(-1, flags);
 }
+
+TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) {
+  layers_.reset(
+      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
+
+  vpx_codec_enc_cfg_t cfg;
+  layers_->ConfigureBitrates(100, 1000, 5, &cfg);
+
+  EXPECT_EQ(static_cast<unsigned int>(
+                ScreenshareLayers::kMaxTL0FpsReduction * 100 + 0.5),
+            cfg.rc_target_bitrate);
+}
+
+TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) {
+  layers_.reset(
+      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
+  vpx_codec_enc_cfg_t cfg;
+  layers_->ConfigureBitrates(100, 450, 5, &cfg);
+
+  EXPECT_EQ(static_cast<unsigned int>(
+                450 / ScreenshareLayers::kAcceptableTargetOvershoot),
+            cfg.rc_target_bitrate);
+}
+
+TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) {
+  layers_.reset(
+      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
+  vpx_codec_enc_cfg_t cfg;
+  layers_->ConfigureBitrates(100, 100, 5, &cfg);
+
+  EXPECT_EQ(100U, cfg.rc_target_bitrate);
+}
+
 }  // namespace webrtc