diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 5dab6f7cd..faa1ead7f 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/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/webrtc/modules/video_coding/main/source/media_optimization.cc b/webrtc/modules/video_coding/main/source/media_optimization.cc index bc78f6d3f..815be364d 100644 --- a/webrtc/modules/video_coding/main/source/media_optimization.cc +++ b/webrtc/modules/video_coding/main/source/media_optimization.cc @@ -46,7 +46,11 @@ MediaOptimization::MediaOptimization(int32_t id, Clock* clock) 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 @@ uint32_t MediaOptimization::SetTargetRates(uint32_t target_bitrate, content_->ResetShortTermAvgData(); } + CheckAutoMuteConditions(); + return target_bit_rate_; } @@ -345,7 +351,9 @@ void MediaOptimization::EnableFrameDropper(bool enable) { 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 @@ int32_t MediaOptimization::SelectQuality() { 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::ProcessIncomingFrameRate(int64_t now) { } } +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/webrtc/modules/video_coding/main/source/media_optimization.h b/webrtc/modules/video_coding/main/source/media_optimization.h index 5ad4f597b..ca383bf55 100644 --- a/webrtc/modules/video_coding/main/source/media_optimization.h +++ b/webrtc/modules/video_coding/main/source/media_optimization.h @@ -120,9 +120,18 @@ class MediaOptimization { // 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 FrameSampleList; @@ -152,6 +161,11 @@ class MediaOptimization { 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 @@ class MediaOptimization { 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 @@ class MediaOptimization { 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/webrtc/modules/video_coding/main/source/media_optimization_unittest.cc b/webrtc/modules/video_coding/main/source/media_optimization_unittest.cc new file mode 100644 index 000000000..fc4fd7529 --- /dev/null +++ b/webrtc/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