| Index: webrtc/video/receive_statistics_proxy.cc
 | 
| diff --git a/webrtc/video/receive_statistics_proxy.cc b/webrtc/video/receive_statistics_proxy.cc
 | 
| index ded510a55e77dafee3fa9f217c3311d2224daf32..3a51ef994291ab9dd65749af8d88999888da9394 100644
 | 
| --- a/webrtc/video/receive_statistics_proxy.cc
 | 
| +++ b/webrtc/video/receive_statistics_proxy.cc
 | 
| @@ -13,6 +13,7 @@
 | 
|  #include <cmath>
 | 
|  
 | 
|  #include "webrtc/base/checks.h"
 | 
| +#include "webrtc/base/logging.h"
 | 
|  #include "webrtc/modules/video_coding/include/video_codec_interface.h"
 | 
|  #include "webrtc/system_wrappers/include/clock.h"
 | 
|  #include "webrtc/system_wrappers/include/metrics.h"
 | 
| @@ -21,6 +22,17 @@ namespace webrtc {
 | 
|  namespace {
 | 
|  // Periodic time interval for processing samples for |freq_offset_counter_|.
 | 
|  const int64_t kFreqOffsetProcessIntervalMs = 40000;
 | 
| +
 | 
| +// Configuration for bad call detection.
 | 
| +const int kNumMeasurements = 10;
 | 
| +const float kBadFraction = 0.8f;
 | 
| +const int kLowFpsThreshold = 12;
 | 
| +const int kHighFpsThreshold = 14;
 | 
| +const int kLowQpThreshold = 60;
 | 
| +const int kHighQpThreshold = 70;
 | 
| +const int kNumMeasurementsVariance = kNumMeasurements * 1.5;
 | 
| +const int kLowVarianceThreshold = 1;
 | 
| +const int kHighVarianceThreshold = 2;
 | 
|  }  // namespace
 | 
|  
 | 
|  ReceiveStatisticsProxy::ReceiveStatisticsProxy(
 | 
| @@ -30,6 +42,19 @@ ReceiveStatisticsProxy::ReceiveStatisticsProxy(
 | 
|        config_(*config),
 | 
|        start_ms_(clock->TimeInMilliseconds()),
 | 
|        // 1000ms window, scale 1000 for ms to s.
 | 
| +      last_sample_time_(clock->TimeInMilliseconds()),
 | 
| +      fps_threshold_(kLowFpsThreshold,
 | 
| +                     kHighFpsThreshold,
 | 
| +                     kBadFraction,
 | 
| +                     kNumMeasurements),
 | 
| +      qp_threshold_(kLowQpThreshold,
 | 
| +                    kHighQpThreshold,
 | 
| +                    kBadFraction,
 | 
| +                    kNumMeasurements),
 | 
| +      variance_threshold_(kLowVarianceThreshold,
 | 
| +                          kHighVarianceThreshold,
 | 
| +                          kBadFraction,
 | 
| +                          kNumMeasurementsVariance),
 | 
|        decode_fps_estimator_(1000, 1000),
 | 
|        renders_fps_estimator_(1000, 1000),
 | 
|        render_fps_tracker_(100, 10u),
 | 
| @@ -160,6 +185,64 @@ void ReceiveStatisticsProxy::UpdateHistograms() {
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +void ReceiveStatisticsProxy::QualitySample() {
 | 
| +  uint64_t now = clock_->TimeInMilliseconds();
 | 
| +
 | 
| +  double fps =
 | 
| +      render_fps_tracker_.ComputeRateForInterval(now - last_sample_time_);
 | 
| +  int qp = qp_sample_.Avg(1);
 | 
| +
 | 
| +  bool prev_fps_bad = !fps_threshold_.IsHigh().value_or(true);
 | 
| +  bool prev_qp_bad = qp_threshold_.IsHigh().value_or(false);
 | 
| +  bool prev_variance_bad = variance_threshold_.IsHigh().value_or(false);
 | 
| +  bool prev_any_bad = prev_fps_bad || prev_qp_bad || prev_variance_bad;
 | 
| +
 | 
| +  fps_threshold_.AddMeasurement(static_cast<int>(fps));
 | 
| +  qp_threshold_.AddMeasurement(qp);
 | 
| +  rtc::Optional<double> fps_variance_opt = fps_threshold_.CalculateVariance();
 | 
| +  double fps_variance = fps_variance_opt.value_or(0);
 | 
| +  if (fps_variance_opt) {
 | 
| +    variance_threshold_.AddMeasurement(static_cast<int>(fps_variance));
 | 
| +  }
 | 
| +
 | 
| +  bool fps_bad = !fps_threshold_.IsHigh().value_or(true);
 | 
| +  bool qp_bad = qp_threshold_.IsHigh().value_or(false);
 | 
| +  bool variance_bad = variance_threshold_.IsHigh().value_or(false);
 | 
| +  bool any_bad = fps_bad || qp_bad || variance_bad;
 | 
| +
 | 
| +  if (!prev_any_bad && any_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (any) start: " << now;
 | 
| +  } else if (prev_any_bad && !any_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (any) end: " << now;
 | 
| +  }
 | 
| +
 | 
| +  if (!prev_fps_bad && fps_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (fps) start: " << now;
 | 
| +  } else if (prev_fps_bad && !fps_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (fps) end: " << now;
 | 
| +  }
 | 
| +
 | 
| +  if (!prev_qp_bad && qp_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (qp) start: " << now;
 | 
| +  } else if (prev_qp_bad && !qp_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (qp) end: " << now;
 | 
| +  }
 | 
| +
 | 
| +  if (!prev_variance_bad && variance_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (variance) start: " << now;
 | 
| +  } else if (prev_variance_bad && !variance_bad) {
 | 
| +    LOG(LS_WARNING) << "Bad call (variance) end: " << now;
 | 
| +  }
 | 
| +
 | 
| +  LOG(LS_INFO) << "SAMPLE: sample_length: " << (now - last_sample_time_)
 | 
| +               << " fps: " << fps << " fps_bad: " << fps_bad << " qp: " << qp
 | 
| +               << " qp_bad: " << qp_bad << " variance_bad: " << variance_bad
 | 
| +               << " fps_variance: " << fps_variance;
 | 
| +
 | 
| +  last_sample_time_ = now;
 | 
| +  qp_sample_.Reset();
 | 
| +}
 | 
| +
 | 
|  VideoReceiveStream::Stats ReceiveStatisticsProxy::GetStats() const {
 | 
|    rtc::CritScope lock(&crit_);
 | 
|    return stats_;
 | 
| @@ -178,6 +261,7 @@ void ReceiveStatisticsProxy::OnDecoderImplementationName(
 | 
|  void ReceiveStatisticsProxy::OnIncomingRate(unsigned int framerate,
 | 
|                                              unsigned int bitrate_bps) {
 | 
|    rtc::CritScope lock(&crit_);
 | 
| +  QualitySample();
 | 
|    stats_.network_frame_rate = framerate;
 | 
|    stats_.total_bitrate_bps = bitrate_bps;
 | 
|  }
 | 
| @@ -324,6 +408,8 @@ void ReceiveStatisticsProxy::OnPreDecode(
 | 
|    }
 | 
|    if (codec_specific_info->codecType == kVideoCodecVP8) {
 | 
|      qp_counters_.vp8.Add(encoded_image.qp_);
 | 
| +    rtc::CritScope lock(&crit_);
 | 
| +    qp_sample_.Add(encoded_image.qp_);
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -338,4 +424,9 @@ int ReceiveStatisticsProxy::SampleCounter::Avg(int min_required_samples) const {
 | 
|    return sum / num_samples;
 | 
|  }
 | 
|  
 | 
| +void ReceiveStatisticsProxy::SampleCounter::Reset() {
 | 
| +  num_samples = 0;
 | 
| +  sum = 0;
 | 
| +}
 | 
| +
 | 
|  }  // namespace webrtc
 | 
| 
 |