| Index: webrtc/modules/video_coding/utility/quality_scaler.cc | 
| diff --git a/webrtc/modules/video_coding/utility/quality_scaler.cc b/webrtc/modules/video_coding/utility/quality_scaler.cc | 
| index c509e843642f2237e94fe1727749479a9523faa7..99bc6dad223bdaa58fcfcb56da6d451cf0ab0ad4 100644 | 
| --- a/webrtc/modules/video_coding/utility/quality_scaler.cc | 
| +++ b/webrtc/modules/video_coding/utility/quality_scaler.cc | 
| @@ -10,10 +10,12 @@ | 
|  | 
| #include "webrtc/modules/video_coding/utility/quality_scaler.h" | 
|  | 
| +#include <algorithm> | 
| +#include <cmath> | 
| + | 
| namespace webrtc { | 
|  | 
| namespace { | 
| -static const int kMinFps = 5; | 
| // Threshold constant used until first downscale (to permit fast rampup). | 
| static const int kMeasureSecondsFastUpscale = 2; | 
| static const int kMeasureSecondsUpscale = 5; | 
| @@ -46,7 +48,11 @@ const int QualityScaler::kLowH264QpThreshold = 24; | 
| const int QualityScaler::kBadH264QpThreshold = 37; | 
| #endif | 
|  | 
| -QualityScaler::QualityScaler() : low_qp_threshold_(-1) {} | 
| +// Default values. Should immediately get set to something more sensible. | 
| +QualityScaler::QualityScaler() | 
| +    : average_qp_(kMeasureSecondsUpscale * 30), | 
| +      framedrop_percent_(kMeasureSecondsUpscale * 30), | 
| +      low_qp_threshold_(-1) {} | 
|  | 
| void QualityScaler::Init(int low_qp_threshold, | 
| int high_qp_threshold, | 
| @@ -54,14 +60,15 @@ void QualityScaler::Init(int low_qp_threshold, | 
| int width, | 
| int height, | 
| int fps) { | 
| -  ClearSamples(); | 
| low_qp_threshold_ = low_qp_threshold; | 
| high_qp_threshold_ = high_qp_threshold; | 
| downscale_shift_ = 0; | 
| -  // Use a faster window for upscaling initially (but be more graceful later). | 
| -  // This enables faster initial rampups without risking strong up-down | 
| -  // behavior later. | 
| -  measure_seconds_upscale_ = kMeasureSecondsFastUpscale; | 
| + | 
| +  fast_rampup_ = true; | 
| + | 
| +  ClearSamples(); | 
| +  ReportFramerate(fps); | 
| + | 
| const int init_width = width; | 
| const int init_height = height; | 
| if (initial_bitrate_kbps > 0) { | 
| @@ -76,24 +83,28 @@ void QualityScaler::Init(int low_qp_threshold, | 
| height /= 2; | 
| } | 
| } | 
| - | 
| -  // Zero out width/height so they can be checked against inside | 
| -  // UpdateTargetResolution. | 
| -  res_.width = res_.height = 0; | 
| UpdateTargetResolution(init_width, init_height); | 
| ReportFramerate(fps); | 
| } | 
|  | 
| // Report framerate(fps) to estimate # of samples. | 
| void QualityScaler::ReportFramerate(int framerate) { | 
| -  framerate_ = framerate; | 
| -  UpdateSampleCounts(); | 
| +  // Use a faster window for upscaling initially. | 
| +  // This enables faster initial rampups without risking strong up-down | 
| +  // behavior later. | 
| +  num_samples_upscale_ = framerate * (fast_rampup_ ? kMeasureSecondsFastUpscale | 
| +                                                   : kMeasureSecondsUpscale); | 
| +  num_samples_downscale_ = framerate * kMeasureSecondsDownscale; | 
| + | 
| +  average_qp_ = | 
| +      MovingAverage(std::max(num_samples_upscale_, num_samples_downscale_)); | 
| +  framedrop_percent_ = | 
| +      MovingAverage(std::max(num_samples_upscale_, num_samples_downscale_)); | 
| } | 
|  | 
| void QualityScaler::ReportQP(int qp) { | 
| framedrop_percent_.AddSample(0); | 
| -  average_qp_downscale_.AddSample(qp); | 
| -  average_qp_upscale_.AddSample(qp); | 
| +  average_qp_.AddSample(qp); | 
| } | 
|  | 
| void QualityScaler::ReportDroppedFrame() { | 
| @@ -103,34 +114,58 @@ void QualityScaler::ReportDroppedFrame() { | 
| void QualityScaler::OnEncodeFrame(int width, int height) { | 
| // Should be set through InitEncode -> Should be set by now. | 
| RTC_DCHECK_GE(low_qp_threshold_, 0); | 
| -  RTC_DCHECK_GT(num_samples_upscale_, 0u); | 
| -  RTC_DCHECK_GT(num_samples_downscale_, 0u); | 
| - | 
| -  // Update scale factor. | 
| -  int avg_drop = 0; | 
| -  int avg_qp = 0; | 
| - | 
| -  if ((framedrop_percent_.GetAverage(num_samples_downscale_, &avg_drop) && | 
| -       avg_drop >= kFramedropPercentThreshold) || | 
| -      (average_qp_downscale_.GetAverage(num_samples_downscale_, &avg_qp) && | 
| -       avg_qp > high_qp_threshold_)) { | 
| -    AdjustScale(false); | 
| -  } else if (average_qp_upscale_.GetAverage(num_samples_upscale_, &avg_qp) && | 
| -             avg_qp <= low_qp_threshold_) { | 
| -    AdjustScale(true); | 
| +  if (target_res_.width != width || target_res_.height != height) { | 
| +    UpdateTargetResolution(width, height); | 
| +  } | 
| + | 
| +  // Check if we should scale down due to high frame drop. | 
| +  const auto drop_rate = framedrop_percent_.GetAverage(num_samples_downscale_); | 
| +  if (drop_rate && *drop_rate >= kFramedropPercentThreshold) { | 
| +    ScaleDown(); | 
| +    return; | 
| +  } | 
| + | 
| +  // Check if we should scale up or down based on QP. | 
| +  const auto avg_qp_down = average_qp_.GetAverage(num_samples_downscale_); | 
| +  if (avg_qp_down && *avg_qp_down > high_qp_threshold_) { | 
| +    ScaleDown(); | 
| +    return; | 
| +  } | 
| +  const auto avg_qp_up = average_qp_.GetAverage(num_samples_upscale_); | 
| +  if (avg_qp_up && *avg_qp_up <= low_qp_threshold_) { | 
| +    // QP has been low. We want to try a higher resolution. | 
| +    ScaleUp(); | 
| +    return; | 
| +  } | 
| +} | 
| + | 
| +void QualityScaler::ScaleUp() { | 
| +  downscale_shift_ = std::max(0, downscale_shift_ - 1); | 
| +  ClearSamples(); | 
| +} | 
| + | 
| +void QualityScaler::ScaleDown() { | 
| +  downscale_shift_ = std::min(maximum_shift_, downscale_shift_ + 1); | 
| +  ClearSamples(); | 
| +  // If we've scaled down, wait longer before scaling up again. | 
| +  if (fast_rampup_) { | 
| +    fast_rampup_ = false; | 
| +    num_samples_upscale_ = (num_samples_upscale_ / kMeasureSecondsFastUpscale) * | 
| +                           kMeasureSecondsUpscale; | 
| } | 
| -  UpdateTargetResolution(width, height); | 
| } | 
|  | 
| QualityScaler::Resolution QualityScaler::GetScaledResolution() const { | 
| -  return res_; | 
| +  const int frame_width = target_res_.width >> downscale_shift_; | 
| +  const int frame_height = target_res_.height >> downscale_shift_; | 
| +  return Resolution{frame_width, frame_height}; | 
| } | 
|  | 
| rtc::scoped_refptr<VideoFrameBuffer> QualityScaler::GetScaledBuffer( | 
| const rtc::scoped_refptr<VideoFrameBuffer>& frame) { | 
| Resolution res = GetScaledResolution(); | 
| -  int src_width = frame->width(); | 
| -  int src_height = frame->height(); | 
| +  const int src_width = frame->width(); | 
| +  const int src_height = frame->height(); | 
|  | 
| if (res.width == src_width && res.height == src_height) | 
| return frame; | 
| @@ -142,50 +177,20 @@ rtc::scoped_refptr<VideoFrameBuffer> QualityScaler::GetScaledBuffer( | 
| return scaled_buffer; | 
| } | 
|  | 
| -void QualityScaler::UpdateTargetResolution(int frame_width, int frame_height) { | 
| -  RTC_DCHECK_GE(downscale_shift_, 0); | 
| -  int shifts_performed = 0; | 
| -  for (int shift = downscale_shift_; | 
| -       shift > 0 && (frame_width / 2 >= kMinDownscaleDimension) && | 
| -       (frame_height / 2 >= kMinDownscaleDimension); | 
| -       --shift, ++shifts_performed) { | 
| -    frame_width /= 2; | 
| -    frame_height /= 2; | 
| -  } | 
| -  // Clamp to number of shifts actually performed to not be stuck trying to | 
| -  // scale way beyond QVGA. | 
| -  downscale_shift_ = shifts_performed; | 
| -  if (res_.width == frame_width && res_.height == frame_height) { | 
| -    // No reset done/needed, using same resolution. | 
| -    return; | 
| +void QualityScaler::UpdateTargetResolution(int width, int height) { | 
| +  if (width < kMinDownscaleDimension || height < kMinDownscaleDimension) { | 
| +    maximum_shift_ = 0; | 
| +  } else { | 
| +    maximum_shift_ = static_cast<int>( | 
| +        std::log2(std::min(width, height) / kMinDownscaleDimension)); | 
| } | 
| -  res_.width = frame_width; | 
| -  res_.height = frame_height; | 
| -  ClearSamples(); | 
| +  target_res_ = Resolution{width, height}; | 
| } | 
|  | 
| void QualityScaler::ClearSamples() { | 
| framedrop_percent_.Reset(); | 
| -  average_qp_downscale_.Reset(); | 
| -  average_qp_upscale_.Reset(); | 
| +  average_qp_.Reset(); | 
| } | 
|  | 
| -void QualityScaler::UpdateSampleCounts() { | 
| -  num_samples_downscale_ = static_cast<size_t>( | 
| -      kMeasureSecondsDownscale * (framerate_ < kMinFps ? kMinFps : framerate_)); | 
| -  num_samples_upscale_ = static_cast<size_t>( | 
| -      measure_seconds_upscale_ * (framerate_ < kMinFps ? kMinFps : framerate_)); | 
| -} | 
| - | 
| -void QualityScaler::AdjustScale(bool up) { | 
| -  downscale_shift_ += up ? -1 : 1; | 
| -  if (downscale_shift_ < 0) | 
| -    downscale_shift_ = 0; | 
| -  if (!up) { | 
| -    // First downscale hit, start using a slower threshold for going up. | 
| -    measure_seconds_upscale_ = kMeasureSecondsUpscale; | 
| -    UpdateSampleCounts(); | 
| -  } | 
| -} | 
|  | 
| }  // namespace webrtc | 
|  |