Add configuration for ability to use the encode usage measure for triggering overuse/underuse.

BUG=1577
R=mflodman@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5767 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/video_engine/include/vie_base.h b/webrtc/video_engine/include/vie_base.h
index 64c53ec..a08cbac 100644
--- a/webrtc/video_engine/include/vie_base.h
+++ b/webrtc/video_engine/include/vie_base.h
@@ -66,6 +66,9 @@
     : 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),
@@ -75,6 +78,10 @@
   bool enable_capture_jitter_method;
   float low_capture_jitter_threshold_ms;  // Threshold for triggering underuse.
   float high_capture_jitter_threshold_ms; // Threshold for triggering overuse.
+  // Method based on encode time of frames.
+  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.
   // General settings.
   int frame_timeout_interval_ms;  // The maximum allowed interval between two
                                   // frames before resetting estimations.
@@ -90,6 +97,11 @@
         low_capture_jitter_threshold_ms == o.low_capture_jitter_threshold_ms &&
         high_capture_jitter_threshold_ms ==
         o.high_capture_jitter_threshold_ms &&
+        enable_encode_usage_method == o.enable_encode_usage_method &&
+        low_encode_usage_threshold_percent ==
+        o.low_encode_usage_threshold_percent &&
+        high_encode_usage_threshold_percent ==
+        o.high_encode_usage_threshold_percent &&
         frame_timeout_interval_ms == o.frame_timeout_interval_ms &&
         min_frame_samples == o.min_frame_samples &&
         min_process_count == o.min_process_count &&
diff --git a/webrtc/video_engine/overuse_frame_detector.cc b/webrtc/video_engine/overuse_frame_detector.cc
index bc283e2..bfecbb5 100644
--- a/webrtc/video_engine/overuse_frame_detector.cc
+++ b/webrtc/video_engine/overuse_frame_detector.cc
@@ -140,13 +140,27 @@
   EncodeUsage()
       : kWeightFactorFrameDiff(0.998f),
         kWeightFactorEncodeTime(0.995f),
+        kInitialSampleDiffMs(50.0f),
+        kMaxSampleDiffMs(66.0f),
+        count_(0),
         filtered_encode_time_ms_(new VCMExpFilter(kWeightFactorEncodeTime)),
         filtered_frame_diff_ms_(new VCMExpFilter(kWeightFactorFrameDiff)) {
-    filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs);
-    filtered_frame_diff_ms_->Apply(1.0f, kSampleDiffMs);
+    Reset();
   }
   ~EncodeUsage() {}
 
+  void SetOptions(const CpuOveruseOptions& options) {
+    options_ = options;
+  }
+
+  void Reset() {
+    count_ = 0;
+    filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff);
+    filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs);
+    filtered_encode_time_ms_->Reset(kWeightFactorEncodeTime);
+    filtered_encode_time_ms_->Apply(1.0f, InitialEncodeTimeMs());
+  }
+
   void AddSample(float sample_ms) {
     float exp = sample_ms / kSampleDiffMs;
     exp = std::min(exp, kMaxExp);
@@ -154,21 +168,40 @@
   }
 
   void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) {
+    ++count_;
     float exp = diff_last_sample_ms / kSampleDiffMs;
     exp = std::min(exp, kMaxExp);
     filtered_encode_time_ms_->Apply(exp, encode_time_ms);
   }
 
   int UsageInPercent() const {
+    if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) {
+      return static_cast<int>(InitialUsageInPercent() + 0.5f);
+    }
     float frame_diff_ms = std::max(filtered_frame_diff_ms_->Value(), 1.0f);
+    frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs);
     float encode_usage_percent =
         100.0f * filtered_encode_time_ms_->Value() / frame_diff_ms;
     return static_cast<int>(encode_usage_percent + 0.5);
   }
 
+  float InitialUsageInPercent() const {
+    // Start in between the underuse and overuse threshold.
+    return (options_.low_encode_usage_threshold_percent +
+            options_.high_encode_usage_threshold_percent) / 2.0f;
+  }
+
+  float InitialEncodeTimeMs() const {
+    return InitialUsageInPercent() * kInitialSampleDiffMs / 100;
+  }
+
  private:
   const float kWeightFactorFrameDiff;
   const float kWeightFactorEncodeTime;
+  const float kInitialSampleDiffMs;
+  const float kMaxSampleDiffMs;
+  uint64_t count_;
+  CpuOveruseOptions options_;
   scoped_ptr<VCMExpFilter> filtered_encode_time_ms_;
   scoped_ptr<VCMExpFilter> filtered_frame_diff_ms_;
 };
@@ -265,6 +298,7 @@
   }
   options_ = options;
   capture_deltas_.SetOptions(options);
+  encode_usage_->SetOptions(options);
   ResetAll(num_pixels_);
 }
 
@@ -315,6 +349,7 @@
 void OveruseFrameDetector::ResetAll(int num_pixels) {
   num_pixels_ = num_pixels;
   capture_deltas_.Reset();
+  encode_usage_->Reset();
   capture_queue_delay_->ClearFrames();
   last_capture_time_ = 0;
   num_process_times_ = 0;
@@ -420,8 +455,15 @@
 }
 
 bool OveruseFrameDetector::IsOverusing() {
-  bool overusing = options_.enable_capture_jitter_method &&
-      (capture_deltas_.StdDev() >= options_.high_capture_jitter_threshold_ms);
+  bool overusing = false;
+  if (options_.enable_capture_jitter_method) {
+    overusing = capture_deltas_.StdDev() >=
+        options_.high_capture_jitter_threshold_ms;
+  } else if (options_.enable_encode_usage_method) {
+    overusing = encode_usage_->UsageInPercent() >=
+        options_.high_encode_usage_threshold_percent;
+  }
+
   if (overusing) {
     ++checks_above_threshold_;
   } else {
@@ -435,8 +477,14 @@
   if (time_now < last_rampup_time_ + delay)
     return false;
 
-  bool underusing = options_.enable_capture_jitter_method &&
-      (capture_deltas_.StdDev() < options_.low_capture_jitter_threshold_ms);
+  bool underusing = false;
+  if (options_.enable_capture_jitter_method) {
+    underusing = capture_deltas_.StdDev() <
+        options_.low_capture_jitter_threshold_ms;
+  } else if (options_.enable_encode_usage_method) {
+    underusing = encode_usage_->UsageInPercent() <
+        options_.low_encode_usage_threshold_percent;
+  }
   return underusing;
 }
 }  // namespace webrtc
diff --git a/webrtc/video_engine/overuse_frame_detector_unittest.cc b/webrtc/video_engine/overuse_frame_detector_unittest.cc
index 5d9cedb..a760fbf 100644
--- a/webrtc/video_engine/overuse_frame_detector_unittest.cc
+++ b/webrtc/video_engine/overuse_frame_detector_unittest.cc
@@ -66,6 +66,11 @@
              options_.high_capture_jitter_threshold_ms) / 2.0f) + 0.5;
   }
 
+  int InitialEncodeUsage() {
+    return ((options_.low_encode_usage_threshold_percent +
+             options_.high_encode_usage_threshold_percent) / 2.0f) + 0.5;
+  }
+
   void InsertFramesWithInterval(
       size_t num_frames, int interval_ms, int width, int height) {
     while (num_frames-- > 0) {
@@ -74,6 +79,16 @@
     }
   }
 
+  void InsertAndEncodeFramesWithInterval(
+      int num_frames, int interval_ms, int width, int height, int encode_ms) {
+    while (num_frames-- > 0) {
+      overuse_detector_->FrameCaptured(width, height);
+      clock_->AdvanceTimeMilliseconds(encode_ms);
+      overuse_detector_->FrameEncoded(encode_ms);
+      clock_->AdvanceTimeMilliseconds(interval_ms - encode_ms);
+    }
+  }
+
   void TriggerOveruse(int num_times) {
     for (int i = 0; i < num_times; ++i) {
       InsertFramesWithInterval(200, kFrameInterval33ms, kWidth, kHeight);
@@ -87,6 +102,22 @@
     overuse_detector_->Process();
   }
 
+  void TriggerOveruseWithEncodeUsage(int num_times) {
+    const int kEncodeTimeMs = 32;
+    for (int i = 0; i < num_times; ++i) {
+      InsertAndEncodeFramesWithInterval(
+          1000, kFrameInterval33ms, kWidth, kHeight, kEncodeTimeMs);
+      overuse_detector_->Process();
+    }
+  }
+
+  void TriggerNormalUsageWithEncodeUsage() {
+    const int kEncodeTimeMs = 5;
+    InsertAndEncodeFramesWithInterval(
+        1000, kFrameInterval33ms, kWidth, kHeight, kEncodeTimeMs);
+    overuse_detector_->Process();
+  }
+
   CpuOveruseOptions options_;
   scoped_ptr<SimulatedClock> clock_;
   scoped_ptr<MockCpuOveruseObserver> observer_;
@@ -266,13 +297,42 @@
   EXPECT_EQ(2, overuse_detector_->AvgEncodeTimeMs());
 }
 
+TEST_F(OveruseFrameDetectorTest, InitialEncodeUsage) {
+  EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent());
+}
+
 TEST_F(OveruseFrameDetectorTest, EncodedUsage) {
-  for (int i = 0; i < 30; i++) {
-    overuse_detector_->FrameCaptured(kWidth, kHeight);
-    clock_->AdvanceTimeMilliseconds(5);
-    overuse_detector_->FrameEncoded(5);
-    clock_->AdvanceTimeMilliseconds(33-5);
-  }
+  const int kEncodeTimeMs = 5;
+  InsertAndEncodeFramesWithInterval(
+      1000, kFrameInterval33ms, kWidth, kHeight, kEncodeTimeMs);
   EXPECT_EQ(15, overuse_detector_->EncodeUsagePercent());
 }
+
+TEST_F(OveruseFrameDetectorTest, EncodeUsageResetAfterChangingThreshold) {
+  EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent());
+  options_.high_encode_usage_threshold_percent = 100;
+  overuse_detector_->SetOptions(options_);
+  EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent());
+  options_.low_encode_usage_threshold_percent = 20;
+  overuse_detector_->SetOptions(options_);
+  EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent());
+}
+
+TEST_F(OveruseFrameDetectorTest, TriggerOveruseWithEncodeUsage) {
+  options_.enable_capture_jitter_method = false;
+  options_.enable_encode_usage_method = true;
+  overuse_detector_->SetOptions(options_);
+  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
+  TriggerOveruseWithEncodeUsage(options_.high_threshold_consecutive_count);
+}
+
+TEST_F(OveruseFrameDetectorTest, OveruseAndRecoverWithEncodeUsage) {
+  options_.enable_capture_jitter_method = false;
+  options_.enable_encode_usage_method = true;
+  overuse_detector_->SetOptions(options_);
+  EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
+  TriggerOveruseWithEncodeUsage(options_.high_threshold_consecutive_count);
+  EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1));
+  TriggerNormalUsageWithEncodeUsage();
+}
 }  // namespace webrtc