Add additional metric (relative standard deviation of encode time) for overuse detection.
This code is currently only for testing.

BUG=1577
R=stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6381 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/video_engine/include/vie_base.h b/webrtc/video_engine/include/vie_base.h
index 47fc0a7..486eca6 100644
--- a/webrtc/video_engine/include/vie_base.h
+++ b/webrtc/video_engine/include/vie_base.h
@@ -63,16 +63,18 @@
 
 struct CpuOveruseOptions {
   CpuOveruseOptions()
-    : enable_capture_jitter_method(true),
-      low_capture_jitter_threshold_ms(kNormalUseStdDevMs),
-      high_capture_jitter_threshold_ms(kOveruseStdDevMs),
-      enable_encode_usage_method(false),
-      low_encode_usage_threshold_percent(60),
-      high_encode_usage_threshold_percent(90),
-      frame_timeout_interval_ms(1500),
-      min_frame_samples(120),
-      min_process_count(3),
-      high_threshold_consecutive_count(2) {}
+      : enable_capture_jitter_method(true),
+        low_capture_jitter_threshold_ms(kNormalUseStdDevMs),
+        high_capture_jitter_threshold_ms(kOveruseStdDevMs),
+        enable_encode_usage_method(false),
+        low_encode_usage_threshold_percent(60),
+        high_encode_usage_threshold_percent(90),
+        low_encode_time_rsd_threshold(0),
+        high_encode_time_rsd_threshold(0),
+        frame_timeout_interval_ms(1500),
+        min_frame_samples(120),
+        min_process_count(3),
+        high_threshold_consecutive_count(2) {}
 
   // Method based on inter-arrival jitter of captured frames.
   bool enable_capture_jitter_method;
@@ -82,6 +84,8 @@
   bool enable_encode_usage_method;
   int low_encode_usage_threshold_percent;  // Threshold for triggering underuse.
   int high_encode_usage_threshold_percent; // Threshold for triggering overuse.
+  int low_encode_time_rsd_threshold;   // Threshold for triggering underuse.
+  int high_encode_time_rsd_threshold;  // Threshold for triggering overuse.
   // General settings.
   int frame_timeout_interval_ms;  // The maximum allowed interval between two
                                   // frames before resetting estimations.
@@ -102,6 +106,8 @@
         o.low_encode_usage_threshold_percent &&
         high_encode_usage_threshold_percent ==
         o.high_encode_usage_threshold_percent &&
+        low_encode_time_rsd_threshold == o.low_encode_time_rsd_threshold &&
+        high_encode_time_rsd_threshold == o.high_encode_time_rsd_threshold &&
         frame_timeout_interval_ms == o.frame_timeout_interval_ms &&
         min_frame_samples == o.min_frame_samples &&
         min_process_count == o.min_process_count &&
@@ -111,16 +117,18 @@
 
 struct CpuOveruseMetrics {
   CpuOveruseMetrics()
-    : capture_jitter_ms(-1),
-      avg_encode_time_ms(-1),
-      encode_usage_percent(-1),
-      capture_queue_delay_ms_per_s(-1) {}
+      : capture_jitter_ms(-1),
+        avg_encode_time_ms(-1),
+        encode_usage_percent(-1),
+        encode_rsd(-1),
+        capture_queue_delay_ms_per_s(-1) {}
 
   int capture_jitter_ms;  // The current estimated jitter in ms based on
                           // incoming captured frames.
   int avg_encode_time_ms;   // The average encode time in ms.
   int encode_usage_percent; // The average encode time divided by the average
                             // time difference between incoming captured frames.
+  int encode_rsd;           // The relative std dev of encode time of frames.
   int capture_queue_delay_ms_per_s;  // The current time delay between an
                                      // incoming captured frame until the frame
                                      // is being processed. The delay is
diff --git a/webrtc/video_engine/overuse_frame_detector.cc b/webrtc/video_engine/overuse_frame_detector.cc
index c136130..4a95103 100644
--- a/webrtc/video_engine/overuse_frame_detector.cc
+++ b/webrtc/video_engine/overuse_frame_detector.cc
@@ -15,6 +15,7 @@
 
 #include <algorithm>
 #include <list>
+#include <map>
 
 #include "webrtc/modules/video_coding/utility/include/exp_filter.h"
 #include "webrtc/system_wrappers/interface/clock.h"
@@ -206,6 +207,111 @@
   scoped_ptr<VCMExpFilter> filtered_frame_diff_ms_;
 };
 
+// Class for calculating the relative standard deviation of encode times.
+class OveruseFrameDetector::EncodeTimeRsd {
+ public:
+  EncodeTimeRsd(Clock* clock)
+      : kWeightFactor(0.6f),
+        count_(0),
+        filtered_rsd_(new VCMExpFilter(kWeightFactor)),
+        hist_samples_(0),
+        hist_sum_(0.0f),
+        last_process_time_ms_(clock->TimeInMilliseconds()) {
+    Reset();
+  }
+  ~EncodeTimeRsd() {}
+
+  void SetOptions(const CpuOveruseOptions& options) {
+    options_ = options;
+  }
+
+  void Reset() {
+    count_ = 0;
+    filtered_rsd_->Reset(kWeightFactor);
+    filtered_rsd_->Apply(1.0f, InitialValue());
+    hist_.clear();
+    hist_samples_ = 0;
+    hist_sum_ = 0.0f;
+  }
+
+  void AddEncodeSample(float encode_time_ms) {
+    int bin = static_cast<int>(encode_time_ms + 0.5f);
+    if (bin <= 0) {
+      // The frame was probably not encoded, skip possible dropped frame.
+      return;
+    }
+    ++count_;
+    ++hist_[bin];
+    ++hist_samples_;
+    hist_sum_ += bin;
+  }
+
+  void Process(int64_t now) {
+    if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) {
+      // Have not received min number of frames since last reset.
+      return;
+    }
+    const int kMinHistSamples = 20;
+    if (hist_samples_ < kMinHistSamples) {
+      return;
+    }
+    const int64_t kMinDiffSinceLastProcessMs = 1000;
+    int64_t diff_last_process_ms = now - last_process_time_ms_;
+    if (now - last_process_time_ms_ <= kMinDiffSinceLastProcessMs) {
+      return;
+    }
+    last_process_time_ms_ = now;
+
+    // Calculate variance (using samples above the mean).
+    // Checks for a larger encode time of some frames while there is a small
+    // increase in the average time.
+    int mean = hist_sum_ / hist_samples_;
+    float variance = 0.0f;
+    int total_count = 0;
+    for (std::map<int,int>::iterator it = hist_.begin();
+         it != hist_.end(); ++it) {
+      int time = it->first;
+      int count = it->second;
+      if (time > mean) {
+        total_count += count;
+        for (int i = 0; i < count; ++i) {
+          variance += ((time - mean) * (time - mean));
+        }
+      }
+    }
+    variance /= std::max(total_count, 1);
+    float cov = sqrt(variance) / mean;
+
+    hist_.clear();
+    hist_samples_ = 0;
+    hist_sum_ = 0.0f;
+
+    float exp = static_cast<float>(diff_last_process_ms) / kProcessIntervalMs;
+    exp = std::min(exp, kMaxExp);
+    filtered_rsd_->Apply(exp, 100.0f * cov);
+  }
+
+  int Value() const {
+    return static_cast<int>(filtered_rsd_->Value() + 0.5);
+  }
+
+  float InitialValue() const {
+    // Start in between the underuse and overuse threshold.
+    return (options_.low_encode_time_rsd_threshold +
+            options_.high_encode_time_rsd_threshold) / 2.0f;
+  }
+
+ private:
+  const float kWeightFactor;
+  uint32_t count_;  // Number of encode samples since last reset.
+  CpuOveruseOptions options_;
+  scoped_ptr<VCMExpFilter> filtered_rsd_;
+  int hist_samples_;
+  float hist_sum_;
+  std::map<int,int> hist_;  // Histogram of encode time of frames.
+  int64_t last_process_time_ms_;
+};
+
 // Class for calculating the capture queue delay change.
 class OveruseFrameDetector::CaptureQueueDelay {
  public:
@@ -278,6 +384,7 @@
       num_pixels_(0),
       last_encode_sample_ms_(0),
       encode_time_(new EncodeTimeAvg()),
+      encode_rsd_(new EncodeTimeRsd(clock)),
       encode_usage_(new EncodeUsage()),
       capture_queue_delay_(new CaptureQueueDelay()) {
 }
@@ -299,6 +406,7 @@
   options_ = options;
   capture_deltas_.SetOptions(options);
   encode_usage_->SetOptions(options);
+  encode_rsd_->SetOptions(options);
   ResetAll(num_pixels_);
 }
 
@@ -312,6 +420,7 @@
   CriticalSectionScoped cs(crit_.get());
   metrics->capture_jitter_ms = static_cast<int>(capture_deltas_.StdDev() + 0.5);
   metrics->avg_encode_time_ms = encode_time_->filtered_encode_time_ms();
+  metrics->encode_rsd = encode_rsd_->Value();
   metrics->encode_usage_percent = encode_usage_->UsageInPercent();
   metrics->capture_queue_delay_ms_per_s =
       capture_queue_delay_->filtered_delay_ms_per_s();
@@ -340,6 +449,7 @@
   num_pixels_ = num_pixels;
   capture_deltas_.Reset();
   encode_usage_->Reset();
+  encode_rsd_->Reset();
   capture_queue_delay_->ClearFrames();
   last_capture_time_ = 0;
   num_process_times_ = 0;
@@ -374,6 +484,7 @@
     int64_t diff_ms = time - last_encode_sample_ms_;
     encode_time_->AddEncodeSample(encode_time_ms, diff_ms);
     encode_usage_->AddEncodeSample(encode_time_ms, diff_ms);
+    encode_rsd_->AddEncodeSample(encode_time_ms);
   }
   last_encode_sample_ms_ = time;
 }
@@ -391,6 +502,7 @@
   next_process_time_ = now + kProcessIntervalMs;
   ++num_process_times_;
 
+  encode_rsd_->Process(now);
   capture_queue_delay_->CalculateDelayChange(diff_ms);
 
   if (num_process_times_ <= options_.min_process_count) {
@@ -430,11 +542,11 @@
 
   int rampup_delay =
       in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
-  LOG(LS_VERBOSE) << "Capture input stats: avg: " << capture_deltas_.Mean()
-                  << " std_dev " << capture_deltas_.StdDev()
-                  << " rampup delay " << rampup_delay
-                  << " overuse >= " << options_.high_capture_jitter_threshold_ms
-                  << " underuse < " << options_.low_capture_jitter_threshold_ms;
+  LOG(LS_INFO) << " Frame stats: capture avg: " << capture_deltas_.Mean()
+               << " capture stddev " << capture_deltas_.StdDev()
+               << " encode usage " << encode_usage_->UsageInPercent()
+               << " encode rsd " << encode_rsd_->Value()
+               << " rampup delay " << rampup_delay;
   return 0;
 }
 
diff --git a/webrtc/video_engine/overuse_frame_detector.h b/webrtc/video_engine/overuse_frame_detector.h
index 38b927b..ef6d6ab 100644
--- a/webrtc/video_engine/overuse_frame_detector.h
+++ b/webrtc/video_engine/overuse_frame_detector.h
@@ -95,6 +95,7 @@
 
  private:
   class EncodeTimeAvg;
+  class EncodeTimeRsd;
   class EncodeUsage;
   class CaptureQueueDelay;
 
@@ -133,6 +134,7 @@
 
   int64_t last_encode_sample_ms_;
   scoped_ptr<EncodeTimeAvg> encode_time_;
+  scoped_ptr<EncodeTimeRsd> encode_rsd_;
   scoped_ptr<EncodeUsage> encode_usage_;
 
   scoped_ptr<CaptureQueueDelay> capture_queue_delay_;