Suppress REMB in bitrate ctrl if it seems lika a short network glitch.
BUG=4082
R=stefan@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/37369004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@7948 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/bitrate_controller/BUILD.gn b/webrtc/modules/bitrate_controller/BUILD.gn
index 96a3782..f0c60a8 100644
--- a/webrtc/modules/bitrate_controller/BUILD.gn
+++ b/webrtc/modules/bitrate_controller/BUILD.gn
@@ -13,6 +13,8 @@
"bitrate_controller_impl.cc",
"bitrate_controller_impl.h",
"include/bitrate_controller.h",
+ "remb_suppressor.cc",
+ "remb_suppressor.h",
"send_side_bandwidth_estimation.cc",
"send_side_bandwidth_estimation.h",
]
diff --git a/webrtc/modules/bitrate_controller/bitrate_controller.gypi b/webrtc/modules/bitrate_controller/bitrate_controller.gypi
index e713282..71dbc1f 100644
--- a/webrtc/modules/bitrate_controller/bitrate_controller.gypi
+++ b/webrtc/modules/bitrate_controller/bitrate_controller.gypi
@@ -18,6 +18,8 @@
'bitrate_controller_impl.cc',
'bitrate_controller_impl.h',
'include/bitrate_controller.h',
+ 'remb_suppressor.cc',
+ 'remb_suppressor.h',
'send_side_bandwidth_estimation.cc',
'send_side_bandwidth_estimation.h',
],
diff --git a/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc b/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc
index 5e1fd46..605190d 100644
--- a/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc
+++ b/webrtc/modules/bitrate_controller/bitrate_controller_impl.cc
@@ -83,7 +83,8 @@
return new BitrateControllerImpl(clock, enforce_min_bitrate);
}
-BitrateControllerImpl::BitrateControllerImpl(Clock* clock, bool enforce_min_bitrate)
+BitrateControllerImpl::BitrateControllerImpl(Clock* clock,
+ bool enforce_min_bitrate)
: clock_(clock),
last_bitrate_update_ms_(clock_->TimeInMilliseconds()),
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
@@ -96,7 +97,9 @@
last_rtt_ms_(0),
last_enforce_min_bitrate_(!enforce_min_bitrate_),
bitrate_observers_modified_(false),
- last_reserved_bitrate_bps_(0) {}
+ last_reserved_bitrate_bps_(0),
+ remb_suppressor_(new RembSuppressor(clock)) {
+}
BitrateControllerImpl::~BitrateControllerImpl() {
BitrateObserverConfList::iterator it = bitrate_observers_.begin();
@@ -219,6 +222,9 @@
void BitrateControllerImpl::OnReceivedEstimatedBitrate(uint32_t bitrate) {
CriticalSectionScoped cs(critsect_);
+ if (remb_suppressor_->SuppresNewRemb(bitrate)) {
+ return;
+ }
bandwidth_estimation_.UpdateReceiverEstimate(bitrate);
MaybeTriggerOnNetworkChanged();
}
@@ -378,4 +384,14 @@
return false;
}
+void BitrateControllerImpl::SetBitrateSent(uint32_t bitrate_sent_bps) {
+ CriticalSectionScoped cs(critsect_);
+ remb_suppressor_->SetBitrateSent(bitrate_sent_bps);
+}
+
+void BitrateControllerImpl::SetCodecMode(webrtc::VideoCodecMode mode) {
+ CriticalSectionScoped cs(critsect_);
+ remb_suppressor_->SetEnabled(mode == kScreensharing);
+}
+
} // namespace webrtc
diff --git a/webrtc/modules/bitrate_controller/bitrate_controller_impl.h b/webrtc/modules/bitrate_controller/bitrate_controller_impl.h
index 3d16202..fb2622f 100644
--- a/webrtc/modules/bitrate_controller/bitrate_controller_impl.h
+++ b/webrtc/modules/bitrate_controller/bitrate_controller_impl.h
@@ -21,6 +21,7 @@
#include <map>
#include <utility>
+#include "webrtc/modules/bitrate_controller/remb_suppressor.h"
#include "webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
@@ -49,6 +50,11 @@
virtual int64_t TimeUntilNextProcess() OVERRIDE;
virtual int32_t Process() OVERRIDE;
+ // Current bitrate actually being sent.
+ virtual void SetBitrateSent(uint32_t bitrate_sent_bps) OVERRIDE;
+
+ virtual void SetCodecMode(webrtc::VideoCodecMode mode) OVERRIDE;
+
private:
class RtcpBandwidthObserverImpl;
@@ -127,6 +133,7 @@
bool last_enforce_min_bitrate_ GUARDED_BY(*critsect_);
bool bitrate_observers_modified_ GUARDED_BY(*critsect_);
uint32_t last_reserved_bitrate_bps_ GUARDED_BY(*critsect_);
+ scoped_ptr<RembSuppressor> remb_suppressor_ GUARDED_BY(*critsect_);
DISALLOW_IMPLICIT_CONSTRUCTORS(BitrateControllerImpl);
};
diff --git a/webrtc/modules/bitrate_controller/include/bitrate_controller.h b/webrtc/modules/bitrate_controller/include/bitrate_controller.h
index 8a558f5..2b84ada 100644
--- a/webrtc/modules/bitrate_controller/include/bitrate_controller.h
+++ b/webrtc/modules/bitrate_controller/include/bitrate_controller.h
@@ -77,6 +77,10 @@
virtual void EnforceMinBitrate(bool enforce_min_bitrate) = 0;
virtual void SetReservedBitrate(uint32_t reserved_bitrate_bps) = 0;
+
+ virtual void SetBitrateSent(uint32_t bitrate_sent_bps) = 0;
+
+ virtual void SetCodecMode(webrtc::VideoCodecMode mode) = 0;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_BITRATE_CONTROLLER_INCLUDE_BITRATE_CONTROLLER_H_
diff --git a/webrtc/modules/bitrate_controller/remb_suppressor.cc b/webrtc/modules/bitrate_controller/remb_suppressor.cc
new file mode 100644
index 0000000..f65837a
--- /dev/null
+++ b/webrtc/modules/bitrate_controller/remb_suppressor.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2014 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 "webrtc/modules/bitrate_controller/remb_suppressor.h"
+
+#include <math.h>
+
+#include "webrtc/system_wrappers/interface/field_trial.h"
+
+namespace webrtc {
+
+// If BWE falls more than this fraction from one REMB to the next,
+// classify this as a glitch.
+static const double kMaxBweDropRatio = 0.45;
+
+// If we are sending less then this fraction of the last REMB when a glitch
+// is detected, start suppressing REMB.
+static const double kMinSendBitrateFraction = 0.75;
+
+// Minimum fractional BWE growth per second needed to keep suppressing.
+static const double kMinGrowth = 0.015;
+
+RembSuppressor::RembSuppressor(Clock* clock)
+ : enabled_(false),
+ clock_(clock),
+ last_remb_bps_(0),
+ bitrate_sent_bps_(0),
+ last_remb_ignored_bps_(0),
+ last_remb_ignore_time_ms_(0),
+ remb_silence_start_(0) {
+}
+
+RembSuppressor::~RembSuppressor() {
+}
+
+bool RembSuppressor::SuppresNewRemb(uint32_t bitrate_bps) {
+ if (!Enabled())
+ return false;
+
+ if (remb_silence_start_ == 0) {
+ // Not currently suppressing. Check if there is a bit rate drop
+ // significant enough to warrant suppression.
+ return StartSuppressing(bitrate_bps);
+ }
+
+ // Check if signs point to recovery, otherwise back off suppression.
+ if (!ContinueSuppressing(bitrate_bps)) {
+ remb_silence_start_ = 0;
+ last_remb_ignored_bps_ = 0;
+ last_remb_ignore_time_ms_ = 0;
+ return false;
+ }
+ return true;
+}
+
+bool RembSuppressor::StartSuppressing(uint32_t bitrate_bps) {
+ if (bitrate_bps <
+ static_cast<uint32_t>(last_remb_bps_ * kMaxBweDropRatio + 0.5)) {
+ if (bitrate_sent_bps_ <
+ static_cast<uint32_t>(last_remb_bps_ * kMinSendBitrateFraction + 0.5)) {
+ int64_t now = clock_->TimeInMilliseconds();
+ remb_silence_start_ = now;
+ last_remb_ignore_time_ms_ = now;
+ last_remb_ignored_bps_ = bitrate_bps;
+ return true;
+ }
+ }
+ last_remb_bps_ = bitrate_bps;
+ return false;
+}
+
+bool RembSuppressor::ContinueSuppressing(uint32_t bitrate_bps) {
+ int64_t now = clock_->TimeInMilliseconds();
+
+ if (bitrate_bps >= last_remb_bps_) {
+ // We have fully recovered, stop suppressing!
+ return false;
+ }
+
+ // If exactly the same REMB, we probably don't have a new estimate. Keep on
+ // suppressing. However, if REMB is going down or just not increasing fast
+ // enough (kMinGrowth = 0.015 => REMB should increase by at least 1.5% / s)
+ // it looks like the link capacity has actually deteriorated and we are
+ // currently over-utilizing; back off.
+ if (bitrate_bps != last_remb_ignored_bps_) {
+ double delta_t = (now - last_remb_ignore_time_ms_) / 1000.0;
+ double min_increase = pow(1.0 + kMinGrowth, delta_t);
+ if (bitrate_bps < last_remb_ignored_bps_ * min_increase) {
+ return false;
+ }
+ }
+
+ last_remb_ignored_bps_ = bitrate_bps;
+ last_remb_ignore_time_ms_ = now;
+
+ return true;
+}
+
+void RembSuppressor::SetBitrateSent(uint32_t bitrate_bps) {
+ bitrate_sent_bps_ = bitrate_bps;
+}
+
+bool RembSuppressor::Enabled() {
+ return enabled_;
+}
+
+void RembSuppressor::SetEnabled(bool enabled) {
+ enabled_ = enabled &&
+ webrtc::field_trial::FindFullName(
+ "WebRTC-ConditionalRembSuppression") == "Enabled";
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/bitrate_controller/remb_suppressor.h b/webrtc/modules/bitrate_controller/remb_suppressor.h
new file mode 100644
index 0000000..987f97f
--- /dev/null
+++ b/webrtc/modules/bitrate_controller/remb_suppressor.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014 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.
+ *
+ * Usage: this class will register multiple RtcpBitrateObserver's one at each
+ * RTCP module. It will aggregate the results and run one bandwidth estimation
+ * and push the result to the encoder via VideoEncoderCallback.
+ */
+
+#ifndef THIRD_PARTY_WEBRTC_MODULES_BITRATE_CONTROLLER_REMB_SUPPRESSOR_H_
+#define THIRD_PARTY_WEBRTC_MODULES_BITRATE_CONTROLLER_REMB_SUPPRESSOR_H_
+
+#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
+#include "webrtc/system_wrappers/interface/clock.h"
+
+namespace webrtc {
+
+class RembSuppressor {
+ public:
+ explicit RembSuppressor(Clock* clock);
+ virtual ~RembSuppressor();
+
+ // Check whether this new REMB value should be suppressed.
+ bool SuppresNewRemb(uint32_t bitrate_bps);
+ // Update the current bitrate actually being sent.
+ void SetBitrateSent(uint32_t bitrate_bps);
+ // Turn suppression on or off.
+ void SetEnabled(bool enabled);
+
+ protected:
+ virtual bool Enabled();
+
+ private:
+ bool StartSuppressing(uint32_t bitrate_bps);
+ bool ContinueSuppressing(uint32_t bitrate_bps);
+
+ bool enabled_;
+ Clock* const clock_;
+ uint32_t last_remb_bps_;
+ uint32_t bitrate_sent_bps_;
+ uint32_t last_remb_ignored_bps_;
+ uint32_t last_remb_ignore_time_ms_;
+ int64_t remb_silence_start_;
+
+ DISALLOW_COPY_AND_ASSIGN(RembSuppressor);
+};
+
+} // namespace webrtc
+#endif // THIRD_PARTY_WEBRTC_MODULES_BITRATE_CONTROLLER_REMB_SUPPRESSOR_H_
diff --git a/webrtc/modules/bitrate_controller/remb_suppressor_unittest.cc b/webrtc/modules/bitrate_controller/remb_suppressor_unittest.cc
new file mode 100644
index 0000000..7ce99a1
--- /dev/null
+++ b/webrtc/modules/bitrate_controller/remb_suppressor_unittest.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014 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 <algorithm>
+#include <vector>
+
+#include "webrtc/modules/bitrate_controller/remb_suppressor.h"
+
+class TestSuppressor : public webrtc::RembSuppressor {
+ public:
+ explicit TestSuppressor(webrtc::Clock* clock) : RembSuppressor(clock) {}
+ virtual ~TestSuppressor() {}
+
+ virtual bool Enabled() OVERRIDE { return true; }
+};
+
+class RembSuppressorTest : public ::testing::Test {
+ protected:
+ RembSuppressorTest() : clock_(0), suppressor_(&clock_) {}
+ ~RembSuppressorTest() {}
+
+ virtual void SetUp() {}
+
+ virtual void TearDown() {}
+
+ bool NewRemb(uint32_t bitrate_bps) {
+ suppressor_.SetBitrateSent(bitrate_bps);
+ bool suppress = suppressor_.SuppresNewRemb(bitrate_bps);
+ // Default one REMB per second.
+ clock_.AdvanceTimeMilliseconds(1000);
+ return suppress;
+ }
+
+ webrtc::SimulatedClock clock_;
+ TestSuppressor suppressor_;
+};
+
+TEST_F(RembSuppressorTest, Basic) {
+ // Never true on first sample.
+ EXPECT_FALSE(NewRemb(50000));
+ // Some rampup.
+ EXPECT_FALSE(NewRemb(55000));
+ EXPECT_FALSE(NewRemb(60500));
+ EXPECT_FALSE(NewRemb(66550));
+ EXPECT_FALSE(NewRemb(73250));
+
+ // Reached limit, some fluctuation ok.
+ EXPECT_FALSE(NewRemb(72100));
+ EXPECT_FALSE(NewRemb(75500));
+ EXPECT_FALSE(NewRemb(69250));
+ EXPECT_FALSE(NewRemb(73250));
+}
+
+TEST_F(RembSuppressorTest, RecoveryTooSlow) {
+ // Never true on first sample.
+ EXPECT_FALSE(NewRemb(50000));
+
+ // Large drop.
+ EXPECT_TRUE(NewRemb(22499));
+
+ // No new estimate, still suppressing.
+ EXPECT_TRUE(NewRemb(22499));
+
+ // Too little increase - stop suppressing.
+ EXPECT_FALSE(NewRemb(22835));
+}
+
+TEST_F(RembSuppressorTest, RembDownDurinSupression) {
+ // Never true on first sample.
+ EXPECT_FALSE(NewRemb(50000));
+
+ // Large drop.
+ EXPECT_TRUE(NewRemb(22499));
+
+ // Remb is not allowed to fall.
+ EXPECT_FALSE(NewRemb(22498));
+}
+
+TEST_F(RembSuppressorTest, GlitchWithRecovery) {
+ const uint32_t start_bitrate = 300000;
+ uint32_t bitrate = start_bitrate;
+ // Never true on first sample.
+ EXPECT_FALSE(NewRemb(bitrate));
+
+ bitrate = static_cast<uint32_t>(bitrate * 0.44);
+ EXPECT_TRUE(NewRemb(bitrate));
+
+ while (bitrate < start_bitrate) {
+ EXPECT_TRUE(NewRemb(bitrate));
+ bitrate = static_cast<uint32_t>(bitrate * 1.10);
+ }
+
+ EXPECT_FALSE(NewRemb(bitrate));
+}
+
+TEST_F(RembSuppressorTest, BitrateSent) {
+ // Never true on first sample.
+ EXPECT_FALSE(NewRemb(50000));
+
+ // Only suppress large drop, if we are not sending at full capacity.
+ suppressor_.SetBitrateSent(37500);
+ EXPECT_FALSE(suppressor_.SuppresNewRemb(22499));
+}
diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp
index 6cd2343..84ec96a 100644
--- a/webrtc/modules/modules.gyp
+++ b/webrtc/modules/modules.gyp
@@ -191,6 +191,7 @@
'audio_processing/utility/delay_estimator_unittest.cc',
'audio_processing/utility/ring_buffer_unittest.cc',
'bitrate_controller/bitrate_controller_unittest.cc',
+ 'bitrate_controller/remb_suppressor_unittest.cc',
'bitrate_controller/send_side_bandwidth_estimation_unittest.cc',
'desktop_capture/desktop_and_cursor_composer_unittest.cc',
'desktop_capture/desktop_region_unittest.cc',
diff --git a/webrtc/video_engine/vie_encoder.cc b/webrtc/video_engine/vie_encoder.cc
index a1ab46b..3b2ab8d 100644
--- a/webrtc/video_engine/vie_encoder.cc
+++ b/webrtc/video_engine/vie_encoder.cc
@@ -389,6 +389,7 @@
video_codec.minBitrate * 1000,
kTransmissionMaxBitrateMultiplier *
video_codec.maxBitrate * 1000);
+ bitrate_controller_->SetCodecMode(video_codec.mode);
CriticalSectionScoped crit(data_cs_.get());
int pad_up_to_bitrate_kbps = video_codec.startBitrate;
@@ -755,6 +756,7 @@
int32_t ViEEncoder::SendStatistics(const uint32_t bit_rate,
const uint32_t frame_rate) {
+ bitrate_controller_->SetBitrateSent(bit_rate);
CriticalSectionScoped cs(callback_cs_.get());
if (codec_observer_) {
codec_observer_->OutgoingRate(channel_id_, frame_rate, bit_rate);