Implemented AutoMuter in MediaOptimization

Also added a unittest. This is the first step towards creating an
AutoMuter function in WebRTC.

R=mflodman@webrtc.org, stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@4857 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/modules.gyp b/modules/modules.gyp
index 5dab6f7..faa1ead 100644
--- a/modules/modules.gyp
+++ b/modules/modules.gyp
@@ -204,6 +204,7 @@
             'video_coding/main/interface/mock/mock_vcm_callbacks.h',
             'video_coding/main/source/decoding_state_unittest.cc',
             'video_coding/main/source/jitter_buffer_unittest.cc',
+            'video_coding/main/source/media_optimization_unittest.cc',
             'video_coding/main/source/receiver_unittest.cc',
             'video_coding/main/source/session_info_unittest.cc',
             'video_coding/main/source/timing_unittest.cc',
diff --git a/modules/video_coding/main/source/media_optimization.cc b/modules/video_coding/main/source/media_optimization.cc
index bc78f6d..815be36 100644
--- a/modules/video_coding/main/source/media_optimization.cc
+++ b/modules/video_coding/main/source/media_optimization.cc
@@ -46,7 +46,11 @@
       qm_resolution_(new VCMQmResolution()),
       last_qm_update_time_(0),
       last_change_time_(0),
-      num_layers_(0) {
+      num_layers_(0),
+      muting_enabled_(false),
+      video_muted_(false),
+      muter_threshold_bps_(0),
+      muter_window_bps_(0) {
   memset(send_statistics_, 0, sizeof(send_statistics_));
   memset(incoming_frame_times_, -1, sizeof(incoming_frame_times_));
 }
@@ -189,6 +193,8 @@
     content_->ResetShortTermAvgData();
   }
 
+  CheckAutoMuteConditions();
+
   return target_bit_rate_;
 }
 
@@ -345,7 +351,9 @@
 bool MediaOptimization::DropFrame() {
   // Leak appropriate number of bytes.
   frame_dropper_->Leak((uint32_t)(InputFrameRate() + 0.5f));
-
+  if (video_muted_) {
+    return true;  // Drop all frames when muted.
+  }
   return frame_dropper_->DropFrame();
 }
 
@@ -410,6 +418,19 @@
   return VCM_OK;
 }
 
+void MediaOptimization::EnableAutoMuting(int threshold_bps, int window_bps) {
+  assert(threshold_bps > 0 && window_bps >= 0);
+  muter_threshold_bps_ = threshold_bps;
+  muter_window_bps_ = window_bps;
+  muting_enabled_ = true;
+  video_muted_ = false;
+}
+
+void MediaOptimization::DisableAutoMuting() {
+  muting_enabled_ = false;
+  video_muted_ = false;
+}
+
 // Private methods below this line.
 
 int MediaOptimization::UpdateProtectionCallback(
@@ -584,5 +605,23 @@
   }
 }
 
+void MediaOptimization::CheckAutoMuteConditions() {
+  // Check conditions for AutoMute. |target_bit_rate_| is in bps.
+  if (muting_enabled_) {
+    if (!video_muted_) {
+      // Check if we just went below the threshold.
+      if (target_bit_rate_ < muter_threshold_bps_) {
+        video_muted_ = true;
+      }
+    } else {
+      // Video is already muted. Check if we just went over the threshold
+      // with a margin.
+      if (target_bit_rate_ > muter_threshold_bps_ + muter_window_bps_) {
+        video_muted_ = false;
+      }
+    }
+  }
+}
+
 }  // namespace media_optimization
 }  // namespace webrtc
diff --git a/modules/video_coding/main/source/media_optimization.h b/modules/video_coding/main/source/media_optimization.h
index 5ad4f59..ca383bf 100644
--- a/modules/video_coding/main/source/media_optimization.h
+++ b/modules/video_coding/main/source/media_optimization.h
@@ -120,9 +120,18 @@
   // Computes new Quality Mode.
   int32_t SelectQuality();
 
+  // Enables AutoMuter to turn off video when the rate drops below
+  // |threshold_bps|, and turns back on when the rate goes back up above
+  // |threshold_bps| + |window_bps|.
+  void EnableAutoMuting(int threshold_bps, int window_bps);
+
+  // Disables AutoMuter.
+  void DisableAutoMuting();
+
   // Accessors and mutators.
   int32_t max_bit_rate() const { return max_bit_rate_; }
   void set_max_payload_size(int32_t mtu) { max_payload_size_ = mtu; }
+  bool video_muted() const { return video_muted_; }
 
  private:
   typedef std::list<EncodedFrameSample> FrameSampleList;
@@ -152,6 +161,11 @@
 
   void ProcessIncomingFrameRate(int64_t now);
 
+  // Checks conditions for AutoMute. The method compares |target_bit_rate_|
+  // with the threshold values for AutoMute, and changes the state of
+  // |video_muted_| accordingly.
+  void CheckAutoMuteConditions();
+
   int32_t id_;
   Clock* clock_;
   int32_t max_bit_rate_;
@@ -165,7 +179,7 @@
   uint32_t send_statistics_[4];
   uint32_t send_statistics_zero_encode_;
   int32_t max_payload_size_;
-  uint32_t target_bit_rate_;
+  int target_bit_rate_;
   float incoming_frame_rate_;
   int64_t incoming_frame_times_[kFrameCountHistorySize];
   bool enable_qm_;
@@ -181,6 +195,10 @@
   int64_t last_qm_update_time_;
   int64_t last_change_time_;  // Content/user triggered.
   int num_layers_;
+  bool muting_enabled_;
+  bool video_muted_;
+  int muter_threshold_bps_;
+  int muter_window_bps_;
 };  // End of MediaOptimization class declaration.
 
 }  // namespace media_optimization
diff --git a/modules/video_coding/main/source/media_optimization_unittest.cc b/modules/video_coding/main/source/media_optimization_unittest.cc
new file mode 100644
index 0000000..fc4fd75
--- /dev/null
+++ b/modules/video_coding/main/source/media_optimization_unittest.cc
@@ -0,0 +1,114 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/video_coding/main/source/media_optimization.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+
+namespace webrtc {
+namespace media_optimization {
+
+class TestMediaOptimization : public ::testing::Test {
+ protected:
+  enum {
+    kId = 4711  // Id number for the MediaOptimization class.
+  };
+  enum {
+    kSampleRate = 90000  // RTP timestamps per second.
+  };
+
+  // Note: simulated clock starts at 1 seconds, since parts of webrtc use 0 as
+  // a special case (e.g. frame rate in media optimization).
+  TestMediaOptimization()
+      : clock_(1000),
+        media_opt_(kId, &clock_),
+        frame_time_ms_(33),
+        next_timestamp_(0) {}
+
+  // This method mimics what happens in VideoSender::AddVideoFrame.
+  void AddFrameAndAdvanceTime(int bitrate_bps, bool expect_frame_drop) {
+    ASSERT_GE(bitrate_bps, 0);
+    media_opt_.UpdateIncomingFrameRate();
+    bool frame_dropped = media_opt_.DropFrame();
+    EXPECT_EQ(expect_frame_drop, frame_dropped);
+    if (!frame_dropped) {
+      int bytes_per_frame = bitrate_bps * frame_time_ms_ / (8 * 1000);
+      ASSERT_EQ(VCM_OK, media_opt_.UpdateWithEncodedData(
+          bytes_per_frame, next_timestamp_, kVideoFrameDelta));
+    }
+    next_timestamp_ += frame_time_ms_ * kSampleRate / 1000;
+    clock_.AdvanceTimeMilliseconds(frame_time_ms_);
+  }
+
+  SimulatedClock clock_;
+  MediaOptimization media_opt_;
+  int frame_time_ms_;
+  uint32_t next_timestamp_;
+};
+
+
+TEST_F(TestMediaOptimization, VerifyMuting) {
+  // Enable video muter with these limits.
+  // Mute the video when the rate is below 50 kbps and unmute when it gets above
+  // 50 + 10 kbps again.
+  const int kThresholdBps = 50000;
+  const int kWindowBps = 10000;
+  media_opt_.EnableAutoMuting(kThresholdBps, kWindowBps);
+
+  // The video should not be muted from the start.
+  EXPECT_FALSE(media_opt_.video_muted());
+
+  int target_bitrate_kbps = 100;
+  media_opt_.SetTargetRates(target_bitrate_kbps * 1000,
+                            0,  // Lossrate.
+                            100);  // RTT in ms.
+  media_opt_.EnableFrameDropper(true);
+  for (int time = 0; time < 2000; time += frame_time_ms_) {
+    ASSERT_NO_FATAL_FAILURE(AddFrameAndAdvanceTime(target_bitrate_kbps, false));
+  }
+
+  // Set the target rate below the limit for muting.
+  media_opt_.SetTargetRates(kThresholdBps - 1000,
+                            0,  // Lossrate.
+                            100);  // RTT in ms.
+  // Expect the muter to engage immediately and stay muted.
+  // Test during 2 seconds.
+  for (int time = 0; time < 2000; time += frame_time_ms_) {
+    EXPECT_TRUE(media_opt_.video_muted());
+    ASSERT_NO_FATAL_FAILURE(AddFrameAndAdvanceTime(target_bitrate_kbps, true));
+  }
+
+  // Set the target above the limit for muting, but not above the
+  // limit + window.
+  media_opt_.SetTargetRates(kThresholdBps + 1000,
+                            0,  // Lossrate.
+                            100);  // RTT in ms.
+  // Expect the muter to stay muted.
+  // Test during 2 seconds.
+  for (int time = 0; time < 2000; time += frame_time_ms_) {
+    EXPECT_TRUE(media_opt_.video_muted());
+    ASSERT_NO_FATAL_FAILURE(AddFrameAndAdvanceTime(target_bitrate_kbps, true));
+  }
+
+  // Set the target above limit + window.
+  media_opt_.SetTargetRates(kThresholdBps + kWindowBps + 1000,
+                            0,  // Lossrate.
+                            100);  // RTT in ms.
+  // Expect the muter to disengage immediately.
+  // Test during 2 seconds.
+  for (int time = 0; time < 2000; time += frame_time_ms_) {
+    EXPECT_FALSE(media_opt_.video_muted());
+    ASSERT_NO_FATAL_FAILURE(
+        AddFrameAndAdvanceTime((kThresholdBps + kWindowBps) / 1000, false));
+  }
+}
+
+}  // namespace media_optimization
+}  // namespace webrtc