In screenshare mode, suppress VP8 bitrate overshoot and increase quality

This change includes several improvements:

* VP8 configured with new rate control
* Detection of frame dropping, with qp bump for next frame
* Increased target and TL0 bitrates
* Reworked rate control (TL allocation) in screenshare_layers

A note on performance: PSNR and SSIM is expected to get slightly worse with this cl. Frame drops and delays should however improve.

BUG=4171
R=pbos@webrtc.org, stefan@webrtc.org

Review URL: https://codereview.webrtc.org/1193513006.

Cr-Commit-Position: refs/heads/master@{#9495}
diff --git a/talk/media/webrtc/simulcast.cc b/talk/media/webrtc/simulcast.cc
index 6a822b8..759cfef 100755
--- a/talk/media/webrtc/simulcast.cc
+++ b/talk/media/webrtc/simulcast.cc
@@ -411,7 +411,7 @@
 
 static const int kScreenshareMinBitrateKbps = 50;
 static const int kScreenshareMaxBitrateKbps = 6000;
-static const int kScreenshareDefaultTl0BitrateKbps = 100;
+static const int kScreenshareDefaultTl0BitrateKbps = 200;
 static const int kScreenshareDefaultTl1BitrateKbps = 1000;
 
 static const char* kScreencastLayerFieldTrialName =
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index d83e8c4..230ca3b 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -1438,7 +1438,8 @@
 
 TEST_F(WebRtcVideoChannel2Test,
        ConferenceModeScreencastConfiguresTemporalLayer) {
-  static const int kConferenceScreencastTemporalBitrateBps = 100000;
+  static const int kConferenceScreencastTemporalBitrateBps =
+      ScreenshareLayerConfig::GetDefault().tl0_bitrate_kbps * 1000;
   VideoOptions options;
   options.conference_mode.Set(true);
   channel_->SetOptions(options);
diff --git a/webrtc/modules/video_coding/codecs/interface/video_error_codes.h b/webrtc/modules/video_coding/codecs/interface/video_error_codes.h
index 349a39a..28e5a32 100644
--- a/webrtc/modules/video_coding/codecs/interface/video_error_codes.h
+++ b/webrtc/modules/video_coding/codecs/interface/video_error_codes.h
@@ -27,5 +27,6 @@
 #define WEBRTC_VIDEO_CODEC_UNINITIALIZED -7
 #define WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI -12
 #define WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE -13
+#define WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT -14
 
 #endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_INTERFACE_VIDEO_ERROR_CODES_H
diff --git a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h
index 61f281f..19846ba 100644
--- a/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h
+++ b/webrtc/modules/video_coding/codecs/vp8/default_temporal_layers.h
@@ -24,20 +24,22 @@
 
   // Returns the recommended VP8 encode flags needed. May refresh the decoder
   // and/or update the reference buffers.
-  virtual int EncodeFlags(uint32_t timestamp);
+  int EncodeFlags(uint32_t timestamp) override;
 
-  virtual bool ConfigureBitrates(int bitrate_kbit,
-                                 int max_bitrate_kbit,
-                                 int framerate,
-                                 vpx_codec_enc_cfg_t* cfg);
+  bool ConfigureBitrates(int bitrate_kbit,
+                         int max_bitrate_kbit,
+                         int framerate,
+                         vpx_codec_enc_cfg_t* cfg) override;
 
-  virtual void PopulateCodecSpecific(bool base_layer_sync,
-                                     CodecSpecificInfoVP8* vp8_info,
-                                     uint32_t timestamp);
+  void PopulateCodecSpecific(bool base_layer_sync,
+                             CodecSpecificInfoVP8* vp8_info,
+                             uint32_t timestamp) override;
 
-  virtual void FrameEncoded(unsigned int size, uint32_t timestamp) {}
+  void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
 
-  virtual int CurrentLayerId() const;
+  bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override { return false; }
+
+  int CurrentLayerId() const override;
 
  private:
   enum TemporalReferences {
diff --git a/webrtc/modules/video_coding/codecs/vp8/realtime_temporal_layers.cc b/webrtc/modules/video_coding/codecs/vp8/realtime_temporal_layers.cc
index f16c756..15b5af9 100644
--- a/webrtc/modules/video_coding/codecs/vp8/realtime_temporal_layers.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/realtime_temporal_layers.cc
@@ -239,7 +239,9 @@
     }
   }
 
-  void FrameEncoded(unsigned int size, uint32_t timestamp) {}
+  void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
+
+  bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override { return false; }
 
  private:
   int temporal_layers_;
diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc
index 63ef227..ecaf3dd 100644
--- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc
@@ -11,32 +11,52 @@
 
 #include <stdlib.h>
 
+#include "webrtc/base/checks.h"
 #include "vpx/vpx_encoder.h"
 #include "vpx/vp8cx.h"
 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
 
 namespace webrtc {
 
-enum { kOneSecond90Khz = 90000 };
+static const int kOneSecond90Khz = 90000;
+static const int kMinTimeBetweenSyncs = kOneSecond90Khz * 5;
+static const int kMaxTimeBetweenSyncs = kOneSecond90Khz * 10;
+static const int kQpDeltaThresholdForSync = 8;
 
 const double ScreenshareLayers::kMaxTL0FpsReduction = 2.5;
 const double ScreenshareLayers::kAcceptableTargetOvershoot = 2.0;
 
+// Since this is TL0 we only allow updating and predicting from the LAST
+// reference frame.
+const int ScreenshareLayers::kTl0Flags =
+    VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF |
+    VP8_EFLAG_NO_REF_ARF;
+
+// Allow predicting from both TL0 and TL1.
+const int ScreenshareLayers::kTl1Flags =
+    VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
+
+// Allow predicting from only TL0 to allow participants to switch to the high
+// bitrate stream. This means predicting only from the LAST reference frame, but
+// only updating GF to not corrupt TL0.
+const int ScreenshareLayers::kTl1SyncFlags =
+    VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF |
+    VP8_EFLAG_NO_UPD_LAST;
+
 ScreenshareLayers::ScreenshareLayers(int num_temporal_layers,
-                                     uint8_t initial_tl0_pic_idx,
-                                     FrameDropper* tl0_frame_dropper,
-                                     FrameDropper* tl1_frame_dropper)
-    : tl0_frame_dropper_(tl0_frame_dropper),
-      tl1_frame_dropper_(tl1_frame_dropper),
-      number_of_temporal_layers_(num_temporal_layers),
+                                     uint8_t initial_tl0_pic_idx)
+    : number_of_temporal_layers_(num_temporal_layers),
       last_base_layer_sync_(false),
       tl0_pic_idx_(initial_tl0_pic_idx),
-      active_layer_(0),
-      framerate_(5),
-      last_sync_timestamp_(-1) {
+      active_layer_(-1),
+      last_timestamp_(-1),
+      last_sync_timestamp_(-1),
+      min_qp_(-1),
+      max_qp_(-1),
+      max_debt_bytes_(0),
+      frame_rate_(-1) {
   assert(num_temporal_layers > 0);
   assert(num_temporal_layers <= 2);
-  assert(tl0_frame_dropper && tl1_frame_dropper);
 }
 
 int ScreenshareLayers::CurrentLayerId() const {
@@ -49,84 +69,125 @@
     // No flags needed for 1 layer screenshare.
     return 0;
   }
-  CalculateFramerate(timestamp);
+
+  int64_t unwrapped_timestamp = time_wrap_handler_.Unwrap(timestamp);
   int flags = 0;
-  // Note that ARF on purpose isn't used in this scheme since it is allocated
-  // for the last key frame to make key frame caching possible.
-  if (tl0_frame_dropper_->DropFrame()) {
-    // Must drop TL0, encode TL1 instead.
-    if (tl1_frame_dropper_->DropFrame()) {
-      // Must drop both TL0 and TL1.
-      flags = -1;
-    } else {
-      active_layer_ = 1;
-      if (TimeToSync(timestamp)) {
-        last_sync_timestamp_ = timestamp;
-        // Allow predicting from only TL0 to allow participants to switch to the
-        // high bitrate stream. This means predicting only from the LAST
-        // reference frame, but only updating GF to not corrupt TL0.
-        flags = VP8_EFLAG_NO_REF_ARF;
-        flags |= VP8_EFLAG_NO_REF_GF;
-        flags |= VP8_EFLAG_NO_UPD_ARF;
-        flags |= VP8_EFLAG_NO_UPD_LAST;
+
+  if (active_layer_ == -1 ||
+      layers_[active_layer_].state != TemporalLayer::State::kDropped) {
+    if (layers_[0].debt_bytes_ > max_debt_bytes_) {
+      // Must drop TL0, encode TL1 instead.
+      if (layers_[1].debt_bytes_ > max_debt_bytes_) {
+        // Must drop both TL0 and TL1.
+        active_layer_ = -1;
       } else {
-        // Allow predicting from both TL0 and TL1.
-        flags = VP8_EFLAG_NO_REF_ARF;
-        flags |= VP8_EFLAG_NO_UPD_ARF;
-        flags |= VP8_EFLAG_NO_UPD_LAST;
+        active_layer_ = 1;
       }
+    } else {
+      active_layer_ = 0;
     }
-  } else {
-    active_layer_ = 0;
-    // Since this is TL0 we only allow updating and predicting from the LAST
-    // reference frame.
-    flags = VP8_EFLAG_NO_UPD_GF;
-    flags |= VP8_EFLAG_NO_UPD_ARF;
-    flags |= VP8_EFLAG_NO_REF_GF;
-    flags |= VP8_EFLAG_NO_REF_ARF;
   }
+
+  switch (active_layer_) {
+    case 0:
+      flags = kTl0Flags;
+      break;
+    case 1:
+      if (TimeToSync(unwrapped_timestamp)) {
+        last_sync_timestamp_ = unwrapped_timestamp;
+        flags = kTl1SyncFlags;
+      } else {
+        flags = kTl1Flags;
+      }
+      break;
+    case -1:
+      flags = -1;
+      break;
+    default:
+      flags = -1;
+      RTC_NOTREACHED();
+  }
+
   // Make sure both frame droppers leak out bits.
-  tl0_frame_dropper_->Leak(framerate_);
-  tl1_frame_dropper_->Leak(framerate_);
+  int64_t ts_diff;
+  if (last_timestamp_ == -1) {
+    ts_diff = kOneSecond90Khz / (frame_rate_ <= 0 ? 5 : frame_rate_);
+  } else {
+    ts_diff = unwrapped_timestamp - last_timestamp_;
+  }
+
+  layers_[0].UpdateDebt(ts_diff / 90);
+  layers_[1].UpdateDebt(ts_diff / 90);
+  last_timestamp_ = timestamp;
   return flags;
 }
 
-bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbit,
-                                          int max_bitrate_kbit,
+bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbps,
+                                          int max_bitrate_kbps,
                                           int framerate,
                                           vpx_codec_enc_cfg_t* cfg) {
-  if (framerate > 0)
-    framerate_ = framerate;
+  layers_[0].target_rate_kbps_ = bitrate_kbps;
+  layers_[1].target_rate_kbps_ = max_bitrate_kbps;
 
-  tl0_frame_dropper_->SetRates(bitrate_kbit, framerate_);
-  tl1_frame_dropper_->SetRates(max_bitrate_kbit, framerate_);
+  int target_bitrate_kbps = bitrate_kbps;
 
   if (cfg != nullptr) {
     // 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));
+    target_bitrate_kbps =
+        std::min(bitrate_kbps * kMaxTL0FpsReduction,
+                 max_bitrate_kbps / kAcceptableTargetOvershoot);
+
+    // Don't reconfigure qp limits during quality boost frames.
+    if (layers_[active_layer_].state != TemporalLayer::State::kQualityBoost) {
+      min_qp_ = cfg->rc_min_quantizer;
+      max_qp_ = cfg->rc_max_quantizer;
+      // After a dropped frame, a frame with max qp will be encoded and the
+      // quality will then ramp up from there. To boost the speed of recovery,
+      // encode the next frame with lower max qp. TL0 is the most important to
+      // improve since the errors in this layer will propagate to TL1.
+      // Currently, reduce max qp by 20% for TL0 and 15% for TL1.
+      layers_[0].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 80) / 100);
+      layers_[1].enhanced_max_qp = min_qp_ + (((max_qp_ - min_qp_) * 85) / 100);
+    }
+
+    cfg->rc_target_bitrate = std::max(bitrate_kbps, target_bitrate_kbps);
   }
 
+  int avg_frame_size = (target_bitrate_kbps * 1000) / (8 * framerate);
+  max_debt_bytes_ = 4 * avg_frame_size;
+
   return true;
 }
 
-void ScreenshareLayers::FrameEncoded(unsigned int size, uint32_t timestamp) {
-  if (active_layer_ == 0) {
-    tl0_frame_dropper_->Fill(size, true);
+void ScreenshareLayers::FrameEncoded(unsigned int size,
+                                     uint32_t timestamp,
+                                     int qp) {
+  if (size == 0) {
+    layers_[active_layer_].state = TemporalLayer::State::kDropped;
+    return;
   }
-  tl1_frame_dropper_->Fill(size, true);
+  if (layers_[active_layer_].state == TemporalLayer::State::kDropped) {
+    layers_[active_layer_].state = TemporalLayer::State::kQualityBoost;
+  }
+
+  if (qp != -1)
+    layers_[active_layer_].last_qp = qp;
+
+  if (active_layer_ == 0) {
+    layers_[0].debt_bytes_ += size;
+    layers_[1].debt_bytes_ += size;
+  } else if (active_layer_ == 1) {
+    layers_[1].debt_bytes_ += size;
+  }
 }
 
 void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync,
                                               CodecSpecificInfoVP8 *vp8_info,
                                               uint32_t timestamp) {
+  int64_t unwrapped_timestamp = time_wrap_handler_.Unwrap(timestamp);
   if (number_of_temporal_layers_ == 1) {
     vp8_info->temporalIdx = kNoTemporalIdx;
     vp8_info->layerSync = false;
@@ -135,13 +196,14 @@
     vp8_info->temporalIdx = active_layer_;
     if (base_layer_sync) {
       vp8_info->temporalIdx = 0;
-      last_sync_timestamp_ = timestamp;
+      last_sync_timestamp_ = unwrapped_timestamp;
     } else if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) {
       // Regardless of pattern the frame after a base layer sync will always
       // be a layer sync.
-      last_sync_timestamp_ = timestamp;
+      last_sync_timestamp_ = unwrapped_timestamp;
     }
-    vp8_info->layerSync = (last_sync_timestamp_ == timestamp);
+    vp8_info->layerSync = last_sync_timestamp_ != -1 &&
+                          last_sync_timestamp_ == unwrapped_timestamp;
     if (vp8_info->temporalIdx == 0) {
       tl0_pic_idx_++;
     }
@@ -150,27 +212,65 @@
   }
 }
 
-bool ScreenshareLayers::TimeToSync(uint32_t timestamp) const {
-  const uint32_t timestamp_diff = timestamp - last_sync_timestamp_;
-  return last_sync_timestamp_ < 0 || timestamp_diff > kOneSecond90Khz;
+bool ScreenshareLayers::TimeToSync(int64_t timestamp) const {
+  if (active_layer_ != 1) {
+    RTC_NOTREACHED();
+    return false;
+  }
+  DCHECK_NE(-1, layers_[0].last_qp);
+  if (layers_[1].last_qp == -1) {
+    // First frame in TL1 should only depend on TL0 since there are no
+    // previous frames in TL1.
+    return true;
+  }
+
+  DCHECK_NE(-1, last_sync_timestamp_);
+  int64_t timestamp_diff = timestamp - last_sync_timestamp_;
+  if (timestamp_diff > kMaxTimeBetweenSyncs) {
+    // After a certain time, force a sync frame.
+    return true;
+  } else if (timestamp_diff < kMinTimeBetweenSyncs) {
+    // If too soon from previous sync frame, don't issue a new one.
+    return false;
+  }
+  // Issue a sync frame if difference in quality between TL0 and TL1 isn't too
+  // large.
+  if (layers_[0].last_qp - layers_[1].last_qp < kQpDeltaThresholdForSync)
+    return true;
+  return false;
 }
 
-void ScreenshareLayers::CalculateFramerate(uint32_t timestamp) {
-  timestamp_list_.push_front(timestamp);
-  // Remove timestamps older than 1 second from the list.
-  uint32_t timestamp_diff = timestamp - timestamp_list_.back();
-  while (timestamp_diff > kOneSecond90Khz) {
-    timestamp_list_.pop_back();
-    timestamp_diff = timestamp - timestamp_list_.back();
+bool ScreenshareLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
+  if (max_qp_ == -1)
+    return false;
+
+  // If layer is in the quality boost state (following a dropped frame), update
+  // the configuration with the adjusted (lower) qp and set the state back to
+  // normal.
+  unsigned int adjusted_max_qp;
+  if (layers_[active_layer_].state == TemporalLayer::State::kQualityBoost &&
+      layers_[active_layer_].enhanced_max_qp != -1) {
+    adjusted_max_qp = layers_[active_layer_].enhanced_max_qp;
+    layers_[active_layer_].state = TemporalLayer::State::kNormal;
+  } else {
+    if (max_qp_ == -1)
+      return false;
+    adjusted_max_qp = max_qp_;  // Set the normal max qp.
   }
-  // If we have encoded frames within the last second, that number of frames
-  // is a reasonable first estimate of the framerate.
-  framerate_ = timestamp_list_.size();
-  if (timestamp_diff > 0) {
-    // Estimate the framerate by dividing the number of timestamp diffs with
-    // the sum of the timestamp diffs (with rounding).
-    framerate_ = (kOneSecond90Khz * (timestamp_list_.size() - 1) +
-        timestamp_diff / 2) / timestamp_diff;
+
+  if (adjusted_max_qp == cfg->rc_max_quantizer)
+    return false;
+
+  cfg->rc_max_quantizer = adjusted_max_qp;
+  return true;
+}
+
+void ScreenshareLayers::TemporalLayer::UpdateDebt(int64_t delta_ms) {
+  uint32_t debt_reduction_bytes = target_rate_kbps_ * delta_ms / 8;
+  if (debt_reduction_bytes >= debt_bytes_) {
+    debt_bytes_ = 0;
+  } else {
+    debt_bytes_ -= debt_reduction_bytes;
   }
 }
 
diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h
index 0bc571e..90a8b1b 100644
--- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h
+++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h
@@ -13,6 +13,7 @@
 
 #include "vpx/vpx_encoder.h"
 
+#include "webrtc/base/timeutils.h"
 #include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
 #include "webrtc/modules/video_coding/utility/include/frame_dropper.h"
 #include "webrtc/typedefs.h"
@@ -25,43 +26,73 @@
  public:
   static const double kMaxTL0FpsReduction;
   static const double kAcceptableTargetOvershoot;
+  static const int kTl0Flags;
+  static const int kTl1Flags;
+  static const int kTl1SyncFlags;
 
-  ScreenshareLayers(int num_temporal_layers,
-                    uint8_t initial_tl0_pic_idx,
-                    FrameDropper* tl0_frame_dropper,
-                    FrameDropper* tl1_frame_dropper);
+  ScreenshareLayers(int num_temporal_layers, uint8_t initial_tl0_pic_idx);
   virtual ~ScreenshareLayers() {}
 
   // Returns the recommended VP8 encode flags needed. May refresh the decoder
   // and/or update the reference buffers.
-  virtual int EncodeFlags(uint32_t timestamp);
+  int EncodeFlags(uint32_t timestamp) override;
 
-  virtual bool ConfigureBitrates(int bitrate_kbit,
-                                 int max_bitrate_kbit,
-                                 int framerate,
-                                 vpx_codec_enc_cfg_t* cfg);
+  bool ConfigureBitrates(int bitrate_kbps,
+                         int max_bitrate_kbps,
+                         int framerate,
+                         vpx_codec_enc_cfg_t* cfg) override;
 
-  virtual void PopulateCodecSpecific(bool base_layer_sync,
-                                     CodecSpecificInfoVP8 *vp8_info,
-                                     uint32_t timestamp);
+  void PopulateCodecSpecific(bool base_layer_sync,
+                             CodecSpecificInfoVP8* vp8_info,
+                             uint32_t timestamp) override;
 
-  virtual void FrameEncoded(unsigned int size, uint32_t timestamp);
+  void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override;
 
-  virtual int CurrentLayerId() const;
+  int CurrentLayerId() const override;
+
+  // Allows the layers adapter to update the encoder configuration prior to a
+  // frame being encoded. Return true if the configuration should be updated
+  // and false if now change is needed.
+  bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
 
  private:
-  void CalculateFramerate(uint32_t timestamp);
-  bool TimeToSync(uint32_t timestamp) const;
+  bool TimeToSync(int64_t timestamp) const;
 
-  FrameDropper* tl0_frame_dropper_;
-  FrameDropper* tl1_frame_dropper_;
   int number_of_temporal_layers_;
   bool last_base_layer_sync_;
   uint8_t tl0_pic_idx_;
   int active_layer_;
-  std::list<uint32_t> timestamp_list_;
-  int framerate_;
+  int64_t last_timestamp_;
   int64_t last_sync_timestamp_;
+  rtc::TimestampWrapAroundHandler time_wrap_handler_;
+  int min_qp_;
+  int max_qp_;
+  uint32_t max_debt_bytes_;
+  int frame_rate_;
+
+  static const int kMaxNumTemporalLayers = 2;
+  struct TemporalLayer {
+    TemporalLayer()
+        : state(State::kNormal),
+          enhanced_max_qp(-1),
+          last_qp(-1),
+          debt_bytes_(0),
+          target_rate_kbps_(0) {}
+
+    enum class State {
+      kNormal,
+      kDropped,
+      kReencoded,
+      kQualityBoost,
+    } state;
+
+    int enhanced_max_qp;
+    int last_qp;
+    uint32_t debt_bytes_;
+    uint32_t target_rate_kbps_;
+
+    void UpdateDebt(int64_t delta_ms);
+  } layers_[kMaxNumTemporalLayers];
 };
 }  // namespace webrtc
 
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 e12f9ce..198be2a 100644
--- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc
@@ -22,62 +22,19 @@
 
 namespace webrtc {
 
-enum { kTimestampDelta5Fps = 90000 / 5 };  // 5 frames per second at 90 kHz.
-enum { kTimestampDelta30Fps = 90000 / 30 };  // 30 frames per second at 90 kHz.
-enum { kFrameSize = 2500 };
-
-const int kFlagsTL0 = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
-    VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF;
-const int kFlagsTL1 = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF |
-    VP8_EFLAG_NO_UPD_LAST;
-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() {}
-};
+// 5 frames per second at 90 kHz.
+const uint32_t kTimestampDelta5Fps = 90000 / 5;
+const int kDefaultQp = 54;
+const int kDefaultTl0BitrateKbps = 200;
+const int kDefaultTl1BitrateKbps = 2000;
+const int kFrameRate = 5;
+const int kSyncPeriodSeconds = 5;
+const int kMaxSyncPeriodSeconds = 10;
 
 class ScreenshareLayerTest : public ::testing::Test {
  protected:
-  void SetEncodeExpectations(bool drop_tl0, bool drop_tl1, int framerate) {
-    EXPECT_CALL(tl0_frame_dropper_, DropFrame())
-        .Times(1)
-        .WillRepeatedly(Return(drop_tl0));
-    if (drop_tl0) {
-      EXPECT_CALL(tl1_frame_dropper_, DropFrame())
-          .Times(1)
-          .WillRepeatedly(Return(drop_tl1));
-    }
-    EXPECT_CALL(tl0_frame_dropper_, Leak(framerate))
-        .Times(1);
-    EXPECT_CALL(tl1_frame_dropper_, Leak(framerate))
-        .Times(1);
-    if (drop_tl0) {
-      EXPECT_CALL(tl0_frame_dropper_, Fill(_, _))
-          .Times(0);
-      if (drop_tl1) {
-        EXPECT_CALL(tl1_frame_dropper_, Fill(_, _))
-              .Times(0);
-      } else {
-        EXPECT_CALL(tl1_frame_dropper_, Fill(kFrameSize, true))
-            .Times(1);
-      }
-    } else {
-      EXPECT_CALL(tl0_frame_dropper_, Fill(kFrameSize, true))
-          .Times(1);
-      EXPECT_CALL(tl1_frame_dropper_, Fill(kFrameSize, true))
-          .Times(1);
-    }
-  }
+  ScreenshareLayerTest() : min_qp_(2), max_qp_(kDefaultQp), frame_size_(-1) {}
+  virtual ~ScreenshareLayerTest() {}
 
   void EncodeFrame(uint32_t timestamp,
                    bool base_sync,
@@ -85,39 +42,83 @@
                    int* flags) {
     *flags = layers_->EncodeFlags(timestamp);
     layers_->PopulateCodecSpecific(base_sync, vp8_info, timestamp);
-    layers_->FrameEncoded(kFrameSize, timestamp);
+    ASSERT_NE(-1, frame_size_);
+    layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
   }
 
-  NiceMock<MockFrameDropper> tl0_frame_dropper_;
-  NiceMock<MockFrameDropper> tl1_frame_dropper_;
-  rtc::scoped_ptr<ScreenshareLayersFT> layers_;
+  void ConfigureBitrates() {
+    vpx_codec_enc_cfg_t vpx_cfg;
+    memset(&vpx_cfg, 0, sizeof(vpx_codec_enc_cfg_t));
+    vpx_cfg.rc_min_quantizer = min_qp_;
+    vpx_cfg.rc_max_quantizer = max_qp_;
+    EXPECT_TRUE(layers_->ConfigureBitrates(
+        kDefaultTl0BitrateKbps, kDefaultTl1BitrateKbps, kFrameRate, &vpx_cfg));
+    frame_size_ = ((vpx_cfg.rc_target_bitrate * 1000) / 8) / kFrameRate;
+  }
+
+  void WithQpLimits(int min_qp, int max_qp) {
+    min_qp_ = min_qp;
+    max_qp_ = max_qp;
+  }
+
+  int RunGracePeriod() {
+    int flags = 0;
+    uint32_t timestamp = 0;
+    CodecSpecificInfoVP8 vp8_info;
+    bool got_tl0 = false;
+    bool got_tl1 = false;
+    for (int i = 0; i < 10; ++i) {
+      EncodeFrame(timestamp, false, &vp8_info, &flags);
+      timestamp += kTimestampDelta5Fps;
+      if (vp8_info.temporalIdx == 0) {
+        got_tl0 = true;
+      } else {
+        got_tl1 = true;
+      }
+      if (got_tl0 && got_tl1)
+        return timestamp;
+    }
+    ADD_FAILURE() << "Frames from both layers not received in time.";
+    return 0;
+  }
+
+  int SkipUntilTl(int layer, int timestamp) {
+    CodecSpecificInfoVP8 vp8_info;
+    for (int i = 0; i < 5; ++i) {
+      layers_->EncodeFlags(timestamp);
+      timestamp += kTimestampDelta5Fps;
+      layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+      if (vp8_info.temporalIdx != layer) {
+        layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+      } else {
+        return timestamp;
+      }
+    }
+    ADD_FAILURE() << "Did not get a frame of TL" << layer << " in time.";
+    return 0;
+  }
+
+  int min_qp_;
+  int max_qp_;
+  int frame_size_;
+  rtc::scoped_ptr<ScreenshareLayers> layers_;
 };
 
 TEST_F(ScreenshareLayerTest, 1Layer) {
-  layers_.reset(
-      new ScreenshareLayersFT(1, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
-  EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
+  layers_.reset(new ScreenshareLayers(1, 0));
+  ConfigureBitrates();
   int flags = 0;
   uint32_t timestamp = 0;
   CodecSpecificInfoVP8 vp8_info;
   // One layer screenshare should not use the frame dropper as all frames will
   // belong to the base layer.
-  EXPECT_CALL(tl0_frame_dropper_, DropFrame())
-      .Times(0);
-  EXPECT_CALL(tl1_frame_dropper_, DropFrame())
-      .Times(0);
   flags = layers_->EncodeFlags(timestamp);
   EXPECT_EQ(0, flags);
   layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
   EXPECT_EQ(static_cast<uint8_t>(kNoTemporalIdx), vp8_info.temporalIdx);
   EXPECT_FALSE(vp8_info.layerSync);
   EXPECT_EQ(kNoTl0PicIdx, vp8_info.tl0PicIdx);
-  layers_->FrameEncoded(kFrameSize, timestamp);
-
-  EXPECT_CALL(tl0_frame_dropper_, DropFrame())
-      .Times(0);
-  EXPECT_CALL(tl1_frame_dropper_, DropFrame())
-      .Times(0);
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
   flags = layers_->EncodeFlags(timestamp);
   EXPECT_EQ(0, flags);
   timestamp += kTimestampDelta5Fps;
@@ -125,139 +126,241 @@
   EXPECT_EQ(static_cast<uint8_t>(kNoTemporalIdx), vp8_info.temporalIdx);
   EXPECT_FALSE(vp8_info.layerSync);
   EXPECT_EQ(kNoTl0PicIdx, vp8_info.tl0PicIdx);
-  layers_->FrameEncoded(kFrameSize, timestamp);
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
 }
 
 TEST_F(ScreenshareLayerTest, 2Layer) {
-  layers_.reset(
-      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
-  EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
   int flags = 0;
   uint32_t timestamp = 0;
   uint8_t expected_tl0_idx = 0;
   CodecSpecificInfoVP8 vp8_info;
-  SetEncodeExpectations(false, false, 1);
   EncodeFrame(timestamp, false, &vp8_info, &flags);
-  EXPECT_EQ(kFlagsTL0, flags);
+  EXPECT_EQ(ScreenshareLayers::kTl0Flags, flags);
   EXPECT_EQ(0, vp8_info.temporalIdx);
   EXPECT_FALSE(vp8_info.layerSync);
   ++expected_tl0_idx;
   EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
 
-  EXPECT_CALL(tl1_frame_dropper_, SetRates(1000, 1))
-      .Times(1);
-  EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, -1, NULL));
-  // Insert 5 frames at 30 fps. All should belong to TL0.
+  // Insert 5 frames, cover grace period. All should be in TL0.
   for (int i = 0; i < 5; ++i) {
-    timestamp += kTimestampDelta30Fps;
-    // First iteration has a framerate based on a single frame, thus 1.
-    SetEncodeExpectations(false, false, 30);
+    timestamp += kTimestampDelta5Fps;
     EncodeFrame(timestamp, false, &vp8_info, &flags);
     EXPECT_EQ(0, vp8_info.temporalIdx);
     EXPECT_FALSE(vp8_info.layerSync);
     ++expected_tl0_idx;
     EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
   }
-  // Drop two frames from TL0, thus being coded in TL1.
-  timestamp += kTimestampDelta30Fps;
-  SetEncodeExpectations(true, false, 30);
+
+  // First frame in TL0.
+  timestamp += kTimestampDelta5Fps;
   EncodeFrame(timestamp, false, &vp8_info, &flags);
-  EXPECT_EQ(kFlagsTL1Sync, flags);
+  EXPECT_EQ(ScreenshareLayers::kTl0Flags, flags);
+  EXPECT_EQ(0, vp8_info.temporalIdx);
+  EXPECT_FALSE(vp8_info.layerSync);
+  ++expected_tl0_idx;
+  EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
+
+  // Drop two frames from TL0, thus being coded in TL1.
+  timestamp += kTimestampDelta5Fps;
+  EncodeFrame(timestamp, false, &vp8_info, &flags);
+  // First frame is sync frame.
+  EXPECT_EQ(ScreenshareLayers::kTl1SyncFlags, flags);
   EXPECT_EQ(1, vp8_info.temporalIdx);
   EXPECT_TRUE(vp8_info.layerSync);
   EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
 
-  timestamp += kTimestampDelta30Fps;
-  SetEncodeExpectations(true, false, 30);
+  timestamp += kTimestampDelta5Fps;
   EncodeFrame(timestamp, false, &vp8_info, &flags);
-  EXPECT_EQ(kFlagsTL1, flags);
+  EXPECT_EQ(ScreenshareLayers::kTl1Flags, flags);
   EXPECT_EQ(1, vp8_info.temporalIdx);
   EXPECT_FALSE(vp8_info.layerSync);
   EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
 }
 
 TEST_F(ScreenshareLayerTest, 2LayersPeriodicSync) {
-  layers_.reset(
-      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
-  EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
   int flags = 0;
   uint32_t timestamp = 0;
   CodecSpecificInfoVP8 vp8_info;
-  const int kNumFrames = 10;
-  const bool kDrops[kNumFrames] = {false, true, true, true, true,
-      true, true, true, true, true};
-  const int kExpectedFramerates[kNumFrames] = {1, 5, 5, 5, 5, 5, 5, 5, 5, 5};
-  const bool kExpectedSyncs[kNumFrames] = {false, true, false, false, false,
-      false, false, true, false, false};
-  const int kExpectedTemporalIdx[kNumFrames] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+  std::vector<int> sync_times;
+
+  const int kNumFrames = kSyncPeriodSeconds * kFrameRate * 2 - 1;
   for (int i = 0; i < kNumFrames; ++i) {
     timestamp += kTimestampDelta5Fps;
-    SetEncodeExpectations(kDrops[i], false, kExpectedFramerates[i]);
     EncodeFrame(timestamp, false, &vp8_info, &flags);
-    EXPECT_EQ(kExpectedTemporalIdx[i], vp8_info.temporalIdx);
-    EXPECT_EQ(kExpectedSyncs[i], vp8_info.layerSync) << "Iteration: " << i;
-    EXPECT_EQ(1, vp8_info.tl0PicIdx);
+    if (vp8_info.temporalIdx == 1 && vp8_info.layerSync) {
+      sync_times.push_back(timestamp);
+    }
   }
+
+  ASSERT_EQ(2u, sync_times.size());
+  EXPECT_GE(sync_times[1] - sync_times[0], 90000 * kSyncPeriodSeconds);
+}
+
+TEST_F(ScreenshareLayerTest, 2LayersSyncAfterTimeout) {
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
+  uint32_t timestamp = 0;
+  CodecSpecificInfoVP8 vp8_info;
+  std::vector<int> sync_times;
+
+  const int kNumFrames = kMaxSyncPeriodSeconds * kFrameRate * 2 - 1;
+  for (int i = 0; i < kNumFrames; ++i) {
+    timestamp += kTimestampDelta5Fps;
+    layers_->EncodeFlags(timestamp);
+    layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+
+    // Simulate TL1 being at least 8 qp steps better.
+    if (vp8_info.temporalIdx == 0) {
+      layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+    } else {
+      layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
+    }
+
+    if (vp8_info.temporalIdx == 1 && vp8_info.layerSync)
+      sync_times.push_back(timestamp);
+  }
+
+  ASSERT_EQ(2u, sync_times.size());
+  EXPECT_GE(sync_times[1] - sync_times[0], 90000 * kMaxSyncPeriodSeconds);
+}
+
+TEST_F(ScreenshareLayerTest, 2LayersSyncAfterSimilarQP) {
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
+  uint32_t timestamp = 0;
+  CodecSpecificInfoVP8 vp8_info;
+  std::vector<int> sync_times;
+
+  const int kNumFrames = (kSyncPeriodSeconds +
+                          ((kMaxSyncPeriodSeconds - kSyncPeriodSeconds) / 2)) *
+                         kFrameRate;
+  for (int i = 0; i < kNumFrames; ++i) {
+    timestamp += kTimestampDelta5Fps;
+    layers_->EncodeFlags(timestamp);
+    layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+
+    // Simulate TL1 being at least 8 qp steps better.
+    if (vp8_info.temporalIdx == 0) {
+      layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+    } else {
+      layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
+    }
+
+    if (vp8_info.temporalIdx == 1 && vp8_info.layerSync)
+      sync_times.push_back(timestamp);
+  }
+
+  ASSERT_EQ(1u, sync_times.size());
+
+  bool bumped_tl0_quality = false;
+  for (int i = 0; i < 3; ++i) {
+    timestamp += kTimestampDelta5Fps;
+    int flags = layers_->EncodeFlags(timestamp);
+    layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+
+    if (vp8_info.temporalIdx == 0) {
+      // Bump TL0 to same quality as TL1.
+      layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
+      bumped_tl0_quality = true;
+    } else {
+      layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
+      if (bumped_tl0_quality) {
+        EXPECT_TRUE(vp8_info.layerSync);
+        EXPECT_EQ(ScreenshareLayers::kTl1SyncFlags, flags);
+        return;
+      }
+    }
+  }
+  ADD_FAILURE() << "No TL1 frame arrived within time limit.";
 }
 
 TEST_F(ScreenshareLayerTest, 2LayersToggling) {
-  layers_.reset(
-      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
-  EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
+  int flags = 0;
+  CodecSpecificInfoVP8 vp8_info;
+  uint32_t timestamp = RunGracePeriod();
+
+  // Insert 50 frames. 2/5 should be TL0.
+  int tl0_frames = 0;
+  int tl1_frames = 0;
+  for (int i = 0; i < 50; ++i) {
+    timestamp += kTimestampDelta5Fps;
+    EncodeFrame(timestamp, false, &vp8_info, &flags);
+    switch (vp8_info.temporalIdx) {
+      case 0:
+        ++tl0_frames;
+        break;
+      case 1:
+        ++tl1_frames;
+        break;
+      default:
+        abort();
+    }
+  }
+  EXPECT_EQ(20, tl0_frames);
+  EXPECT_EQ(30, tl1_frames);
+}
+
+TEST_F(ScreenshareLayerTest, AllFitsLayer0) {
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
+  frame_size_ = ((kDefaultTl0BitrateKbps * 1000) / 8) / kFrameRate;
+
   int flags = 0;
   uint32_t timestamp = 0;
   CodecSpecificInfoVP8 vp8_info;
-  const int kNumFrames = 10;
-  const bool kDrops[kNumFrames] = {false, true, false, true, false,
-      true, false, true, false, true};
-  const int kExpectedFramerates[kNumFrames] = {1, 5, 5, 5, 5, 5, 5, 5, 5, 5};
-  const bool kExpectedSyncs[kNumFrames] = {false, true, false, false, false,
-      false, false, true, false, false};
-  const int kExpectedTemporalIdx[kNumFrames] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
-  const int kExpectedTl0Idx[kNumFrames] = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5};
-  for (int i = 0; i < kNumFrames; ++i) {
-    timestamp += kTimestampDelta5Fps;
-    SetEncodeExpectations(kDrops[i], false, kExpectedFramerates[i]);
+  // Insert 50 frames, small enough that all fits in TL0.
+  for (int i = 0; i < 50; ++i) {
     EncodeFrame(timestamp, false, &vp8_info, &flags);
-    EXPECT_EQ(kExpectedTemporalIdx[i], vp8_info.temporalIdx);
-    EXPECT_EQ(kExpectedSyncs[i], vp8_info.layerSync) << "Iteration: " << i;
-    EXPECT_EQ(kExpectedTl0Idx[i], vp8_info.tl0PicIdx);
+    timestamp += kTimestampDelta5Fps;
+    EXPECT_EQ(ScreenshareLayers::kTl0Flags, flags);
+    EXPECT_EQ(0, vp8_info.temporalIdx);
   }
 }
 
-TEST_F(ScreenshareLayerTest, 2LayersBothDrops) {
-  layers_.reset(
-      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
-  EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL));
+TEST_F(ScreenshareLayerTest, TooHighBitrate) {
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
+  frame_size_ = 2 * ((kDefaultTl1BitrateKbps * 1000) / 8) / kFrameRate;
   int flags = 0;
-  uint32_t timestamp = 0;
-  uint8_t expected_tl0_idx = 0;
   CodecSpecificInfoVP8 vp8_info;
-  SetEncodeExpectations(false, false, 1);
-  EncodeFrame(timestamp, false, &vp8_info, &flags);
-  EXPECT_EQ(kFlagsTL0, flags);
-  EXPECT_EQ(0, vp8_info.temporalIdx);
-  EXPECT_FALSE(vp8_info.layerSync);
-  ++expected_tl0_idx;
-  EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
+  uint32_t timestamp = RunGracePeriod();
 
-  timestamp += kTimestampDelta5Fps;
-  SetEncodeExpectations(true, false, 5);
-  EncodeFrame(timestamp, false, &vp8_info, &flags);
-  EXPECT_EQ(kFlagsTL1Sync, flags);
-  EXPECT_EQ(1, vp8_info.temporalIdx);
-  EXPECT_TRUE(vp8_info.layerSync);
-  EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
+  // Insert 100 frames. Half should be dropped.
+  int tl0_frames = 0;
+  int tl1_frames = 0;
+  int dropped_frames = 0;
+  for (int i = 0; i < 100; ++i) {
+    timestamp += kTimestampDelta5Fps;
+    EncodeFrame(timestamp, false, &vp8_info, &flags);
+    if (flags == -1) {
+      ++dropped_frames;
+    } else {
+      switch (vp8_info.temporalIdx) {
+        case 0:
+          ++tl0_frames;
+          break;
+        case 1:
+          ++tl1_frames;
+          break;
+        default:
+          abort();
+      }
+    }
+  }
 
-  timestamp += kTimestampDelta5Fps;
-  SetEncodeExpectations(true, true, 5);
-  flags = layers_->EncodeFlags(timestamp);
-  EXPECT_EQ(-1, flags);
+  EXPECT_EQ(5, tl0_frames);
+  EXPECT_EQ(45, tl1_frames);
+  EXPECT_EQ(50, dropped_frames);
 }
 
 TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) {
-  layers_.reset(
-      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
+  layers_.reset(new ScreenshareLayers(2, 0));
 
   vpx_codec_enc_cfg_t cfg;
   layers_->ConfigureBitrates(100, 1000, 5, &cfg);
@@ -268,8 +371,7 @@
 }
 
 TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) {
-  layers_.reset(
-      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
+  layers_.reset(new ScreenshareLayers(2, 0));
   vpx_codec_enc_cfg_t cfg;
   layers_->ConfigureBitrates(100, 450, 5, &cfg);
 
@@ -279,12 +381,64 @@
 }
 
 TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) {
-  layers_.reset(
-      new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_));
+  layers_.reset(new ScreenshareLayers(2, 0));
   vpx_codec_enc_cfg_t cfg;
   layers_->ConfigureBitrates(100, 100, 5, &cfg);
 
   EXPECT_EQ(100U, cfg.rc_target_bitrate);
 }
 
+TEST_F(ScreenshareLayerTest, EncoderDrop) {
+  layers_.reset(new ScreenshareLayers(2, 0));
+  ConfigureBitrates();
+  CodecSpecificInfoVP8 vp8_info;
+  vpx_codec_enc_cfg_t cfg;
+  cfg.rc_max_quantizer = kDefaultQp;
+
+  uint32_t timestamp = RunGracePeriod();
+  timestamp = SkipUntilTl(0, timestamp);
+
+  // Size 0 indicates dropped frame.
+  layers_->FrameEncoded(0, timestamp, kDefaultQp);
+  timestamp += kTimestampDelta5Fps;
+  EXPECT_FALSE(layers_->UpdateConfiguration(&cfg));
+  EXPECT_EQ(ScreenshareLayers::kTl0Flags, layers_->EncodeFlags(timestamp));
+  layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+
+  timestamp = SkipUntilTl(0, timestamp);
+  EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
+  EXPECT_LT(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+
+  layers_->EncodeFlags(timestamp);
+  timestamp += kTimestampDelta5Fps;
+  EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
+  layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+  EXPECT_EQ(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+
+  // Next drop in TL1.
+
+  timestamp = SkipUntilTl(1, timestamp);
+  layers_->FrameEncoded(0, timestamp, kDefaultQp);
+  timestamp += kTimestampDelta5Fps;
+  EXPECT_FALSE(layers_->UpdateConfiguration(&cfg));
+  EXPECT_EQ(ScreenshareLayers::kTl1Flags, layers_->EncodeFlags(timestamp));
+  layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+
+  timestamp = SkipUntilTl(1, timestamp);
+  EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
+  EXPECT_LT(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+
+  layers_->EncodeFlags(timestamp);
+  timestamp += kTimestampDelta5Fps;
+  EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
+  layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
+  EXPECT_EQ(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
+  layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
index 280296c..ee7fd85 100644
--- a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
@@ -102,10 +102,7 @@
 
   virtual webrtc::TemporalLayers* Create(int num_temporal_layers,
                                          uint8_t initial_tl0_pic_idx) const {
-    return new webrtc::ScreenshareLayers(num_temporal_layers,
-                                         rand(),
-                                         &tl0_frame_dropper_,
-                                         &tl1_frame_dropper_);
+    return new webrtc::ScreenshareLayers(num_temporal_layers, rand());
   }
 
   mutable webrtc::FrameDropper tl0_frame_dropper_;
diff --git a/webrtc/modules/video_coding/codecs/vp8/simulcast_unittest.h b/webrtc/modules/video_coding/codecs/vp8/simulcast_unittest.h
index fffe4ab..2e436a9 100644
--- a/webrtc/modules/video_coding/codecs/vp8/simulcast_unittest.h
+++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_unittest.h
@@ -196,12 +196,16 @@
       layers_->PopulateCodecSpecific(base_layer_sync, vp8_info, timestamp);
     }
 
-    void FrameEncoded(unsigned int size, uint32_t timestamp) override {
-      layers_->FrameEncoded(size, timestamp);
+    void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {
+      layers_->FrameEncoded(size, timestamp, qp);
     }
 
     int CurrentLayerId() const override { return layers_->CurrentLayerId(); }
 
+    bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override {
+      return false;
+    }
+
     int configured_bitrate_;
     TemporalLayers* layers_;
   };
diff --git a/webrtc/modules/video_coding/codecs/vp8/temporal_layers.h b/webrtc/modules/video_coding/codecs/vp8/temporal_layers.h
index b3b73a6..7607210 100644
--- a/webrtc/modules/video_coding/codecs/vp8/temporal_layers.h
+++ b/webrtc/modules/video_coding/codecs/vp8/temporal_layers.h
@@ -47,9 +47,11 @@
                                      CodecSpecificInfoVP8* vp8_info,
                                      uint32_t timestamp) = 0;
 
-  virtual void FrameEncoded(unsigned int size, uint32_t timestamp) = 0;
+  virtual void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) = 0;
 
   virtual int CurrentLayerId() const = 0;
+
+  virtual bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) = 0;
 };
 
 // Factory for a temporal layers strategy that adaptively changes the number of
diff --git a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
index 4370424..05e0799 100644
--- a/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
+++ b/webrtc/modules/video_coding/codecs/vp8/vp8_impl.cc
@@ -278,7 +278,6 @@
       int tl0_bitrate = std::min(codec_.targetBitrate, target_bitrate);
       max_bitrate = std::min(codec_.maxBitrate, target_bitrate);
       target_bitrate = tl0_bitrate;
-      framerate = -1;
     }
     configurations_[i].rc_target_bitrate = target_bitrate;
     temporal_layers_[stream_idx]->ConfigureBitrates(target_bitrate,
@@ -312,10 +311,8 @@
   if (num_streams == 1) {
     if (codec.mode == kScreensharing) {
       // Special mode when screensharing on a single stream.
-      temporal_layers_.push_back(new ScreenshareLayers(num_temporal_layers,
-                                                       rand(),
-                                                       &tl0_frame_dropper_,
-                                                       &tl1_frame_dropper_));
+      temporal_layers_.push_back(
+          new ScreenshareLayers(num_temporal_layers, rand()));
     } else {
       temporal_layers_.push_back(
           tl_factory.Create(num_temporal_layers, rand()));
@@ -670,8 +667,10 @@
                       static_cast<vp8e_token_partitions>(token_partitions_));
     vpx_codec_control(&(encoders_[i]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
                       rc_max_intra_target_);
+    // VP8E_SET_SCREEN_CONTENT_MODE 2 = screen content with more aggressive
+    // rate control (drop frames on large target bitrate overshoot)
     vpx_codec_control(&(encoders_[i]), VP8E_SET_SCREEN_CONTENT_MODE,
-                      codec_.mode == kScreensharing);
+                      codec_.mode == kScreensharing ? 2 : 0);
   }
   inited_ = true;
   return WEBRTC_VIDEO_CODEC_OK;
@@ -698,15 +697,12 @@
                            const std::vector<VideoFrameType>* frame_types) {
   TRACE_EVENT1("webrtc", "VP8::Encode", "timestamp", frame.timestamp());
 
-  if (!inited_) {
+  if (!inited_)
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
-  }
-  if (frame.IsZeroSize()) {
+  if (frame.IsZeroSize())
     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
-  }
-  if (encoded_complete_callback_ == NULL) {
+  if (encoded_complete_callback_ == NULL)
     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
-  }
 
   // Only apply scaling to improve for single-layer streams. The scaling metrics
   // use frame drops as a signal and is only applicable when we drop frames.
@@ -851,6 +847,16 @@
   // whereas |encoder_| is from highest to lowest resolution.
   size_t stream_idx = encoders_.size() - 1;
   for (size_t i = 0; i < encoders_.size(); ++i, --stream_idx) {
+    // Allow the layers adapter to temporarily modify the configuration. This
+    // change isn't stored in configurations_ so change will be discarded at
+    // the next update.
+    vpx_codec_enc_cfg_t temp_config;
+    memcpy(&temp_config, &configurations_[i], sizeof(vpx_codec_enc_cfg_t));
+    if (temporal_layers_[stream_idx]->UpdateConfiguration(&temp_config)) {
+      if (vpx_codec_enc_config_set(&encoders_[i], &temp_config))
+        return WEBRTC_VIDEO_CODEC_ERROR;
+    }
+
     vpx_codec_control(&encoders_[i], VP8E_SET_FRAME_FLAGS, flags[stream_idx]);
     vpx_codec_control(&encoders_[i],
                       VP8E_SET_TEMPORAL_LAYER_ID,
@@ -873,9 +879,8 @@
     vpx_codec_control(&(encoders_[0]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
         rc_max_intra_target_);
   }
-  if (error) {
+  if (error)
     return WEBRTC_VIDEO_CODEC_ERROR;
-  }
   timestamp_ += duration;
   return GetEncodedPartitions(input_image, only_predict_from_key_frame);
 }
@@ -933,6 +938,7 @@
 int VP8EncoderImpl::GetEncodedPartitions(const VideoFrame& input_image,
                                          bool only_predicting_from_key_frame) {
   int stream_idx = static_cast<int>(encoders_.size()) - 1;
+  int result = WEBRTC_VIDEO_CODEC_OK;
   for (size_t encoder_idx = 0; encoder_idx < encoders_.size();
       ++encoder_idx, --stream_idx) {
     vpx_codec_iter_t iter = NULL;
@@ -981,9 +987,12 @@
     encoded_images_[encoder_idx]._timeStamp = input_image.timestamp();
     encoded_images_[encoder_idx].capture_time_ms_ =
         input_image.render_time_ms();
+
+    int qp = -1;
+    vpx_codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER_64, &qp);
     temporal_layers_[stream_idx]->FrameEncoded(
         encoded_images_[encoder_idx]._length,
-        encoded_images_[encoder_idx]._timeStamp);
+        encoded_images_[encoder_idx]._timeStamp, qp);
     if (send_stream_[stream_idx]) {
       if (encoded_images_[encoder_idx]._length > 0) {
         TRACE_COUNTER_ID1("webrtc", "EncodedFrameSize", encoder_idx,
@@ -994,6 +1003,8 @@
             codec_.simulcastStream[stream_idx].width;
         encoded_complete_callback_->Encoded(encoded_images_[encoder_idx],
                                             &codec_specific, &frag_info);
+      } else if (codec_.mode == kScreensharing) {
+        result = WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT;
       }
     } else {
       // Required in case padding is applied to dropped frames.
@@ -1017,7 +1028,7 @@
       quality_scaler_.ReportDroppedFrame();
     }
   }
-  return WEBRTC_VIDEO_CODEC_OK;
+  return result;
 }
 
 int VP8EncoderImpl::SetChannelParameters(uint32_t packetLoss, int64_t rtt) {
diff --git a/webrtc/modules/video_coding/main/source/generic_encoder.cc b/webrtc/modules/video_coding/main/source/generic_encoder.cc
index 1091501..f2cdd59 100644
--- a/webrtc/modules/video_coding/main/source/generic_encoder.cc
+++ b/webrtc/modules/video_coding/main/source/generic_encoder.cc
@@ -60,7 +60,8 @@
       bit_rate_(0),
       frame_rate_(0),
       internal_source_(internalSource),
-      rotation_(kVideoRotation_0) {
+      rotation_(kVideoRotation_0),
+      is_screenshare_(false) {
 }
 
 VCMGenericEncoder::~VCMGenericEncoder()
@@ -90,6 +91,7 @@
       frame_rate_ = settings->maxFramerate;
     }
 
+    is_screenshare_ = settings->mode == VideoCodecMode::kScreensharing;
     if (encoder_->InitEncode(settings, numberOfCores, maxPayloadSize) != 0) {
       LOG(LS_ERROR) << "Failed to initialize the encoder associated with "
                        "payload name: " << settings->plName;
@@ -114,7 +116,15 @@
     vcm_encoded_frame_callback_->SetRotation(rotation_);
   }
 
-  return encoder_->Encode(inputFrame, codecSpecificInfo, &video_frame_types);
+  int32_t result =
+      encoder_->Encode(inputFrame, codecSpecificInfo, &video_frame_types);
+  if (is_screenshare_ &&
+      result == WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT) {
+    // Target bitrate exceeded, encoder state has been reset - try again.
+    return encoder_->Encode(inputFrame, codecSpecificInfo, &video_frame_types);
+  }
+
+  return result;
 }
 
 int32_t
diff --git a/webrtc/modules/video_coding/main/source/generic_encoder.h b/webrtc/modules/video_coding/main/source/generic_encoder.h
index a722772..862c06b 100644
--- a/webrtc/modules/video_coding/main/source/generic_encoder.h
+++ b/webrtc/modules/video_coding/main/source/generic_encoder.h
@@ -149,6 +149,7 @@
     const bool internal_source_;
     mutable rtc::CriticalSection rates_lock_;
     VideoRotation rotation_;
+    bool is_screenshare_;
 }; // end of VCMGenericEncoder class
 
 }  // namespace webrtc
diff --git a/webrtc/video/full_stack.cc b/webrtc/video/full_stack.cc
index 63341b1..f0c5d79 100644
--- a/webrtc/video/full_stack.cc
+++ b/webrtc/video/full_stack.cc
@@ -649,8 +649,8 @@
       {"screenshare_slides", 1850, 1110, 5},
       true,
       50000,
-      100000,
-      1000000,
+      200000,
+      2000000,
       0.0,
       0.0,
       kFullStackTestDurationSecs};
diff --git a/webrtc/video/screenshare_loopback.cc b/webrtc/video/screenshare_loopback.cc
index c0f96d4..f2133a2 100644
--- a/webrtc/video/screenshare_loopback.cc
+++ b/webrtc/video/screenshare_loopback.cc
@@ -45,12 +45,12 @@
   return static_cast<size_t>(FLAGS_min_bitrate);
 }
 
-DEFINE_int32(tl0_bitrate, 100, "Temporal layer 0 target bitrate.");
+DEFINE_int32(tl0_bitrate, 200, "Temporal layer 0 target bitrate.");
 size_t StartBitrate() {
   return static_cast<size_t>(FLAGS_tl0_bitrate);
 }
 
-DEFINE_int32(tl1_bitrate, 1000, "Temporal layer 1 target bitrate.");
+DEFINE_int32(tl1_bitrate, 2000, "Temporal layer 1 target bitrate.");
 size_t MaxBitrate() {
   return static_cast<size_t>(FLAGS_tl1_bitrate);
 }