| Index: webrtc/modules/congestion_controller/delay_based_bwe.cc
 | 
| diff --git a/webrtc/modules/congestion_controller/delay_based_bwe.cc b/webrtc/modules/congestion_controller/delay_based_bwe.cc
 | 
| index 04dadc489c0afac2a17e88510e2ea4397b5e9f4b..bb63f353daf31f3fd3d413a3ae0fc39c93bf5f86 100644
 | 
| --- a/webrtc/modules/congestion_controller/delay_based_bwe.cc
 | 
| +++ b/webrtc/modules/congestion_controller/delay_based_bwe.cc
 | 
| @@ -21,6 +21,7 @@
 | 
|  #include "webrtc/modules/congestion_controller/include/congestion_controller.h"
 | 
|  #include "webrtc/modules/pacing/paced_sender.h"
 | 
|  #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
 | 
| +#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
 | 
|  #include "webrtc/system_wrappers/include/field_trial.h"
 | 
|  #include "webrtc/system_wrappers/include/metrics.h"
 | 
|  #include "webrtc/typedefs.h"
 | 
| @@ -39,12 +40,18 @@ constexpr uint32_t kFixedSsrc = 0;
 | 
|  constexpr int kInitialRateWindowMs = 500;
 | 
|  constexpr int kRateWindowMs = 150;
 | 
|  
 | 
| +// Parameters for linear least squares fit of regression line to noisy data.
 | 
|  constexpr size_t kDefaultTrendlineWindowSize = 15;
 | 
|  constexpr double kDefaultTrendlineSmoothingCoeff = 0.9;
 | 
|  constexpr double kDefaultTrendlineThresholdGain = 4.0;
 | 
|  
 | 
| +// Parameters for Theil-Sen robust fitting of line to noisy data.
 | 
| +constexpr size_t kDefaultMedianSlopeWindowSize = 20;
 | 
| +constexpr double kDefaultMedianSlopeThresholdGain = 4.0;
 | 
| +
 | 
|  const char kBitrateEstimateExperiment[] = "WebRTC-ImprovedBitrateEstimate";
 | 
|  const char kBweTrendlineFilterExperiment[] = "WebRTC-BweTrendlineFilter";
 | 
| +const char kBweMedianSlopeFilterExperiment[] = "WebRTC-BweMedianSlopeFilter";
 | 
|  
 | 
|  bool BitrateEstimateExperimentIsEnabled() {
 | 
|    return webrtc::field_trial::FindFullName(kBitrateEstimateExperiment) ==
 | 
| @@ -58,16 +65,27 @@ bool TrendlineFilterExperimentIsEnabled() {
 | 
|    return experiment_string.find("Enabled") == 0;
 | 
|  }
 | 
|  
 | 
| -bool ReadTrendlineFilterExperimentParameters(size_t* window_points,
 | 
| +bool MedianSlopeFilterExperimentIsEnabled() {
 | 
| +  std::string experiment_string =
 | 
| +      webrtc::field_trial::FindFullName(kBweMedianSlopeFilterExperiment);
 | 
| +  // The experiment is enabled iff the field trial string begins with "Enabled".
 | 
| +  return experiment_string.find("Enabled") == 0;
 | 
| +}
 | 
| +
 | 
| +bool ReadTrendlineFilterExperimentParameters(size_t* window_size,
 | 
|                                               double* smoothing_coef,
 | 
|                                               double* threshold_gain) {
 | 
|    RTC_DCHECK(TrendlineFilterExperimentIsEnabled());
 | 
| +  RTC_DCHECK(!MedianSlopeFilterExperimentIsEnabled());
 | 
| +  RTC_DCHECK(window_size != nullptr);
 | 
| +  RTC_DCHECK(smoothing_coef != nullptr);
 | 
| +  RTC_DCHECK(threshold_gain != nullptr);
 | 
|    std::string experiment_string =
 | 
|        webrtc::field_trial::FindFullName(kBweTrendlineFilterExperiment);
 | 
|    int parsed_values = sscanf(experiment_string.c_str(), "Enabled-%zu,%lf,%lf",
 | 
| -                             window_points, smoothing_coef, threshold_gain);
 | 
| +                             window_size, smoothing_coef, threshold_gain);
 | 
|    if (parsed_values == 3) {
 | 
| -    RTC_CHECK_GT(*window_points, 1) << "Need at least 2 points to fit a line.";
 | 
| +    RTC_CHECK_GT(*window_size, 1) << "Need at least 2 points to fit a line.";
 | 
|      RTC_CHECK(0 <= *smoothing_coef && *smoothing_coef <= 1)
 | 
|          << "Coefficient needs to be between 0 and 1 for weighted average.";
 | 
|      RTC_CHECK_GT(*threshold_gain, 0) << "Threshold gain needs to be positive.";
 | 
| @@ -75,12 +93,33 @@ bool ReadTrendlineFilterExperimentParameters(size_t* window_points,
 | 
|    }
 | 
|    LOG(LS_WARNING) << "Failed to parse parameters for BweTrendlineFilter "
 | 
|                       "experiment from field trial string. Using default.";
 | 
| -  *window_points = kDefaultTrendlineWindowSize;
 | 
| +  *window_size = kDefaultTrendlineWindowSize;
 | 
|    *smoothing_coef = kDefaultTrendlineSmoothingCoeff;
 | 
|    *threshold_gain = kDefaultTrendlineThresholdGain;
 | 
|    return false;
 | 
|  }
 | 
|  
 | 
| +bool ReadMedianSlopeFilterExperimentParameters(size_t* window_size,
 | 
| +                                               double* threshold_gain) {
 | 
| +  RTC_DCHECK(!TrendlineFilterExperimentIsEnabled());
 | 
| +  RTC_DCHECK(MedianSlopeFilterExperimentIsEnabled());
 | 
| +  RTC_DCHECK(window_size != nullptr);
 | 
| +  RTC_DCHECK(threshold_gain != nullptr);
 | 
| +  std::string experiment_string =
 | 
| +      webrtc::field_trial::FindFullName(kBweMedianSlopeFilterExperiment);
 | 
| +  int parsed_values = sscanf(experiment_string.c_str(), "Enabled-%zu,%lf",
 | 
| +                             window_size, threshold_gain);
 | 
| +  if (parsed_values == 2) {
 | 
| +    RTC_CHECK_GT(*window_size, 1) << "Need at least 2 points to fit a line.";
 | 
| +    RTC_CHECK_GT(*threshold_gain, 0) << "Threshold gain needs to be positive.";
 | 
| +    return true;
 | 
| +  }
 | 
| +  LOG(LS_WARNING) << "Failed to parse parameters for BweMedianSlopeFilter "
 | 
| +                     "experiment from field trial string. Using default.";
 | 
| +  *window_size = kDefaultMedianSlopeWindowSize;
 | 
| +  *threshold_gain = kDefaultMedianSlopeThresholdGain;
 | 
| +  return false;
 | 
| +}
 | 
|  }  // namespace
 | 
|  
 | 
|  namespace webrtc {
 | 
| @@ -168,7 +207,9 @@ rtc::Optional<uint32_t> DelayBasedBwe::BitrateEstimator::bitrate_bps() const {
 | 
|  }
 | 
|  
 | 
|  DelayBasedBwe::DelayBasedBwe(Clock* clock)
 | 
| -    : clock_(clock),
 | 
| +    : in_trendline_experiment_(TrendlineFilterExperimentIsEnabled()),
 | 
| +      in_median_slope_experiment_(MedianSlopeFilterExperimentIsEnabled()),
 | 
| +      clock_(clock),
 | 
|        inter_arrival_(),
 | 
|        kalman_estimator_(),
 | 
|        trendline_estimator_(),
 | 
| @@ -180,13 +221,19 @@ DelayBasedBwe::DelayBasedBwe(Clock* clock)
 | 
|        trendline_window_size_(kDefaultTrendlineWindowSize),
 | 
|        trendline_smoothing_coeff_(kDefaultTrendlineSmoothingCoeff),
 | 
|        trendline_threshold_gain_(kDefaultTrendlineThresholdGain),
 | 
| -      in_trendline_experiment_(TrendlineFilterExperimentIsEnabled()),
 | 
| -      probing_interval_estimator_(&rate_control_) {
 | 
| +      probing_interval_estimator_(&rate_control_),
 | 
| +      median_slope_window_size_(kDefaultMedianSlopeWindowSize),
 | 
| +      median_slope_threshold_gain_(kDefaultMedianSlopeThresholdGain) {
 | 
|    if (in_trendline_experiment_) {
 | 
|      ReadTrendlineFilterExperimentParameters(&trendline_window_size_,
 | 
|                                              &trendline_smoothing_coeff_,
 | 
|                                              &trendline_threshold_gain_);
 | 
|    }
 | 
| +  if (in_median_slope_experiment_) {
 | 
| +    ReadMedianSlopeFilterExperimentParameters(&trendline_window_size_,
 | 
| +                                              &trendline_threshold_gain_);
 | 
| +  }
 | 
| +
 | 
|    network_thread_.DetachFromThread();
 | 
|  }
 | 
|  
 | 
| @@ -224,6 +271,8 @@ DelayBasedBwe::Result DelayBasedBwe::IncomingPacketInfo(
 | 
|      trendline_estimator_.reset(new TrendlineEstimator(
 | 
|          trendline_window_size_, trendline_smoothing_coeff_,
 | 
|          trendline_threshold_gain_));
 | 
| +    median_slope_estimator_.reset(new MedianSlopeEstimator(
 | 
| +        median_slope_window_size_, median_slope_threshold_gain_));
 | 
|    }
 | 
|    last_seen_packet_ms_ = now_ms;
 | 
|  
 | 
| @@ -249,7 +298,12 @@ DelayBasedBwe::Result DelayBasedBwe::IncomingPacketInfo(
 | 
|        detector_.Detect(trendline_estimator_->trendline_slope(), ts_delta_ms,
 | 
|                         trendline_estimator_->num_of_deltas(),
 | 
|                         info.arrival_time_ms);
 | 
| -
 | 
| +    } else if (in_median_slope_experiment_) {
 | 
| +      median_slope_estimator_->Update(t_delta, ts_delta_ms,
 | 
| +                                      info.arrival_time_ms);
 | 
| +      detector_.Detect(median_slope_estimator_->trendline_slope(), ts_delta_ms,
 | 
| +                       median_slope_estimator_->num_of_deltas(),
 | 
| +                       info.arrival_time_ms);
 | 
|      } else {
 | 
|        kalman_estimator_->Update(t_delta, ts_delta_ms, size_delta,
 | 
|                                  detector_.State(), info.arrival_time_ms);
 | 
| @@ -288,8 +342,11 @@ DelayBasedBwe::Result DelayBasedBwe::IncomingPacketInfo(
 | 
|          UpdateEstimate(info.arrival_time_ms, now_ms, acked_bitrate_bps,
 | 
|                         &result.target_bitrate_bps);
 | 
|    }
 | 
| -  if (result.updated)
 | 
| +  if (result.updated) {
 | 
|      last_update_ms_ = now_ms;
 | 
| +    BWE_TEST_LOGGING_PLOT(1, "target_bitrate_bps", now_ms,
 | 
| +                          result.target_bitrate_bps);
 | 
| +  }
 | 
|  
 | 
|    return result;
 | 
|  }
 | 
| 
 |