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