diff --git a/webrtc/build/common.gypi b/webrtc/build/common.gypi index b31ea5393..b98b9c39a 100644 --- a/webrtc/build/common.gypi +++ b/webrtc/build/common.gypi @@ -40,7 +40,6 @@ 'modules_java_gyp_path%': '<(modules_java_gyp_path)', 'webrtc_vp8_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp8', 'webrtc_vp9_dir%': '<(webrtc_root)/modules/video_coding/codecs/vp9', - 'rbe_components_path%': '<(webrtc_root)/modules/remote_bitrate_estimator', 'include_opus%': 1, }, 'build_with_chromium%': '<(build_with_chromium)', @@ -52,7 +51,6 @@ 'webrtc_vp9_dir%': '<(webrtc_vp9_dir)', 'include_opus%': '<(include_opus)', 'rtc_relative_path%': 1, - 'rbe_components_path%': '<(rbe_components_path)', 'external_libraries%': '0', 'json_root%': '<(DEPTH)/third_party/jsoncpp/source/include/', # openssl needs to be defined or gyp will complain. Is is only used when diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index c09945363..5d76fbfae 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -89,12 +89,12 @@ 'webrtc_utility', 'webrtc_video_coding', '<@(neteq_dependencies)', - '<(rbe_components_path)/remote_bitrate_estimator_components.gyp:rbe_components', '<(DEPTH)/testing/gmock.gyp:gmock', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', '<(webrtc_root)/modules/modules.gyp:video_capture_module_impl', + '<(webrtc_root)/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp:rbe_components', '<(webrtc_root)/modules/video_coding/codecs/vp8/vp8.gyp:webrtc_vp8', '<(webrtc_root)/modules/video_coding/codecs/vp9/vp9.gyp:webrtc_vp9', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', @@ -186,11 +186,16 @@ 'pacing/paced_sender_unittest.cc', 'remote_bitrate_estimator/bwe_simulations.cc', 'remote_bitrate_estimator/include/mock/mock_remote_bitrate_observer.h', + 'remote_bitrate_estimator/inter_arrival_unittest.cc', + 'remote_bitrate_estimator/overuse_detector_unittest.cc', 'remote_bitrate_estimator/rate_statistics_unittest.cc', + 'remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc', 'remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc', 'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc', 'remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h', 'remote_bitrate_estimator/remote_bitrate_estimators_test.cc', + 'remote_bitrate_estimator/test/bwe_test.cc', + 'remote_bitrate_estimator/test/bwe_test.h', 'remote_bitrate_estimator/test/bwe_test_baselinefile.cc', 'remote_bitrate_estimator/test/bwe_test_baselinefile.h', 'remote_bitrate_estimator/test/bwe_test_fileutils.cc', @@ -200,8 +205,6 @@ 'remote_bitrate_estimator/test/bwe_test_framework_unittest.cc', 'remote_bitrate_estimator/test/bwe_test_logging.cc', 'remote_bitrate_estimator/test/bwe_test_logging.h', - 'remote_bitrate_estimator/test/bwe_test.cc', - 'remote_bitrate_estimator/test/bwe_test.h', 'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h', 'rtp_rtcp/source/byte_io_unittest.cc', 'rtp_rtcp/source/fec_receiver_unittest.cc', diff --git a/webrtc/modules/remote_bitrate_estimator/BUILD.gn b/webrtc/modules/remote_bitrate_estimator/BUILD.gn index d7945776f..d72850091 100644 --- a/webrtc/modules/remote_bitrate_estimator/BUILD.gn +++ b/webrtc/modules/remote_bitrate_estimator/BUILD.gn @@ -24,8 +24,17 @@ source_set("remote_bitrate_estimator") { source_set("rbe_components") { sources = [ + "aimd_rate_control.cc", + "aimd_rate_control.h", + "inter_arrival.cc", + "inter_arrival.h", + "mimd_rate_control.cc", + "mimd_rate_control.h", "overuse_detector.cc", "overuse_detector.h", + "overuse_estimator.cc", + "overuse_estimator.h", + "remote_bitrate_estimator_abs_send_time.cc", "remote_bitrate_estimator_single_stream.cc", "remote_rate_control.cc", "remote_rate_control.h", diff --git a/webrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc b/webrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc new file mode 100644 index 000000000..1ea088ad5 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc @@ -0,0 +1,339 @@ +/* + * 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/remote_bitrate_estimator/aimd_rate_control.h" + +#include +#include +#include + +#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h" + +namespace webrtc { + +static const uint32_t kDefaultRttMs = 200; +static const int64_t kLogIntervalMs = 1000; +static const int kMinFeedbackIntervalMs = 200; +static const double kWithinIncomingBitrateHysteresis = 1.05; + +AimdRateControl::AimdRateControl(uint32_t min_bitrate_bps) + : min_configured_bitrate_bps_(min_bitrate_bps), + max_configured_bitrate_bps_(30000000), + current_bitrate_bps_(max_configured_bitrate_bps_), + max_hold_rate_bps_(0), + avg_max_bitrate_kbps_(-1.0f), + var_max_bitrate_kbps_(0.4f), + rate_control_state_(kRcHold), + came_from_state_(kRcDecrease), + rate_control_region_(kRcMaxUnknown), + time_last_bitrate_change_(-1), + current_input_(kBwNormal, 0, 1.0), + updated_(false), + time_first_incoming_estimate_(-1), + bitrate_is_initialized_(false), + beta_(0.9f), + rtt_(kDefaultRttMs), + time_of_last_log_(-1) {} + +RateControlType AimdRateControl::GetControlType() const { + return kAimdControl; +} + +uint32_t AimdRateControl::GetMinBitrate() const { + return min_configured_bitrate_bps_; +} + +bool AimdRateControl::ValidEstimate() const { + return bitrate_is_initialized_; +} + +int AimdRateControl::GetFeedbackInterval() const { + // Estimate how often we can send RTCP if we allocate up to 5% of bandwidth + // to feedback. + static const int kRtcpSize = 80.0; + int interval = kRtcpSize * 8.0 * 1000.0 / (0.05 * current_bitrate_bps_) + 0.5; + if (interval > kMaxFeedbackIntervalMs) + interval = kMaxFeedbackIntervalMs; + else if (interval < kMinFeedbackIntervalMs) { + interval = kMinFeedbackIntervalMs; + } + return interval; +} + +bool AimdRateControl::TimeToReduceFurther(int64_t time_now, + uint32_t incoming_bitrate_bps) const { + const int bitrate_reduction_interval = std::max(std::min(rtt_, 200u), 10u); + if (time_now - time_last_bitrate_change_ >= bitrate_reduction_interval) { + return true; + } + if (ValidEstimate()) { + const int threshold = static_cast(kWithinIncomingBitrateHysteresis * + incoming_bitrate_bps); + const int bitrate_difference = LatestEstimate() - incoming_bitrate_bps; + return bitrate_difference > threshold; + } + return false; +} + +uint32_t AimdRateControl::LatestEstimate() const { + return current_bitrate_bps_; +} + +uint32_t AimdRateControl::UpdateBandwidthEstimate(int64_t now_ms) { + current_bitrate_bps_ = ChangeBitrate(current_bitrate_bps_, + current_input_._incomingBitRate, + now_ms); + if (now_ms - time_of_last_log_ > kLogIntervalMs) { + time_of_last_log_ = now_ms; + } + return current_bitrate_bps_; +} + +void AimdRateControl::SetRtt(uint32_t rtt) { + rtt_ = rtt; +} + +RateControlRegion AimdRateControl::Update(const RateControlInput* input, + int64_t now_ms) { + assert(input); + + // Set the initial bit rate value to what we're receiving the first half + // second. + if (!bitrate_is_initialized_) { + if (time_first_incoming_estimate_ < 0) { + if (input->_incomingBitRate > 0) { + time_first_incoming_estimate_ = now_ms; + } + } else if (now_ms - time_first_incoming_estimate_ > 500 && + input->_incomingBitRate > 0) { + current_bitrate_bps_ = input->_incomingBitRate; + bitrate_is_initialized_ = true; + } + } + + if (updated_ && current_input_._bwState == kBwOverusing) { + // Only update delay factor and incoming bit rate. We always want to react + // on an over-use. + current_input_._noiseVar = input->_noiseVar; + current_input_._incomingBitRate = input->_incomingBitRate; + } else { + updated_ = true; + current_input_ = *input; + } + return rate_control_region_; +} + +void AimdRateControl::SetEstimate(int bitrate_bps, int64_t now_ms) { + updated_ = true; + bitrate_is_initialized_ = true; + current_bitrate_bps_ = ChangeBitrate(bitrate_bps, bitrate_bps, now_ms); +} + +uint32_t AimdRateControl::ChangeBitrate(uint32_t current_bitrate_bps, + uint32_t incoming_bitrate_bps, + int64_t now_ms) { + BWE_TEST_LOGGING_PLOT("estimated_incoming#1", -1, + incoming_bitrate_bps / 1000); + if (!updated_) { + return current_bitrate_bps_; + } + updated_ = false; + ChangeState(current_input_, now_ms); + // Calculated here because it's used in multiple places. + const float incoming_bitrate_kbps = incoming_bitrate_bps / 1000.0f; + // Calculate the max bit rate std dev given the normalized + // variance and the current incoming bit rate. + const float std_max_bit_rate = sqrt(var_max_bitrate_kbps_ * + avg_max_bitrate_kbps_); + bool fast_recovery_after_hold = false; + switch (rate_control_state_) { + case kRcHold: { + max_hold_rate_bps_ = std::max(max_hold_rate_bps_, incoming_bitrate_bps); + break; + } + case kRcIncrease: { + if (avg_max_bitrate_kbps_ >= 0) { + if (incoming_bitrate_kbps > avg_max_bitrate_kbps_ + + 3 * std_max_bit_rate) { + ChangeRegion(kRcMaxUnknown); + avg_max_bitrate_kbps_ = -1.0; + } else if (incoming_bitrate_kbps > avg_max_bitrate_kbps_ + + 2.5 * std_max_bit_rate) { + ChangeRegion(kRcAboveMax); + } + } + if (rate_control_region_ == kRcNearMax) { + // Approximate the over-use estimator delay to 100 ms. + const uint32_t response_time = rtt_ + 100; + uint32_t additive_increase_bps = AdditiveRateIncrease( + now_ms, time_last_bitrate_change_, response_time); + BWE_TEST_LOGGING_PLOT("add_increase#1", -1, + additive_increase_bps / 1000); + current_bitrate_bps += additive_increase_bps; + + } else { + uint32_t multiplicative_increase_bps = MultiplicativeRateIncrease( + now_ms, time_last_bitrate_change_, current_bitrate_bps); + BWE_TEST_LOGGING_PLOT("mult_increase#1", -1, + multiplicative_increase_bps / 1000); + current_bitrate_bps += multiplicative_increase_bps; + } + + if (max_hold_rate_bps_ > 0 && + beta_ * max_hold_rate_bps_ > current_bitrate_bps) { + current_bitrate_bps = static_cast(beta_ * max_hold_rate_bps_); + avg_max_bitrate_kbps_ = beta_ * max_hold_rate_bps_ / 1000.0f; + ChangeRegion(kRcNearMax); + fast_recovery_after_hold = true; + } + max_hold_rate_bps_ = 0; + time_last_bitrate_change_ = now_ms; + break; + } + case kRcDecrease: { + if (incoming_bitrate_bps < min_configured_bitrate_bps_) { + current_bitrate_bps = min_configured_bitrate_bps_; + } else { + // Set bit rate to something slightly lower than max + // to get rid of any self-induced delay. + current_bitrate_bps = static_cast(beta_ * + incoming_bitrate_bps + 0.5); + if (current_bitrate_bps > current_bitrate_bps_) { + // Avoid increasing the rate when over-using. + if (rate_control_region_ != kRcMaxUnknown) { + current_bitrate_bps = static_cast( + beta_ * avg_max_bitrate_kbps_ * 1000 + 0.5f); + } + current_bitrate_bps = std::min(current_bitrate_bps, + current_bitrate_bps_); + } + ChangeRegion(kRcNearMax); + + if (incoming_bitrate_kbps < avg_max_bitrate_kbps_ - + 3 * std_max_bit_rate) { + avg_max_bitrate_kbps_ = -1.0f; + } + + UpdateMaxBitRateEstimate(incoming_bitrate_kbps); + } + // Stay on hold until the pipes are cleared. + ChangeState(kRcHold); + time_last_bitrate_change_ = now_ms; + break; + } + default: + assert(false); + } + if (!fast_recovery_after_hold && (incoming_bitrate_bps > 100000 || + current_bitrate_bps > 150000) && + current_bitrate_bps > 1.5 * incoming_bitrate_bps) { + // Allow changing the bit rate if we are operating at very low rates + // Don't change the bit rate if the send side is too far off + current_bitrate_bps = current_bitrate_bps_; + time_last_bitrate_change_ = now_ms; + } + return current_bitrate_bps; +} + +uint32_t AimdRateControl::MultiplicativeRateIncrease( + int64_t now_ms, int64_t last_ms, uint32_t current_bitrate_bps) const { + double alpha = 1.08; + if (last_ms > -1) { + int time_since_last_update_ms = std::min(static_cast(now_ms - last_ms), + 1000); + alpha = pow(alpha, time_since_last_update_ms / 1000.0); + } + uint32_t multiplicative_increase_bps = std::max( + current_bitrate_bps * (alpha - 1.0), 1000.0); + return multiplicative_increase_bps; +} + +uint32_t AimdRateControl::AdditiveRateIncrease( + int64_t now_ms, int64_t last_ms, uint32_t response_time_ms) const { + assert(response_time_ms > 0); + double beta = 0.0; + if (last_ms > 0) { + beta = std::min((now_ms - last_ms) / + static_cast(response_time_ms), 1.0); + } + double bits_per_frame = static_cast(current_bitrate_bps_) / 30.0; + double packets_per_frame = std::ceil(bits_per_frame / (8.0 * 1200.0)); + double avg_packet_size_bits = bits_per_frame / packets_per_frame; + uint32_t additive_increase_bps = std::max( + 1000.0, beta * avg_packet_size_bits); + return additive_increase_bps; +} + +void AimdRateControl::UpdateMaxBitRateEstimate(float incoming_bitrate_kbps) { + const float alpha = 0.05f; + if (avg_max_bitrate_kbps_ == -1.0f) { + avg_max_bitrate_kbps_ = incoming_bitrate_kbps; + } else { + avg_max_bitrate_kbps_ = (1 - alpha) * avg_max_bitrate_kbps_ + + alpha * incoming_bitrate_kbps; + } + // Estimate the max bit rate variance and normalize the variance + // with the average max bit rate. + const float norm = std::max(avg_max_bitrate_kbps_, 1.0f); + var_max_bitrate_kbps_ = (1 - alpha) * var_max_bitrate_kbps_ + + alpha * (avg_max_bitrate_kbps_ - incoming_bitrate_kbps) * + (avg_max_bitrate_kbps_ - incoming_bitrate_kbps) / norm; + // 0.4 ~= 14 kbit/s at 500 kbit/s + if (var_max_bitrate_kbps_ < 0.4f) { + var_max_bitrate_kbps_ = 0.4f; + } + // 2.5f ~= 35 kbit/s at 500 kbit/s + if (var_max_bitrate_kbps_ > 2.5f) { + var_max_bitrate_kbps_ = 2.5f; + } +} + +void AimdRateControl::ChangeState(const RateControlInput& input, + int64_t now_ms) { + switch (current_input_._bwState) { + case kBwNormal: + if (rate_control_state_ == kRcHold) { + time_last_bitrate_change_ = now_ms; + ChangeState(kRcIncrease); + } + break; + case kBwOverusing: + if (rate_control_state_ != kRcDecrease) { + ChangeState(kRcDecrease); + } + break; + case kBwUnderusing: + ChangeState(kRcHold); + break; + default: + assert(false); + } +} + +void AimdRateControl::ChangeRegion(RateControlRegion region) { + rate_control_region_ = region; + switch (rate_control_region_) { + case kRcAboveMax: + case kRcMaxUnknown: + beta_ = 0.9f; + break; + case kRcNearMax: + beta_ = 0.95f; + break; + default: + assert(false); + } +} + +void AimdRateControl::ChangeState(RateControlState new_state) { + came_from_state_ = rate_control_state_; + rate_control_state_ = new_state; +} +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h b/webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h new file mode 100644 index 000000000..f2f6e0849 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ +#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ + +#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" + +namespace webrtc { + +// A RemoteRateControl implementation based on additive increases of +// bitrate when no over-use is detected and multiplicative decreases when +// over-uses are detected. When we think the available bandwidth has changes or +// is unknown, we will switch to a "slow-start mode" where we increase +// multiplicatively. +class AimdRateControl : public RemoteRateControl { + public: + explicit AimdRateControl(uint32_t min_bitrate_bps); + virtual ~AimdRateControl() {} + + // Implements RemoteRateControl. + virtual bool ValidEstimate() const OVERRIDE; + virtual RateControlType GetControlType() const OVERRIDE; + virtual uint32_t GetMinBitrate() const OVERRIDE; + virtual int GetFeedbackInterval() const OVERRIDE; + // Returns true if the bitrate estimate hasn't been changed for more than + // an RTT, or if the incoming_bitrate is more than 5% above the current + // estimate. Should be used to decide if we should reduce the rate further + // when over-using. + virtual bool TimeToReduceFurther( + int64_t time_now, uint32_t incoming_bitrate_bps) const OVERRIDE; + virtual uint32_t LatestEstimate() const OVERRIDE; + virtual uint32_t UpdateBandwidthEstimate(int64_t now_ms) OVERRIDE; + virtual void SetRtt(uint32_t rtt) OVERRIDE; + virtual RateControlRegion Update(const RateControlInput* input, + int64_t now_ms) OVERRIDE; + virtual void SetEstimate(int bitrate_bps, int64_t now_ms) OVERRIDE; + + private: + // Update the target bitrate according based on, among other things, + // the current rate control state, the current target bitrate and the incoming + // bitrate. When in the "increase" state the bitrate will be increased either + // additively or multiplicatively depending on the rate control region. When + // in the "decrease" state the bitrate will be decreased to slightly below the + // incoming bitrate. When in the "hold" state the bitrate will be kept + // constant to allow built up queues to drain. + uint32_t ChangeBitrate(uint32_t current_bit_rate, + uint32_t incoming_bit_rate, + int64_t now_ms); + uint32_t MultiplicativeRateIncrease(int64_t now_ms, int64_t last_ms, + uint32_t current_bitrate_bps) const; + uint32_t AdditiveRateIncrease(int64_t now_ms, int64_t last_ms, + uint32_t response_time_ms) const; + void UpdateChangePeriod(int64_t now_ms); + void UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps); + void ChangeState(const RateControlInput& input, int64_t now_ms); + void ChangeState(RateControlState new_state); + void ChangeRegion(RateControlRegion region); + + uint32_t min_configured_bitrate_bps_; + uint32_t max_configured_bitrate_bps_; + uint32_t current_bitrate_bps_; + uint32_t max_hold_rate_bps_; + float avg_max_bitrate_kbps_; + float var_max_bitrate_kbps_; + RateControlState rate_control_state_; + RateControlState came_from_state_; + RateControlRegion rate_control_region_; + int64_t time_last_bitrate_change_; + RateControlInput current_input_; + bool updated_; + int64_t time_first_incoming_estimate_; + bool bitrate_is_initialized_; + float beta_; + uint32_t rtt_; + int64_t time_of_last_log_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AimdRateControl); +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ diff --git a/webrtc/modules/remote_bitrate_estimator/inter_arrival.cc b/webrtc/modules/remote_bitrate_estimator/inter_arrival.cc new file mode 100644 index 000000000..a9a7ae7d0 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/inter_arrival.cc @@ -0,0 +1,121 @@ +/* + * 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 "webrtc/modules/remote_bitrate_estimator/inter_arrival.h" + +#include +#include + +#include "webrtc/modules/interface/module_common_types.h" + +namespace webrtc { + +static const int kBurstDeltaThresholdMs = 5; + +InterArrival::InterArrival(uint32_t timestamp_group_length_ticks, + double timestamp_to_ms_coeff, + bool enable_burst_grouping) + : kTimestampGroupLengthTicks(timestamp_group_length_ticks), + current_timestamp_group_(), + prev_timestamp_group_(), + timestamp_to_ms_coeff_(timestamp_to_ms_coeff), + burst_grouping_(enable_burst_grouping) {} + +bool InterArrival::ComputeDeltas(uint32_t timestamp, + int64_t arrival_time_ms, + size_t packet_size, + uint32_t* timestamp_delta, + int64_t* arrival_time_delta_ms, + int* packet_size_delta) { + assert(timestamp_delta != NULL); + assert(arrival_time_delta_ms != NULL); + assert(packet_size_delta != NULL); + bool calculated_deltas = false; + if (current_timestamp_group_.IsFirstPacket()) { + // We don't have enough data to update the filter, so we store it until we + // have two frames of data to process. + current_timestamp_group_.timestamp = timestamp; + current_timestamp_group_.first_timestamp = timestamp; + } else if (!PacketInOrder(timestamp)) { + return false; + } else if (NewTimestampGroup(arrival_time_ms, timestamp)) { + // First packet of a later frame, the previous frame sample is ready. + if (prev_timestamp_group_.complete_time_ms >= 0) { + *timestamp_delta = current_timestamp_group_.timestamp - + prev_timestamp_group_.timestamp; + *arrival_time_delta_ms = current_timestamp_group_.complete_time_ms - + prev_timestamp_group_.complete_time_ms; + assert(*arrival_time_delta_ms >= 0); + *packet_size_delta = static_cast(current_timestamp_group_.size) - + static_cast(prev_timestamp_group_.size); + calculated_deltas = true; + } + prev_timestamp_group_ = current_timestamp_group_; + // The new timestamp is now the current frame. + current_timestamp_group_.first_timestamp = timestamp; + current_timestamp_group_.timestamp = timestamp; + current_timestamp_group_.size = 0; + } + else { + current_timestamp_group_.timestamp = LatestTimestamp( + current_timestamp_group_.timestamp, timestamp); + } + // Accumulate the frame size. + current_timestamp_group_.size += packet_size; + current_timestamp_group_.complete_time_ms = arrival_time_ms; + + return calculated_deltas; +} + +bool InterArrival::PacketInOrder(uint32_t timestamp) { + if (current_timestamp_group_.IsFirstPacket()) { + return true; + } else { + // Assume that a diff which is bigger than half the timestamp interval + // (32 bits) must be due to reordering. This code is almost identical to + // that in IsNewerTimestamp() in module_common_types.h. + uint32_t timestamp_diff = timestamp - + current_timestamp_group_.first_timestamp; + return timestamp_diff < 0x80000000; + } +} + +// Assumes that |timestamp| is not reordered compared to +// |current_timestamp_group_|. +bool InterArrival::NewTimestampGroup(int64_t arrival_time_ms, + uint32_t timestamp) const { + if (current_timestamp_group_.IsFirstPacket()) { + return false; + } else if (BelongsToBurst(arrival_time_ms, timestamp)) { + return false; + } else { + uint32_t timestamp_diff = timestamp - + current_timestamp_group_.first_timestamp; + return timestamp_diff > kTimestampGroupLengthTicks; + } +} + +bool InterArrival::BelongsToBurst(int64_t arrival_time_ms, + uint32_t timestamp) const { + if (!burst_grouping_) { + return false; + } + assert(current_timestamp_group_.complete_time_ms >= 0); + int64_t arrival_time_delta_ms = arrival_time_ms - + current_timestamp_group_.complete_time_ms; + uint32_t timestamp_diff = timestamp - current_timestamp_group_.timestamp; + int64_t ts_delta_ms = timestamp_to_ms_coeff_ * timestamp_diff + 0.5; + if (ts_delta_ms == 0) + return true; + int propagation_delta_ms = arrival_time_delta_ms - ts_delta_ms; + return propagation_delta_ms < 0 && + arrival_time_delta_ms <= kBurstDeltaThresholdMs; +} +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/inter_arrival.h b/webrtc/modules/remote_bitrate_estimator/inter_arrival.h new file mode 100644 index 000000000..ace855118 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/inter_arrival.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ +#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +// Helper class to compute the inter-arrival time delta and the size delta +// between two timestamp groups. A timestamp is a 32 bit unsigned number with +// a client defined rate. +class InterArrival { + public: + // A timestamp group is defined as all packets with a timestamp which are at + // most timestamp_group_length_ticks older than the first timestamp in that + // group. + InterArrival(uint32_t timestamp_group_length_ticks, + double timestamp_to_ms_coeff, + bool enable_burst_grouping); + + // This function returns true if a delta was computed, or false if the current + // group is still incomplete or if only one group has been completed. + // |timestamp| is the timestamp. + // |arrival_time_ms| is the local time at which the packet arrived. + // |packet_size| is the size of the packet. + // |timestamp_delta| (output) is the computed timestamp delta. + // |arrival_time_delta_ms| (output) is the computed arrival-time delta. + // |packet_size_delta| (output) is the computed size delta. + bool ComputeDeltas(uint32_t timestamp, + int64_t arrival_time_ms, + size_t packet_size, + uint32_t* timestamp_delta, + int64_t* arrival_time_delta_ms, + int* packet_size_delta); + + private: + struct TimestampGroup { + TimestampGroup() + : size(0), + first_timestamp(0), + timestamp(0), + complete_time_ms(-1) {} + + bool IsFirstPacket() const { + return complete_time_ms == -1; + } + + size_t size; + uint32_t first_timestamp; + uint32_t timestamp; + int64_t complete_time_ms; + }; + + // Returns true if the packet with timestamp |timestamp| arrived in order. + bool PacketInOrder(uint32_t timestamp); + + // Returns true if the last packet was the end of the current batch and the + // packet with |timestamp| is the first of a new batch. + bool NewTimestampGroup(int64_t arrival_time_ms, uint32_t timestamp) const; + + bool BelongsToBurst(int64_t arrival_time_ms, uint32_t timestamp) const; + + const uint32_t kTimestampGroupLengthTicks; + TimestampGroup current_timestamp_group_; + TimestampGroup prev_timestamp_group_; + double timestamp_to_ms_coeff_; + bool burst_grouping_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InterArrival); +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ diff --git a/webrtc/modules/remote_bitrate_estimator/inter_arrival_unittest.cc b/webrtc/modules/remote_bitrate_estimator/inter_arrival_unittest.cc new file mode 100644 index 000000000..6389f1176 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/inter_arrival_unittest.cc @@ -0,0 +1,422 @@ +/* + * 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/common_types.h" +#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { +namespace testing { + +enum { + kTimestampGroupLengthUs = 5000, + kMinStep = 20, + kTriggerNewGroupUs = kTimestampGroupLengthUs + kMinStep, + kBurstThresholdMs = 5, + kAbsSendTimeFraction = 18, + kAbsSendTimeInterArrivalUpshift = 8, + kInterArrivalShift = kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift, +}; + +const double kRtpTimestampToMs = 1.0 / 90.0; +const double kAstToMs = 1000.0 / static_cast(1 << kInterArrivalShift); + +class InterArrivalTest : public ::testing::Test { + protected: + virtual void SetUp() { + inter_arrival_rtp_.reset(new InterArrival( + MakeRtpTimestamp(kTimestampGroupLengthUs), + kRtpTimestampToMs, + true)); + inter_arrival_ast_.reset(new InterArrival( + MakeAbsSendTime(kTimestampGroupLengthUs), + kAstToMs, + true)); + } + + // Test that neither inter_arrival instance complete the timestamp group from + // the given data. + void ExpectFalse(int64_t timestamp_us, int64_t arrival_time_ms, + size_t packet_size) { + InternalExpectFalse(inter_arrival_rtp_.get(), + MakeRtpTimestamp(timestamp_us), arrival_time_ms, + packet_size); + InternalExpectFalse(inter_arrival_ast_.get(), MakeAbsSendTime(timestamp_us), + arrival_time_ms, packet_size); + } + + // Test that both inter_arrival instances complete the timestamp group from + // the given data and that all returned deltas are as expected (except + // timestamp delta, which is rounded from us to different ranges and must + // match within an interval, given in |timestamp_near]. + void ExpectTrue(int64_t timestamp_us, int64_t arrival_time_ms, + size_t packet_size, int64_t expected_timestamp_delta_us, + int64_t expected_arrival_time_delta_ms, + int expected_packet_size_delta, + uint32_t timestamp_near) { + InternalExpectTrue(inter_arrival_rtp_.get(), MakeRtpTimestamp(timestamp_us), + arrival_time_ms, packet_size, + MakeRtpTimestamp(expected_timestamp_delta_us), + expected_arrival_time_delta_ms, + expected_packet_size_delta, timestamp_near); + InternalExpectTrue(inter_arrival_ast_.get(), MakeAbsSendTime(timestamp_us), + arrival_time_ms, packet_size, + MakeAbsSendTime(expected_timestamp_delta_us), + expected_arrival_time_delta_ms, + expected_packet_size_delta, timestamp_near << 8); + } + + void WrapTestHelper(int64_t wrap_start_us, uint32_t timestamp_near, + bool unorderly_within_group) { + // Step through the range of a 32 bit int, 1/4 at a time to not cause + // packets close to wraparound to be judged as out of order. + + // G1 + int64_t arrival_time = 17; + ExpectFalse(0, arrival_time, 1); + + // G2 + arrival_time += kBurstThresholdMs + 1; + ExpectFalse(wrap_start_us / 4, arrival_time, 1); + + // G3 + arrival_time += kBurstThresholdMs + 1; + ExpectTrue(wrap_start_us / 2, arrival_time, 1, + wrap_start_us / 4, 6, 0, // Delta G2-G1 + 0); + + // G4 + arrival_time += kBurstThresholdMs + 1; + int64_t g4_arrival_time = arrival_time; + ExpectTrue(wrap_start_us / 2 + wrap_start_us / 4, arrival_time, 1, + wrap_start_us / 4, 6, 0, // Delta G3-G2 + timestamp_near); + + // G5 + arrival_time += kBurstThresholdMs + 1; + ExpectTrue(wrap_start_us, arrival_time, 2, + wrap_start_us / 4, 6, 0, // Delta G4-G3 + timestamp_near); + for (int i = 0; i < 10; ++i) { + // Slowly step across the wrap point. + arrival_time += kBurstThresholdMs + 1; + if (unorderly_within_group) { + // These packets arrive with timestamps in decreasing order but are + // nevertheless accumulated to group because their timestamps are higher + // than the initial timestamp of the group. + ExpectFalse(wrap_start_us + kMinStep * (9 - i), arrival_time, 1); + } else { + ExpectFalse(wrap_start_us + kMinStep * i, arrival_time, 1); + } + } + int64_t g5_arrival_time = arrival_time; + + // This packet is out of order and should be dropped. + arrival_time += kBurstThresholdMs + 1; + ExpectFalse(wrap_start_us - 100, arrival_time, 100); + + // G6 + arrival_time += kBurstThresholdMs + 1; + int64_t g6_arrival_time = arrival_time; + ExpectTrue(wrap_start_us + kTriggerNewGroupUs, arrival_time, 10, + wrap_start_us / 4 + 9 * kMinStep, + g5_arrival_time - g4_arrival_time, + (2 + 10) - 1, // Delta G5-G4 + timestamp_near); + + // This packet is out of order and should be dropped. + arrival_time += kBurstThresholdMs + 1; + ExpectFalse(wrap_start_us + kTimestampGroupLengthUs, arrival_time, 100); + + // G7 + arrival_time += kBurstThresholdMs + 1; + ExpectTrue(wrap_start_us + 2 * kTriggerNewGroupUs, + arrival_time, 100, + // Delta G6-G5 + kTriggerNewGroupUs - 9 * kMinStep, + g6_arrival_time - g5_arrival_time, + 10 - (2 + 10), + timestamp_near); + } + + private: + static uint32_t MakeRtpTimestamp(int64_t us) { + return static_cast(static_cast(us * 90 + 500) / 1000); + } + + static uint32_t MakeAbsSendTime(int64_t us) { + uint32_t absolute_send_time = static_cast( + ((static_cast(us) << 18) + 500000) / 1000000) & 0x00FFFFFFul; + return absolute_send_time << 8; + } + + static void InternalExpectFalse(InterArrival* inter_arrival, + uint32_t timestamp, int64_t arrival_time_ms, + size_t packet_size) { + uint32_t dummy_timestamp = 101; + int64_t dummy_arrival_time_ms = 303; + int dummy_packet_size = 909; + bool computed = inter_arrival->ComputeDeltas(timestamp, + arrival_time_ms, + packet_size, + &dummy_timestamp, + &dummy_arrival_time_ms, + &dummy_packet_size); + EXPECT_EQ(computed, false); + EXPECT_EQ(101ul, dummy_timestamp); + EXPECT_EQ(303, dummy_arrival_time_ms); + EXPECT_EQ(909, dummy_packet_size); + } + + static void InternalExpectTrue(InterArrival* inter_arrival, + uint32_t timestamp, int64_t arrival_time_ms, + size_t packet_size, + uint32_t expected_timestamp_delta, + int64_t expected_arrival_time_delta_ms, + int expected_packet_size_delta, + uint32_t timestamp_near) { + uint32_t delta_timestamp = 101; + int64_t delta_arrival_time_ms = 303; + int delta_packet_size = 909; + bool computed = inter_arrival->ComputeDeltas(timestamp, + arrival_time_ms, + packet_size, + &delta_timestamp, + &delta_arrival_time_ms, + &delta_packet_size); + EXPECT_EQ(true, computed); + EXPECT_NEAR(expected_timestamp_delta, delta_timestamp, timestamp_near); + EXPECT_EQ(expected_arrival_time_delta_ms, delta_arrival_time_ms); + EXPECT_EQ(expected_packet_size_delta, delta_packet_size); + } + + scoped_ptr inter_arrival_rtp_; + scoped_ptr inter_arrival_ast_; +}; + +TEST_F(InterArrivalTest, FirstPacket) { + ExpectFalse(0, 17, 1); +} + +TEST_F(InterArrivalTest, FirstGroup) { + // G1 + int64_t arrival_time = 17; + int64_t g1_arrival_time = arrival_time; + ExpectFalse(0, arrival_time, 1); + + // G2 + arrival_time += kBurstThresholdMs + 1; + int64_t g2_arrival_time = arrival_time; + ExpectFalse(kTriggerNewGroupUs, arrival_time, 2); + + // G3 + // Only once the first packet of the third group arrives, do we see the deltas + // between the first two. + arrival_time += kBurstThresholdMs + 1; + ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 1, + // Delta G2-G1 + kTriggerNewGroupUs, g2_arrival_time - g1_arrival_time, 1, + 0); +} + +TEST_F(InterArrivalTest, SecondGroup) { + // G1 + int64_t arrival_time = 17; + int64_t g1_arrival_time = arrival_time; + ExpectFalse(0, arrival_time, 1); + + // G2 + arrival_time += kBurstThresholdMs + 1; + int64_t g2_arrival_time = arrival_time; + ExpectFalse(kTriggerNewGroupUs, arrival_time, 2); + + // G3 + arrival_time += kBurstThresholdMs + 1; + int64_t g3_arrival_time = arrival_time; + ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 1, + // Delta G2-G1 + kTriggerNewGroupUs, g2_arrival_time - g1_arrival_time, 1, + 0); + + // G4 + // First packet of 4th group yields deltas between group 2 and 3. + arrival_time += kBurstThresholdMs + 1; + ExpectTrue(3 * kTriggerNewGroupUs, arrival_time, 2, + // Delta G3-G2 + kTriggerNewGroupUs, g3_arrival_time - g2_arrival_time, -1, + 0); +} + +TEST_F(InterArrivalTest, AccumulatedGroup) { + // G1 + int64_t arrival_time = 17; + int64_t g1_arrival_time = arrival_time; + ExpectFalse(0, arrival_time, 1); + + // G2 + arrival_time += kBurstThresholdMs + 1; + ExpectFalse(kTriggerNewGroupUs, 28, 2); + int64_t timestamp = kTriggerNewGroupUs; + for (int i = 0; i < 10; ++i) { + // A bunch of packets arriving within the same group. + arrival_time += kBurstThresholdMs + 1; + timestamp += kMinStep; + ExpectFalse(timestamp, arrival_time, 1); + } + int64_t g2_arrival_time = arrival_time; + int64_t g2_timestamp = timestamp; + + // G3 + arrival_time = 500; + ExpectTrue(2 * kTriggerNewGroupUs, arrival_time, 100, + g2_timestamp, g2_arrival_time - g1_arrival_time, + (2 + 10) - 1, // Delta G2-G1 + 0); +} + +TEST_F(InterArrivalTest, OutOfOrderPacket) { + // G1 + int64_t arrival_time = 17; + int64_t timestamp = 0; + ExpectFalse(timestamp, arrival_time, 1); + int64_t g1_timestamp = timestamp; + int64_t g1_arrival_time = arrival_time; + + // G2 + arrival_time += 11; + timestamp += kTriggerNewGroupUs; + ExpectFalse(timestamp, 28, 2); + for (int i = 0; i < 10; ++i) { + arrival_time += kBurstThresholdMs + 1; + timestamp += kMinStep; + ExpectFalse(timestamp, arrival_time, 1); + } + int64_t g2_timestamp = timestamp; + int64_t g2_arrival_time = arrival_time; + + // This packet is out of order and should be dropped. + arrival_time = 281; + ExpectFalse(g1_timestamp, arrival_time, 100); + + // G3 + arrival_time = 500; + timestamp = 2 * kTriggerNewGroupUs; + ExpectTrue(timestamp, arrival_time, 100, + // Delta G2-G1 + g2_timestamp - g1_timestamp, g2_arrival_time - g1_arrival_time, + (2 + 10) - 1, + 0); +} + +TEST_F(InterArrivalTest, OutOfOrderWithinGroup) { + // G1 + int64_t arrival_time = 17; + int64_t timestamp = 0; + ExpectFalse(timestamp, arrival_time, 1); + int64_t g1_timestamp = timestamp; + int64_t g1_arrival_time = arrival_time; + + // G2 + timestamp += kTriggerNewGroupUs; + arrival_time += 11; + ExpectFalse(kTriggerNewGroupUs, 28, 2); + timestamp += 10 * kMinStep; + int64_t g2_timestamp = timestamp; + for (int i = 0; i < 10; ++i) { + // These packets arrive with timestamps in decreasing order but are + // nevertheless accumulated to group because their timestamps are higher + // than the initial timestamp of the group. + arrival_time += kBurstThresholdMs + 1; + ExpectFalse(timestamp, arrival_time, 1); + timestamp -= kMinStep; + } + int64_t g2_arrival_time = arrival_time; + + // However, this packet is deemed out of order and should be dropped. + arrival_time = 281; + timestamp = g1_timestamp; + ExpectFalse(timestamp, arrival_time, 100); + + // G3 + timestamp = 2 * kTriggerNewGroupUs; + arrival_time = 500; + ExpectTrue(timestamp, arrival_time, 100, + g2_timestamp - g1_timestamp, g2_arrival_time - g1_arrival_time, + (2 + 10) - 1, + 0); +} + +TEST_F(InterArrivalTest, TwoBursts) { + // G1 + int64_t g1_arrival_time = 17; + ExpectFalse(0, g1_arrival_time, 1); + + // G2 + int64_t timestamp = kTriggerNewGroupUs; + int64_t arrival_time = 100; // Simulate no packets arriving for 100 ms. + for (int i = 0; i < 10; ++i) { + // A bunch of packets arriving in one burst (within 5 ms apart). + timestamp += 30000; + arrival_time += kBurstThresholdMs; + ExpectFalse(timestamp, arrival_time, 1); + } + int64_t g2_arrival_time = arrival_time; + int64_t g2_timestamp = timestamp; + + // G3 + timestamp += 30000; + arrival_time += kBurstThresholdMs + 1; + ExpectTrue(timestamp, arrival_time, 100, + g2_timestamp, g2_arrival_time - g1_arrival_time, + 10 - 1, // Delta G2-G1 + 0); +} + + +TEST_F(InterArrivalTest, NoBursts) { + // G1 + ExpectFalse(0, 17, 1); + + // G2 + int64_t timestamp = kTriggerNewGroupUs; + int64_t arrival_time = 28; + ExpectFalse(timestamp, arrival_time, 2); + + // G3 + ExpectTrue(kTriggerNewGroupUs + 30000, arrival_time + kBurstThresholdMs + 1, + 100, timestamp - 0, arrival_time - 17, + 2 - 1, // Delta G2-G1 + 0); +} + +// Yields 0xfffffffe when converted to internal representation in +// inter_arrival_rtp_ and inter_arrival_ast_ respectively. +static const int64_t kStartRtpTimestampWrapUs = 47721858827; +static const int64_t kStartAbsSendTimeWrapUs = 63999995; + +TEST_F(InterArrivalTest, RtpTimestampWrap) { + WrapTestHelper(kStartRtpTimestampWrapUs, 1, false); +} + +TEST_F(InterArrivalTest, AbsSendTimeWrap) { + WrapTestHelper(kStartAbsSendTimeWrapUs, 1, false); +} + +TEST_F(InterArrivalTest, RtpTimestampWrapOutOfOrderWithinGroup) { + WrapTestHelper(kStartRtpTimestampWrapUs, 1, true); +} + +TEST_F(InterArrivalTest, AbsSendTimeWrapOutOfOrderWithinGroup) { + WrapTestHelper(kStartAbsSendTimeWrapUs, 1, true); +} +} // namespace testing +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/mimd_rate_control.cc b/webrtc/modules/remote_bitrate_estimator/mimd_rate_control.cc new file mode 100644 index 000000000..8c9e8f4aa --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/mimd_rate_control.cc @@ -0,0 +1,327 @@ +/* + * 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/remote_bitrate_estimator/mimd_rate_control.h" + +#include +#include +#include +#include + +namespace webrtc { + +const uint32_t kDefaultRttMs = 200; +const int64_t kLogIntervalMs = 1000; + +MimdRateControl::MimdRateControl(uint32_t min_bitrate_bps) + : min_configured_bit_rate_(min_bitrate_bps), + max_configured_bit_rate_(30000000), + current_bit_rate_(max_configured_bit_rate_), + max_hold_rate_(0), + avg_max_bit_rate_(-1.0f), + var_max_bit_rate_(0.4f), + rate_control_state_(kRcHold), + came_from_state_(kRcDecrease), + rate_control_region_(kRcMaxUnknown), + last_bit_rate_change_(-1), + current_input_(kBwNormal, 0, 1.0), + updated_(false), + time_first_incoming_estimate_(-1), + initialized_bit_rate_(false), + avg_change_period_(1000.0f), + last_change_ms_(-1), + beta_(0.9f), + rtt_(kDefaultRttMs), + time_of_last_log_(-1) +{ +} + +RateControlType MimdRateControl::GetControlType() const { + return kMimdControl; +} + +uint32_t MimdRateControl::GetMinBitrate() const { + return min_configured_bit_rate_; +} + +bool MimdRateControl::ValidEstimate() const { + return initialized_bit_rate_; +} + +int MimdRateControl::GetFeedbackInterval() const { + return kMaxFeedbackIntervalMs; +} + +bool MimdRateControl::TimeToReduceFurther(int64_t time_now, + uint32_t incoming_bitrate_bps) const { + const int bitrate_reduction_interval = std::max(std::min(rtt_, 200u), 10u); + if (time_now - last_bit_rate_change_ >= bitrate_reduction_interval) { + return true; + } + if (ValidEstimate()) { + const int threshold = static_cast(1.05 * incoming_bitrate_bps); + const int bitrate_difference = LatestEstimate() - incoming_bitrate_bps; + return bitrate_difference > threshold; + } + return false; +} + +uint32_t MimdRateControl::LatestEstimate() const { + return current_bit_rate_; +} + +uint32_t MimdRateControl::UpdateBandwidthEstimate(int64_t now_ms) { + current_bit_rate_ = ChangeBitRate(current_bit_rate_, + current_input_._incomingBitRate, + current_input_._noiseVar, + now_ms); + if (now_ms - time_of_last_log_ > kLogIntervalMs) { + time_of_last_log_ = now_ms; + } + return current_bit_rate_; +} + +void MimdRateControl::SetRtt(uint32_t rtt) { + rtt_ = rtt; +} + +RateControlRegion MimdRateControl::Update(const RateControlInput* input, + int64_t now_ms) { + assert(input); + + // Set the initial bit rate value to what we're receiving the first half + // second. + if (!initialized_bit_rate_) { + if (time_first_incoming_estimate_ < 0) { + if (input->_incomingBitRate > 0) { + time_first_incoming_estimate_ = now_ms; + } + } else if (now_ms - time_first_incoming_estimate_ > 500 && + input->_incomingBitRate > 0) { + current_bit_rate_ = input->_incomingBitRate; + initialized_bit_rate_ = true; + } + } + + if (updated_ && current_input_._bwState == kBwOverusing) { + // Only update delay factor and incoming bit rate. We always want to react + // on an over-use. + current_input_._noiseVar = input->_noiseVar; + current_input_._incomingBitRate = input->_incomingBitRate; + return rate_control_region_; + } + updated_ = true; + current_input_ = *input; + return rate_control_region_; +} + +void MimdRateControl::SetEstimate(int bitrate_bps, int64_t now_ms) { +} + +uint32_t MimdRateControl::ChangeBitRate(uint32_t current_bit_rate, + uint32_t incoming_bit_rate, + double noise_var, + int64_t now_ms) { + if (!updated_) { + return current_bit_rate_; + } + updated_ = false; + UpdateChangePeriod(now_ms); + ChangeState(current_input_, now_ms); + // calculated here because it's used in multiple places + const float incoming_bit_rate_kbps = incoming_bit_rate / 1000.0f; + // Calculate the max bit rate std dev given the normalized + // variance and the current incoming bit rate. + const float std_max_bit_rate = sqrt(var_max_bit_rate_ * avg_max_bit_rate_); + bool recovery = false; + switch (rate_control_state_) { + case kRcHold: { + max_hold_rate_ = std::max(max_hold_rate_, incoming_bit_rate); + break; + } + case kRcIncrease: { + if (avg_max_bit_rate_ >= 0) { + if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 3 * std_max_bit_rate) { + ChangeRegion(kRcMaxUnknown); + avg_max_bit_rate_ = -1.0; + } else if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 2.5 * + std_max_bit_rate) { + ChangeRegion(kRcAboveMax); + } + } + const uint32_t response_time = static_cast(avg_change_period_ + + 0.5f) + rtt_ + 300; + double alpha = RateIncreaseFactor(now_ms, last_bit_rate_change_, + response_time, noise_var); + + current_bit_rate = static_cast(current_bit_rate * alpha) + 1000; + if (max_hold_rate_ > 0 && beta_ * max_hold_rate_ > current_bit_rate) { + current_bit_rate = static_cast(beta_ * max_hold_rate_); + avg_max_bit_rate_ = beta_ * max_hold_rate_ / 1000.0f; + ChangeRegion(kRcNearMax); + recovery = true; + } + max_hold_rate_ = 0; + last_bit_rate_change_ = now_ms; + break; + } + case kRcDecrease: { + if (incoming_bit_rate < min_configured_bit_rate_) { + current_bit_rate = min_configured_bit_rate_; + } else { + // Set bit rate to something slightly lower than max + // to get rid of any self-induced delay. + current_bit_rate = static_cast(beta_ * incoming_bit_rate + + 0.5); + if (current_bit_rate > current_bit_rate_) { + // Avoid increasing the rate when over-using. + if (rate_control_region_ != kRcMaxUnknown) { + current_bit_rate = static_cast(beta_ * avg_max_bit_rate_ * + 1000 + 0.5f); + } + current_bit_rate = std::min(current_bit_rate, current_bit_rate_); + } + ChangeRegion(kRcNearMax); + + if (incoming_bit_rate_kbps < avg_max_bit_rate_ - 3 * std_max_bit_rate) { + avg_max_bit_rate_ = -1.0f; + } + + UpdateMaxBitRateEstimate(incoming_bit_rate_kbps); + } + // Stay on hold until the pipes are cleared. + ChangeState(kRcHold); + last_bit_rate_change_ = now_ms; + break; + } + default: + assert(false); + } + if (!recovery && (incoming_bit_rate > 100000 || current_bit_rate > 150000) && + current_bit_rate > 1.5 * incoming_bit_rate) { + // Allow changing the bit rate if we are operating at very low rates + // Don't change the bit rate if the send side is too far off + current_bit_rate = current_bit_rate_; + last_bit_rate_change_ = now_ms; + } + return current_bit_rate; +} + +double MimdRateControl::RateIncreaseFactor(int64_t now_ms, + int64_t last_ms, + uint32_t reaction_time_ms, + double noise_var) const { + // alpha = 1.02 + B ./ (1 + exp(b*(tr - (c1*s2 + c2)))) + // Parameters + const double B = 0.0407; + const double b = 0.0025; + const double c1 = -6700.0 / (33 * 33); + const double c2 = 800.0; + const double d = 0.85; + + double alpha = 1.005 + B / (1 + exp( b * (d * reaction_time_ms - + (c1 * noise_var + c2)))); + + if (alpha < 1.005) { + alpha = 1.005; + } else if (alpha > 1.3) { + alpha = 1.3; + } + + if (last_ms > -1) { + alpha = pow(alpha, (now_ms - last_ms) / 1000.0); + } + + if (rate_control_region_ == kRcNearMax) { + // We're close to our previous maximum. Try to stabilize the + // bit rate in this region, by increasing in smaller steps. + alpha = alpha - (alpha - 1.0) / 2.0; + } else if (rate_control_region_ == kRcMaxUnknown) { + alpha = alpha + (alpha - 1.0) * 2.0; + } + + return alpha; +} + +void MimdRateControl::UpdateChangePeriod(int64_t now_ms) { + int64_t change_period = 0; + if (last_change_ms_ > -1) { + change_period = now_ms - last_change_ms_; + } + last_change_ms_ = now_ms; + avg_change_period_ = 0.9f * avg_change_period_ + 0.1f * change_period; +} + +void MimdRateControl::UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps) { + const float alpha = 0.05f; + if (avg_max_bit_rate_ == -1.0f) { + avg_max_bit_rate_ = incoming_bit_rate_kbps; + } else { + avg_max_bit_rate_ = (1 - alpha) * avg_max_bit_rate_ + + alpha * incoming_bit_rate_kbps; + } + // Estimate the max bit rate variance and normalize the variance + // with the average max bit rate. + const float norm = std::max(avg_max_bit_rate_, 1.0f); + var_max_bit_rate_ = (1 - alpha) * var_max_bit_rate_ + + alpha * (avg_max_bit_rate_ - incoming_bit_rate_kbps) * + (avg_max_bit_rate_ - incoming_bit_rate_kbps) / norm; + // 0.4 ~= 14 kbit/s at 500 kbit/s + if (var_max_bit_rate_ < 0.4f) { + var_max_bit_rate_ = 0.4f; + } + // 2.5f ~= 35 kbit/s at 500 kbit/s + if (var_max_bit_rate_ > 2.5f) { + var_max_bit_rate_ = 2.5f; + } +} + +void MimdRateControl::ChangeState(const RateControlInput& input, + int64_t now_ms) { + switch (current_input_._bwState) { + case kBwNormal: + if (rate_control_state_ == kRcHold) { + last_bit_rate_change_ = now_ms; + ChangeState(kRcIncrease); + } + break; + case kBwOverusing: + if (rate_control_state_ != kRcDecrease) { + ChangeState(kRcDecrease); + } + break; + case kBwUnderusing: + ChangeState(kRcHold); + break; + default: + assert(false); + } +} + +void MimdRateControl::ChangeRegion(RateControlRegion region) { + rate_control_region_ = region; + switch (rate_control_region_) { + case kRcAboveMax: + case kRcMaxUnknown: + beta_ = 0.9f; + break; + case kRcNearMax: + beta_ = 0.95f; + break; + default: + assert(false); + } +} + +void MimdRateControl::ChangeState(RateControlState new_state) { + came_from_state_ = rate_control_state_; + rate_control_state_ = new_state; +} +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h b/webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h new file mode 100644 index 000000000..fe9ddbb7e --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_MIMD_RATE_CONTROL_H_ +#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_MIMD_RATE_CONTROL_H_ + +#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" + +namespace webrtc { + +// A RemoteRateControl implementation based on multiplicative increases of +// bitrate when no over-use is detected and multiplicative decreases when +// over-uses are detected. +class MimdRateControl : public RemoteRateControl { + public: + explicit MimdRateControl(uint32_t min_bitrate_bps); + virtual ~MimdRateControl() {} + + // Implements RemoteRateControl. + virtual RateControlType GetControlType() const OVERRIDE; + virtual uint32_t GetMinBitrate() const OVERRIDE; + virtual bool ValidEstimate() const OVERRIDE; + virtual int GetFeedbackInterval() const OVERRIDE; + virtual bool TimeToReduceFurther( + int64_t time_now, uint32_t incoming_bitrate_bps) const OVERRIDE; + virtual uint32_t LatestEstimate() const OVERRIDE; + virtual uint32_t UpdateBandwidthEstimate(int64_t now_ms) OVERRIDE; + virtual void SetRtt(uint32_t rtt) OVERRIDE; + virtual RateControlRegion Update(const RateControlInput* input, + int64_t now_ms) OVERRIDE; + virtual void SetEstimate(int bitrate_bps, int64_t now_ms) OVERRIDE; + + private: + uint32_t ChangeBitRate(uint32_t current_bit_rate, + uint32_t incoming_bit_rate, + double delay_factor, + int64_t now_ms); + double RateIncreaseFactor(int64_t now_ms, + int64_t last_ms, + uint32_t reaction_time_ms, + double noise_var) const; + void UpdateChangePeriod(int64_t now_ms); + void UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps); + void ChangeState(const RateControlInput& input, int64_t now_ms); + void ChangeState(RateControlState new_state); + void ChangeRegion(RateControlRegion region); + + uint32_t min_configured_bit_rate_; + uint32_t max_configured_bit_rate_; + uint32_t current_bit_rate_; + uint32_t max_hold_rate_; + float avg_max_bit_rate_; + float var_max_bit_rate_; + RateControlState rate_control_state_; + RateControlState came_from_state_; + RateControlRegion rate_control_region_; + int64_t last_bit_rate_change_; + RateControlInput current_input_; + bool updated_; + int64_t time_first_incoming_estimate_; + bool initialized_bit_rate_; + float avg_change_period_; + int64_t last_change_ms_; + float beta_; + uint32_t rtt_; + int64_t time_of_last_log_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(MimdRateControl); +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_MIMD_RATE_CONTROL_H_ diff --git a/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc b/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc index df98450a6..fa5b8f71d 100644 --- a/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc +++ b/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc @@ -8,88 +8,34 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include -#include // fabsf - #include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h" -#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" + +#include +#include +#include + +#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/trace.h" -enum { kOverUsingTimeThreshold = 100 }; -enum { kMinFramePeriodHistoryLength = 60 }; - namespace webrtc { + +enum { kOverUsingTimeThreshold = 100 }; + OveruseDetector::OveruseDetector(const OverUseDetectorOptions& options) : options_(options), - current_frame_(), - prev_frame_(), - num_of_deltas_(0), - slope_(options_.initial_slope), - offset_(options_.initial_offset), - E_(), - process_noise_(), - avg_noise_(options_.initial_avg_noise), - var_noise_(options_.initial_var_noise), threshold_(options_.initial_threshold), - ts_delta_hist_(), prev_offset_(0.0), time_over_using_(-1), - over_use_counter_(0), - hypothesis_(kBwNormal) { - memcpy(E_, options_.initial_e, sizeof(E_)); - memcpy(process_noise_, options_.initial_process_noise, - sizeof(process_noise_)); -} + overuse_counter_(0), + hypothesis_(kBwNormal) {} -OveruseDetector::~OveruseDetector() { - ts_delta_hist_.clear(); -} - -void OveruseDetector::Update(size_t packet_size, - int64_t timestamp_ms, - uint32_t timestamp, - const int64_t arrival_time_ms) { - bool new_timestamp = (timestamp != current_frame_.timestamp); - if (timestamp_ms >= 0) { - if (prev_frame_.timestamp_ms == -1 && current_frame_.timestamp_ms == -1) { - SwitchTimeBase(); - } - new_timestamp = (timestamp_ms != current_frame_.timestamp_ms); - } - if (current_frame_.timestamp == -1) { - // This is the first incoming packet. We don't have enough data to update - // the filter, so we store it until we have two frames of data to process. - current_frame_.timestamp = timestamp; - current_frame_.timestamp_ms = timestamp_ms; - } else if (!PacketInOrder(timestamp, timestamp_ms)) { - return; - } else if (new_timestamp) { - // First packet of a later frame, the previous frame sample is ready. - if (prev_frame_.complete_time_ms >= 0) { // This is our second frame. - int64_t t_delta = 0; - double ts_delta = 0; - TimeDeltas(current_frame_, prev_frame_, &t_delta, &ts_delta); - UpdateKalman(t_delta, ts_delta, current_frame_.size, prev_frame_.size); - } - prev_frame_ = current_frame_; - // The new timestamp is now the current frame. - current_frame_.timestamp = timestamp; - current_frame_.timestamp_ms = timestamp_ms; - current_frame_.size = 0; - } - // Accumulate the frame size - current_frame_.size += packet_size; - current_frame_.complete_time_ms = arrival_time_ms; -} +OveruseDetector::~OveruseDetector() {} BandwidthUsage OveruseDetector::State() const { return hypothesis_; } -double OveruseDetector::NoiseVar() const { - return var_noise_; -} void OveruseDetector::SetRateControlRegion(RateControlRegion region) { switch (region) { @@ -105,192 +51,40 @@ void OveruseDetector::SetRateControlRegion(RateControlRegion region) { } } -void OveruseDetector::SwitchTimeBase() { - current_frame_.size = 0; - current_frame_.complete_time_ms = -1; - current_frame_.timestamp = -1; - prev_frame_ = current_frame_; -} - -void OveruseDetector::TimeDeltas(const FrameSample& current_frame, - const FrameSample& prev_frame, - int64_t* t_delta, - double* ts_delta) { - assert(t_delta); - assert(ts_delta); - num_of_deltas_++; - if (num_of_deltas_ > 1000) { - num_of_deltas_ = 1000; - } - if (current_frame.timestamp_ms == -1) { - uint32_t timestamp_diff = current_frame.timestamp - prev_frame.timestamp; - *ts_delta = timestamp_diff / 90.0; - } else { - *ts_delta = current_frame.timestamp_ms - prev_frame.timestamp_ms; - } - *t_delta = current_frame.complete_time_ms - prev_frame.complete_time_ms; - assert(*ts_delta > 0); -} - -bool OveruseDetector::PacketInOrder(uint32_t timestamp, int64_t timestamp_ms) { - if (current_frame_.timestamp_ms == -1 && current_frame_.timestamp > -1) { - return InOrderTimestamp(timestamp, current_frame_.timestamp); - } else if (current_frame_.timestamp_ms > 0) { - // Using timestamps converted to NTP time. - return timestamp_ms > current_frame_.timestamp_ms; - } - // This is the first packet. - return true; -} - -bool OveruseDetector::InOrderTimestamp(uint32_t timestamp, - uint32_t prev_timestamp) { - uint32_t timestamp_diff = timestamp - prev_timestamp; - // Assume that a diff this big must be due to reordering. Don't update - // with reordered samples. - return (timestamp_diff < 0x80000000); -} - -double OveruseDetector::CurrentDrift() { - return 1.0; -} - -void OveruseDetector::UpdateKalman(int64_t t_delta, - double ts_delta, - size_t frame_size, - size_t prev_frame_size) { - const double min_frame_period = UpdateMinFramePeriod(ts_delta); - const double drift = CurrentDrift(); - // Compensate for drift - const double t_ts_delta = t_delta - ts_delta / drift; - double fs_delta = static_cast(frame_size) - prev_frame_size; - - // Update the Kalman filter - const double scale_factor = min_frame_period / (1000.0 / 30.0); - E_[0][0] += process_noise_[0] * scale_factor; - E_[1][1] += process_noise_[1] * scale_factor; - - if ((hypothesis_ == kBwOverusing && offset_ < prev_offset_) || - (hypothesis_ == kBwUnderusing && offset_ > prev_offset_)) { - E_[1][1] += 10 * process_noise_[1] * scale_factor; - } - - const double h[2] = {fs_delta, 1.0}; - const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1], - E_[1][0]*h[0] + E_[1][1]*h[1]}; - - const double residual = t_ts_delta - slope_*h[0] - offset_; - - const bool stable_state = - (BWE_MIN(num_of_deltas_, 60) * fabs(offset_) < threshold_); - // We try to filter out very late frames. For instance periodic key - // frames doesn't fit the Gaussian model well. - if (fabs(residual) < 3 * sqrt(var_noise_)) { - UpdateNoiseEstimate(residual, min_frame_period, stable_state); - } else { - UpdateNoiseEstimate(3 * sqrt(var_noise_), min_frame_period, stable_state); - } - - const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1]; - - const double K[2] = {Eh[0] / denom, - Eh[1] / denom}; - - const double IKh[2][2] = {{1.0 - K[0]*h[0], -K[0]*h[1]}, - {-K[1]*h[0], 1.0 - K[1]*h[1]}}; - const double e00 = E_[0][0]; - const double e01 = E_[0][1]; - - // Update state - E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1]; - E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1]; - E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1]; - E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1]; - - // Covariance matrix, must be positive semi-definite - assert(E_[0][0] + E_[1][1] >= 0 && - E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && - E_[0][0] >= 0); - - slope_ = slope_ + K[0] * residual; - prev_offset_ = offset_; - offset_ = offset_ + K[1] * residual; - - Detect(ts_delta); -} - -double OveruseDetector::UpdateMinFramePeriod(double ts_delta) { - double min_frame_period = ts_delta; - if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) { - std::list::iterator first_item = ts_delta_hist_.begin(); - ts_delta_hist_.erase(first_item); - } - std::list::iterator it = ts_delta_hist_.begin(); - for (; it != ts_delta_hist_.end(); it++) { - min_frame_period = BWE_MIN(*it, min_frame_period); - } - ts_delta_hist_.push_back(ts_delta); - return min_frame_period; -} - -void OveruseDetector::UpdateNoiseEstimate(double residual, - double ts_delta, - bool stable_state) { - if (!stable_state) { - return; - } - // Faster filter during startup to faster adapt to the jitter level - // of the network alpha is tuned for 30 frames per second, but - double alpha = 0.01; - if (num_of_deltas_ > 10*30) { - alpha = 0.002; - } - // Only update the noise estimate if we're not over-using - // beta is a function of alpha and the time delta since - // the previous update. - const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0); - avg_noise_ = beta * avg_noise_ - + (1 - beta) * residual; - var_noise_ = beta * var_noise_ - + (1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual); - if (var_noise_ < 1e-7) { - var_noise_ = 1e-7; - } -} - -BandwidthUsage OveruseDetector::Detect(double ts_delta) { - if (num_of_deltas_ < 2) { +BandwidthUsage OveruseDetector::Detect(double offset, double ts_delta, + int num_of_deltas) { + if (num_of_deltas < 2) { return kBwNormal; } - const double T = BWE_MIN(num_of_deltas_, 60) * offset_; - if (fabs(T) > threshold_) { - if (offset_ > 0) { - if (time_over_using_ == -1) { - // Initialize the timer. Assume that we've been - // over-using half of the time since the previous - // sample. - time_over_using_ = ts_delta / 2; - } else { - // Increment timer - time_over_using_ += ts_delta; - } - over_use_counter_++; - if (time_over_using_ > kOverUsingTimeThreshold - && over_use_counter_ > 1) { - if (offset_ >= prev_offset_) { - time_over_using_ = 0; - over_use_counter_ = 0; - hypothesis_ = kBwOverusing; - } - } + const double prev_offset = prev_offset_; + prev_offset_ = offset; + const double T = std::min(num_of_deltas, 60) * offset; + if (T > threshold_) { + if (time_over_using_ == -1) { + // Initialize the timer. Assume that we've been + // over-using half of the time since the previous + // sample. + time_over_using_ = ts_delta / 2; } else { - time_over_using_ = -1; - over_use_counter_ = 0; - hypothesis_ = kBwUnderusing; + // Increment timer + time_over_using_ += ts_delta; } + overuse_counter_++; + if (time_over_using_ > kOverUsingTimeThreshold + && overuse_counter_ > 1) { + if (offset >= prev_offset) { + time_over_using_ = 0; + overuse_counter_ = 0; + hypothesis_ = kBwOverusing; + } + } + } else if (T < -threshold_) { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = kBwUnderusing; } else { time_over_using_ = -1; - over_use_counter_ = 0; + overuse_counter_ = 0; hypothesis_ = kBwNormal; } return hypothesis_; diff --git a/webrtc/modules/remote_bitrate_estimator/overuse_detector.h b/webrtc/modules/remote_bitrate_estimator/overuse_detector.h index f80c92c29..2a6cdd764 100644 --- a/webrtc/modules/remote_bitrate_estimator/overuse_detector.h +++ b/webrtc/modules/remote_bitrate_estimator/overuse_detector.h @@ -7,11 +7,12 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_ +#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ +#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ #include +#include "webrtc/base/constructormagic.h" #include "webrtc/modules/interface/module_common_types.h" #include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" #include "webrtc/typedefs.h" @@ -19,76 +20,39 @@ namespace webrtc { enum RateControlRegion; -// This class is assumed to be protected by the owner if used by multiple -// threads. class OveruseDetector { public: explicit OveruseDetector(const OverUseDetectorOptions& options); ~OveruseDetector(); - void Update(size_t packet_size, - int64_t timestamp_ms, - uint32_t rtp_timestamp, - int64_t arrival_time_ms); + + // Update the detection state based on the estimated inter-arrival time delta + // offset. |timestamp_delta| is the delta between the last timestamp which the + // estimated offset is based on and the last timestamp on which the last + // offset was based on, representing the time between detector updates. + // |num_of_deltas| is the number of deltas the offset estimate is based on. + // Returns the state after the detection update. + BandwidthUsage Detect(double offset, double timestamp_delta, + int num_of_deltas); + + // Returns the current detector state. BandwidthUsage State() const; - double NoiseVar() const; - void SetRateControlRegion(RateControlRegion region); + + // Sets the current rate-control region as decided by RemoteRateControl. This + // affects the sensitivity of the detector. + void SetRateControlRegion(webrtc::RateControlRegion region); private: - struct FrameSample { - FrameSample() - : size(0), - complete_time_ms(-1), - timestamp(-1), - timestamp_ms(-1) {} - - size_t size; - int64_t complete_time_ms; - int64_t timestamp; - int64_t timestamp_ms; - }; - - // Returns true if |timestamp| represent a time which is later than - // |prev_timestamp|. - static bool InOrderTimestamp(uint32_t timestamp, uint32_t prev_timestamp); - - bool PacketInOrder(uint32_t timestamp, int64_t timestamp_ms); - - // Prepares the overuse detector to start using timestamps in milliseconds - // instead of 90 kHz timestamps. - void SwitchTimeBase(); - - void TimeDeltas(const FrameSample& current_frame, - const FrameSample& prev_frame, - int64_t* t_delta, - double* ts_delta); - void UpdateKalman(int64_t t_delta, - double ts_elta, - size_t frame_size, - size_t prev_frame_size); - double UpdateMinFramePeriod(double ts_delta); - void UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state); - BandwidthUsage Detect(double ts_delta); - double CurrentDrift(); - - OverUseDetectorOptions options_; // Must be first member - // variable. Cannot be const - // because we need to be copyable. - FrameSample current_frame_; - FrameSample prev_frame_; - uint16_t num_of_deltas_; - double slope_; - double offset_; - double E_[2][2]; - double process_noise_[2]; - double avg_noise_; - double var_noise_; + // Must be first member variable. Cannot be const because we need to be + // copyable. + webrtc::OverUseDetectorOptions options_; double threshold_; - std::list ts_delta_hist_; double prev_offset_; double time_over_using_; - uint16_t over_use_counter_; + int overuse_counter_; BandwidthUsage hypothesis_; + + DISALLOW_COPY_AND_ASSIGN(OveruseDetector); }; } // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_OVERUSE_DETECTOR_H_ +#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ diff --git a/webrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc b/webrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc new file mode 100644 index 000000000..564653481 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc @@ -0,0 +1,605 @@ +/* + * Copyright (c) 2012 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 +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/common_types.h" +#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h" +#include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h" +#include "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/test/testsupport/gtest_disable.h" + +namespace webrtc { +namespace testing { + +const double kRtpTimestampToMs = 1.0 / 90.0; + +class OveruseDetectorTest : public ::testing::Test { + protected: + void SetUp() { + srand(1234); + now_ms_ = 0; + receive_time_ms_ = 0; + rtp_timestamp_ = 10 * 90; + overuse_detector_.reset(new OveruseDetector(options_)); + overuse_estimator_.reset(new OveruseEstimator(options_)); + inter_arrival_.reset(new InterArrival(5 * 90, kRtpTimestampToMs, true)); + } + // Normal Distribution. + #define PI 3.14159265 + int GaussianRandom(int mean_ms, int standard_deviation_ms) { + // Creating a Normal distribution variable from two independent uniform + // variables based on the Box-Muller transform. + double uniform1 = (std::rand() + 1.0) / (RAND_MAX + 1.0); + double uniform2 = (std::rand() + 1.0) / (RAND_MAX + 1.0); + return static_cast(mean_ms + standard_deviation_ms * + sqrt(-2 * log(uniform1)) * cos(2 * PI * uniform2)); + } + + int Run100000Samples(int packets_per_frame, size_t packet_size, int mean_ms, + int standard_deviation_ms) { + int unique_overuse = 0; + int last_overuse = -1; + for (int i = 0; i < 100000; ++i) { + for (int j = 0; j < packets_per_frame; ++j) { + UpdateDetector(rtp_timestamp_, receive_time_ms_, packet_size); + } + rtp_timestamp_ += mean_ms * 90; + now_ms_ += mean_ms; + receive_time_ms_ = std::max(receive_time_ms_, + now_ms_ + GaussianRandom(0, standard_deviation_ms)); + if (kBwOverusing == overuse_detector_->State()) { + if (last_overuse + 1 != i) { + unique_overuse++; + } + last_overuse = i; + } + } + return unique_overuse; + } + + int RunUntilOveruse(int packets_per_frame, size_t packet_size, int mean_ms, + int standard_deviation_ms, int drift_per_frame_ms) { + // Simulate a higher send pace, that is too high. + for (int i = 0; i < 1000; ++i) { + for (int j = 0; j < packets_per_frame; ++j) { + UpdateDetector(rtp_timestamp_, receive_time_ms_, packet_size); + } + rtp_timestamp_ += mean_ms * 90; + now_ms_ += mean_ms + drift_per_frame_ms; + receive_time_ms_ = std::max(receive_time_ms_, + now_ms_ + GaussianRandom(0, standard_deviation_ms)); + if (kBwOverusing == overuse_detector_->State()) { + return i + 1; + } + } + return -1; + } + + void UpdateDetector(uint32_t rtp_timestamp, int64_t receive_time_ms, + size_t packet_size) { + uint32_t timestamp_delta; + int64_t time_delta; + int size_delta; + if (inter_arrival_->ComputeDeltas(rtp_timestamp, + receive_time_ms, + packet_size, + ×tamp_delta, + &time_delta, + &size_delta)) { + double timestamp_delta_ms = timestamp_delta / 90.0; + overuse_estimator_->Update(time_delta, timestamp_delta_ms, size_delta, + overuse_detector_->State()); + overuse_detector_->Detect(overuse_estimator_->offset(), + timestamp_delta_ms, + overuse_estimator_->num_of_deltas()); + } + } + + int64_t now_ms_; + int64_t receive_time_ms_; + uint32_t rtp_timestamp_; + OverUseDetectorOptions options_; + scoped_ptr overuse_detector_; + scoped_ptr overuse_estimator_; + scoped_ptr inter_arrival_; +}; + +TEST_F(OveruseDetectorTest, GaussianRandom) { + int buckets[100]; + memset(buckets, 0, sizeof(buckets)); + for (int i = 0; i < 100000; ++i) { + int index = GaussianRandom(49, 10); + if (index >= 0 && index < 100) + buckets[index]++; + } + for (int n = 0; n < 100; ++n) { + printf("Bucket n:%d, %d\n", n, buckets[n]); + } +} + +TEST_F(OveruseDetectorTest, SimpleNonOveruse30fps) { + size_t packet_size = 1200; + uint32_t frame_duration_ms = 33; + uint32_t rtp_timestamp = 10 * 90; + + // No variance. + for (int i = 0; i < 1000; ++i) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + now_ms_ += frame_duration_ms; + rtp_timestamp += frame_duration_ms * 90; + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } +} + +// Roughly 1 Mbit/s +TEST_F(OveruseDetectorTest, SimpleNonOveruseWithReceiveVariance) { + uint32_t frame_duration_ms = 10; + uint32_t rtp_timestamp = 10 * 90; + size_t packet_size = 1200; + + for (int i = 0; i < 1000; ++i) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + rtp_timestamp += frame_duration_ms * 90; + if (i % 2) { + now_ms_ += frame_duration_ms - 5; + } else { + now_ms_ += frame_duration_ms + 5; + } + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } +} + +TEST_F(OveruseDetectorTest, SimpleNonOveruseWithRtpTimestampVariance) { + // Roughly 1 Mbit/s. + uint32_t frame_duration_ms = 10; + uint32_t rtp_timestamp = 10 * 90; + size_t packet_size = 1200; + + for (int i = 0; i < 1000; ++i) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + now_ms_ += frame_duration_ms; + if (i % 2) { + rtp_timestamp += (frame_duration_ms - 5) * 90; + } else { + rtp_timestamp += (frame_duration_ms + 5) * 90; + } + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } +} + +TEST_F(OveruseDetectorTest, SimpleOveruse2000Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 6; + int frame_duration_ms = 33; + int drift_per_frame_ms = 1; + int sigma_ms = 0; // No variance. + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_EQ(6, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, SimpleOveruse100kbit10fps) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 100; + int drift_per_frame_ms = 1; + int sigma_ms = 0; // No variance. + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_EQ(4, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, DISABLED_OveruseWithHighVariance100Kbit10fps) { + uint32_t frame_duration_ms = 100; + uint32_t drift_per_frame_ms = 10; + uint32_t rtp_timestamp = frame_duration_ms * 90; + size_t packet_size = 1200; + int offset = 10; + + // Run 1000 samples to reach steady state. + for (int i = 0; i < 1000; ++i) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + rtp_timestamp += frame_duration_ms * 90; + if (i % 2) { + offset = rand() % 50; + now_ms_ += frame_duration_ms - offset; + } else { + now_ms_ += frame_duration_ms + offset; + } + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } + // Simulate a higher send pace, that is too high. + // Above noise generate a standard deviation of approximately 28 ms. + // Total build up of 150 ms. + for (int j = 0; j < 15; ++j) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + now_ms_ += frame_duration_ms + drift_per_frame_ms; + rtp_timestamp += frame_duration_ms * 90; + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + EXPECT_EQ(kBwOverusing, overuse_detector_->State()); +} + +TEST_F(OveruseDetectorTest, DISABLED_OveruseWithLowVariance100Kbit10fps) { + uint32_t frame_duration_ms = 100; + uint32_t drift_per_frame_ms = 1; + uint32_t rtp_timestamp = frame_duration_ms * 90; + size_t packet_size = 1200; + int offset = 10; + + // Run 1000 samples to reach steady state. + for (int i = 0; i < 1000; ++i) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + rtp_timestamp += frame_duration_ms * 90; + if (i % 2) { + offset = rand() % 2; + now_ms_ += frame_duration_ms - offset; + } else { + now_ms_ += frame_duration_ms + offset; + } + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } + // Simulate a higher send pace, that is too high. + // Total build up of 6 ms. + for (int j = 0; j < 6; ++j) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + now_ms_ += frame_duration_ms + drift_per_frame_ms; + rtp_timestamp += frame_duration_ms * 90; + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + EXPECT_EQ(kBwOverusing, overuse_detector_->State()); +} + +TEST_F(OveruseDetectorTest, OveruseWithLowVariance2000Kbit30fps) { + uint32_t frame_duration_ms = 33; + uint32_t drift_per_frame_ms = 1; + uint32_t rtp_timestamp = frame_duration_ms * 90; + size_t packet_size = 1200; + int offset = 0; + + // Run 1000 samples to reach steady state. + for (int i = 0; i < 1000; ++i) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + rtp_timestamp += frame_duration_ms * 90; + if (i % 2) { + offset = rand() % 2; + now_ms_ += frame_duration_ms - offset; + } else { + now_ms_ += frame_duration_ms + offset; + } + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } + // Simulate a higher send pace, that is too high. + // Total build up of 30 ms. + for (int j = 0; j < 5; ++j) { + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + now_ms_ += frame_duration_ms + drift_per_frame_ms * 6; + rtp_timestamp += frame_duration_ms * 90; + EXPECT_EQ(kBwNormal, overuse_detector_->State()); + } + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + EXPECT_EQ(kBwOverusing, overuse_detector_->State()); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(LowGaussianVariance30Kbit3fps)) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 333; + int drift_per_frame_ms = 1; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(29, frames_until_overuse, 5); +} + +TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift30Kbit3fps) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 333; + int drift_per_frame_ms = 100; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(4, frames_until_overuse, 1); +} + +TEST_F(OveruseDetectorTest, HighGaussianVariance30Kbit3fps) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 333; + int drift_per_frame_ms = 1; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(79, frames_until_overuse, 30); +} + +TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift30Kbit3fps) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 333; + int drift_per_frame_ms = 100; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(4, frames_until_overuse, 1); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(LowGaussianVariance100Kbit5fps)) { + size_t packet_size = 1200; + int packets_per_frame = 2; + int frame_duration_ms = 200; + int drift_per_frame_ms = 1; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(29, frames_until_overuse, 5); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(HighGaussianVariance100Kbit5fps)) { + size_t packet_size = 1200; + int packets_per_frame = 2; + int frame_duration_ms = 200; + int drift_per_frame_ms = 1; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(79, frames_until_overuse, 15); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(LowGaussianVariance100Kbit10fps)) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 100; + int drift_per_frame_ms = 1; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(29, frames_until_overuse, 5); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(HighGaussianVariance100Kbit10fps)) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 100; + int drift_per_frame_ms = 1; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(79, frames_until_overuse, 15); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(LowGaussianVariance300Kbit30fps)) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 33; + int drift_per_frame_ms = 1; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(30, frames_until_overuse, 5); +} + +TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift300Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 33; + int drift_per_frame_ms = 10; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(7, frames_until_overuse, 1); +} + +TEST_F(OveruseDetectorTest, HighGaussianVariance300Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 33; + int drift_per_frame_ms = 1; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(98, frames_until_overuse, 22); +} + +TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift300Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 1; + int frame_duration_ms = 33; + int drift_per_frame_ms = 10; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(12, frames_until_overuse, 2); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(LowGaussianVariance1000Kbit30fps)) { + size_t packet_size = 1200; + int packets_per_frame = 3; + int frame_duration_ms = 33; + int drift_per_frame_ms = 1; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(30, frames_until_overuse, 5); +} + +TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift1000Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 3; + int frame_duration_ms = 33; + int drift_per_frame_ms = 10; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(7, frames_until_overuse, 1); +} + +TEST_F(OveruseDetectorTest, HighGaussianVariance1000Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 3; + int frame_duration_ms = 33; + int drift_per_frame_ms = 1; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(98, frames_until_overuse, 22); +} + +TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift1000Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 3; + int frame_duration_ms = 33; + int drift_per_frame_ms = 10; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(12, frames_until_overuse, 2); +} + +TEST_F(OveruseDetectorTest, + DISABLED_ON_ANDROID(LowGaussianVariance2000Kbit30fps)) { + size_t packet_size = 1200; + int packets_per_frame = 6; + int frame_duration_ms = 33; + int drift_per_frame_ms = 1; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(30, frames_until_overuse, 5); +} + +TEST_F(OveruseDetectorTest, LowGaussianVarianceFastDrift2000Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 6; + int frame_duration_ms = 33; + int drift_per_frame_ms = 10; + int sigma_ms = 3; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(7, frames_until_overuse, 1); +} + +TEST_F(OveruseDetectorTest, HighGaussianVariance2000Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 6; + int frame_duration_ms = 33; + int drift_per_frame_ms = 1; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(98, frames_until_overuse, 22); +} + +TEST_F(OveruseDetectorTest, HighGaussianVarianceFastDrift2000Kbit30fps) { + size_t packet_size = 1200; + int packets_per_frame = 6; + int frame_duration_ms = 33; + int drift_per_frame_ms = 10; + int sigma_ms = 10; + int unique_overuse = Run100000Samples(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms); + EXPECT_EQ(0, unique_overuse); + int frames_until_overuse = RunUntilOveruse(packets_per_frame, packet_size, + frame_duration_ms, sigma_ms, drift_per_frame_ms); + EXPECT_NEAR(12, frames_until_overuse, 2); +} +} // namespace testing +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/overuse_estimator.cc b/webrtc/modules/remote_bitrate_estimator/overuse_estimator.cc new file mode 100644 index 000000000..2f6e3305a --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/overuse_estimator.cc @@ -0,0 +1,153 @@ +/* + * 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 "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h" + +#include +#include +#include +#include +#include + +#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "webrtc/system_wrappers/interface/logging.h" + +namespace webrtc { + +enum { kMinFramePeriodHistoryLength = 60 }; +enum { kDeltaCounterMax = 1000 }; + +OveruseEstimator::OveruseEstimator(const OverUseDetectorOptions& options) + : options_(options), + num_of_deltas_(0), + slope_(options_.initial_slope), + offset_(options_.initial_offset), + prev_offset_(options_.initial_offset), + E_(), + process_noise_(), + avg_noise_(options_.initial_avg_noise), + var_noise_(options_.initial_var_noise), + ts_delta_hist_() { + memcpy(E_, options_.initial_e, sizeof(E_)); + memcpy(process_noise_, options_.initial_process_noise, + sizeof(process_noise_)); +} + +OveruseEstimator::~OveruseEstimator() { + ts_delta_hist_.clear(); +} + +void OveruseEstimator::Update(int64_t t_delta, + double ts_delta, + int size_delta, + BandwidthUsage current_hypothesis) { + const double min_frame_period = UpdateMinFramePeriod(ts_delta); + const double t_ts_delta = t_delta - ts_delta; + double fs_delta = size_delta; + + ++num_of_deltas_; + if (num_of_deltas_ > kDeltaCounterMax) { + num_of_deltas_ = kDeltaCounterMax; + } + + // Update the Kalman filter. + E_[0][0] += process_noise_[0]; + E_[1][1] += process_noise_[1]; + + if ((current_hypothesis == kBwOverusing && offset_ < prev_offset_) || + (current_hypothesis == kBwUnderusing && offset_ > prev_offset_)) { + E_[1][1] += 10 * process_noise_[1]; + } + + const double h[2] = {fs_delta, 1.0}; + const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1], + E_[1][0]*h[0] + E_[1][1]*h[1]}; + + const double residual = t_ts_delta - slope_*h[0] - offset_; + + const bool in_stable_state = (current_hypothesis == kBwNormal); + const double max_residual = 3.0 * sqrt(var_noise_); + // We try to filter out very late frames. For instance periodic key + // frames doesn't fit the Gaussian model well. + if (fabs(residual) < max_residual) { + UpdateNoiseEstimate(residual, min_frame_period, in_stable_state); + } else { + UpdateNoiseEstimate(residual < 0 ? -max_residual : max_residual, + min_frame_period, in_stable_state); + } + + const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1]; + + const double K[2] = {Eh[0] / denom, + Eh[1] / denom}; + + const double IKh[2][2] = {{1.0 - K[0]*h[0], -K[0]*h[1]}, + {-K[1]*h[0], 1.0 - K[1]*h[1]}}; + const double e00 = E_[0][0]; + const double e01 = E_[0][1]; + + // Update state. + E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1]; + E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1]; + E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1]; + E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1]; + + // The covariance matrix must be positive semi-definite. + bool positive_semi_definite = E_[0][0] + E_[1][1] >= 0 && + E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 && E_[0][0] >= 0; + assert(positive_semi_definite); + if (!positive_semi_definite) { + LOG(LS_ERROR) << "The over-use estimator's covariance matrix is no longer " + "semi-definite."; + } + + slope_ = slope_ + K[0] * residual; + prev_offset_ = offset_; + offset_ = offset_ + K[1] * residual; +} + +double OveruseEstimator::UpdateMinFramePeriod(double ts_delta) { + double min_frame_period = ts_delta; + if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) { + ts_delta_hist_.pop_front(); + } + std::list::iterator it = ts_delta_hist_.begin(); + for (; it != ts_delta_hist_.end(); it++) { + min_frame_period = std::min(*it, min_frame_period); + } + ts_delta_hist_.push_back(ts_delta); + return min_frame_period; +} + +void OveruseEstimator::UpdateNoiseEstimate(double residual, + double ts_delta, + bool stable_state) { + if (!stable_state) { + return; + } + // Faster filter during startup to faster adapt to the jitter level + // of the network. |alpha| is tuned for 30 frames per second, but is scaled + // according to |ts_delta|. + double alpha = 0.01; + if (num_of_deltas_ > 10*30) { + alpha = 0.002; + } + // Only update the noise estimate if we're not over-using. |beta| is a + // function of alpha and the time delta since the previous update. + const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0); + avg_noise_ = beta * avg_noise_ + + (1 - beta) * residual; + var_noise_ = beta * var_noise_ + + (1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual); + if (var_noise_ < 1e-7) { + var_noise_ = 1e-7; + } +} +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/overuse_estimator.h b/webrtc/modules/remote_bitrate_estimator/overuse_estimator.h new file mode 100644 index 000000000..6499d8d04 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/overuse_estimator.h @@ -0,0 +1,70 @@ +/* + * 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. + */ +#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_ +#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_ + +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_types.h" +#include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" + +namespace webrtc { + +class OveruseEstimator { + public: + explicit OveruseEstimator(const OverUseDetectorOptions& options); + ~OveruseEstimator(); + + // Update the estimator with a new sample. The deltas should represent deltas + // between timestamp groups as defined by the InterArrival class. + // |current_hypothesis| should be the hypothesis of the over-use detector at + // this time. + void Update(int64_t t_delta, double ts_delta, int size_delta, + BandwidthUsage current_hypothesis); + + // Returns the estimated noise/jitter variance in ms^2. + double var_noise() const { + return var_noise_; + } + + // Returns the estimated inter-arrival time delta offset in ms. + double offset() const { + return offset_; + } + + // Returns the number of deltas which the current over-use estimator state is + // based on. + unsigned int num_of_deltas() const { + return num_of_deltas_; + } + + private: + double UpdateMinFramePeriod(double ts_delta); + void UpdateNoiseEstimate(double residual, double ts_delta, bool stable_state); + + // Must be first member variable. Cannot be const because we need to be + // copyable. + OverUseDetectorOptions options_; + uint16_t num_of_deltas_; + double slope_; + double offset_; + double prev_offset_; + double E_[2][2]; + double process_noise_[2]; + double avg_noise_; + double var_noise_; + std::list ts_delta_hist_; + + DISALLOW_COPY_AND_ASSIGN(OveruseEstimator); +}; +} // namespace webrtc + +#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_ diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi index f88a16a25..4636eca64 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi @@ -15,8 +15,8 @@ 'target_name': 'remote_bitrate_estimator', 'type': 'static_library', 'dependencies': [ + '<(webrtc_root)/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp:rbe_components', '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', - '<(rbe_components_path)/remote_bitrate_estimator_components.gyp:rbe_components', ], 'sources': [ 'include/bwe_defines.h', diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc new file mode 100644 index 000000000..9a723fcb7 --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc @@ -0,0 +1,499 @@ +/* + * 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 +#include + +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/thread_annotations.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h" +#include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h" +#include "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h" +#include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" +#include "webrtc/modules/remote_bitrate_estimator/rate_statistics.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/logging.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" +#include "webrtc/typedefs.h" + +namespace webrtc { + +enum { + kTimestampGroupLengthMs = 5, + kAbsSendTimeFraction = 18, + kAbsSendTimeInterArrivalUpshift = 8, + kInterArrivalShift = kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift, + kInitialProbingIntervalMs = 2000, + kMinClusterSize = 4, + kMaxProbePackets = 15, + kExpectedNumberOfProbes = 3 +}; + +static const size_t kPropagationDeltaQueueMaxSize = 1000; +static const int64_t kPropagationDeltaQueueMaxTimeMs = 1000; +static const double kTimestampToMs = 1000.0 / + static_cast(1 << kInterArrivalShift); + +// Removes the entries at |index| of |time| and |value|, if time[index] is +// smaller than or equal to |deadline|. |time| must be sorted ascendingly. +static void RemoveStaleEntries( + std::vector* time, std::vector* value, int64_t deadline) { + assert(time->size() == value->size()); + std::vector::iterator end_of_removal = std::upper_bound( + time->begin(), time->end(), deadline); + size_t end_of_removal_index = end_of_removal - time->begin(); + + time->erase(time->begin(), end_of_removal); + value->erase(value->begin(), value->begin() + end_of_removal_index); +} + +template +std::vector Keys(const std::map& map) { + std::vector keys; + keys.reserve(map.size()); + for (typename std::map::const_iterator it = map.begin(); + it != map.end(); ++it) { + keys.push_back(it->first); + } + return keys; +} + +class RemoteBitrateEstimatorAbsSendTimeImpl : public RemoteBitrateEstimator { + public: + RemoteBitrateEstimatorAbsSendTimeImpl(RemoteBitrateObserver* observer, + Clock* clock, + RateControlType control_type, + uint32_t min_bitrate_bps); + virtual ~RemoteBitrateEstimatorAbsSendTimeImpl() {} + + virtual void IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) OVERRIDE; + // This class relies on Process() being called periodically (at least once + // every other second) for streams to be timed out properly. Therefore it + // shouldn't be detached from the ProcessThread except if it's about to be + // deleted. + virtual int32_t Process() OVERRIDE; + virtual int32_t TimeUntilNextProcess() OVERRIDE; + virtual void OnRttUpdate(uint32_t rtt) OVERRIDE; + virtual void RemoveStream(unsigned int ssrc) OVERRIDE; + virtual bool LatestEstimate(std::vector* ssrcs, + unsigned int* bitrate_bps) const OVERRIDE; + virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const OVERRIDE; + + private: + typedef std::map Ssrcs; + + struct Probe { + Probe(int64_t send_time_ms, int64_t recv_time_ms, size_t payload_size) + : send_time_ms(send_time_ms), + recv_time_ms(recv_time_ms), + payload_size(payload_size) {} + int64_t send_time_ms; + int64_t recv_time_ms; + size_t payload_size; + }; + + struct Cluster { + Cluster() + : send_mean_ms(0.0f), + recv_mean_ms(0.0f), + mean_size(0), + count(0), + num_above_min_delta(0) {} + + float send_mean_ms; + float recv_mean_ms; + // TODO(holmer): Add some variance metric as well? + size_t mean_size; + int count; + int num_above_min_delta; + }; + + static bool IsWithinClusterBounds(int send_delta_ms, + const Cluster& cluster_aggregate) { + if (cluster_aggregate.count == 0) + return true; + float cluster_mean = cluster_aggregate.send_mean_ms / + static_cast(cluster_aggregate.count); + return fabs(static_cast(send_delta_ms) - cluster_mean) < 2.5f; + } + + static void AddCluster(std::list* clusters, Cluster* cluster) { + cluster->send_mean_ms /= static_cast(cluster->count); + cluster->recv_mean_ms /= static_cast(cluster->count); + cluster->mean_size /= cluster->count; + clusters->push_back(*cluster); + } + + int Id() const { + return static_cast(reinterpret_cast(this)); + } + + bool IsProbe(int64_t send_time_ms, int payload_size) const + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get()); + + // Triggers a new estimate calculation. + void UpdateEstimate(int64_t now_ms) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get()); + + void UpdateStats(int propagation_delta_ms, int64_t now_ms) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get()); + + void ComputeClusters(std::list* clusters) const; + + int FindBestProbeBitrate(const std::list& clusters) const; + + void ProcessClusters(int64_t now_ms) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get()); + + scoped_ptr crit_sect_; + RemoteBitrateObserver* observer_ GUARDED_BY(crit_sect_.get()); + Clock* clock_; + Ssrcs ssrcs_ GUARDED_BY(crit_sect_.get()); + scoped_ptr inter_arrival_ GUARDED_BY(crit_sect_.get()); + OveruseEstimator estimator_ GUARDED_BY(crit_sect_.get()); + OveruseDetector detector_ GUARDED_BY(crit_sect_.get()); + RateStatistics incoming_bitrate_ GUARDED_BY(crit_sect_.get()); + scoped_ptr remote_rate_ GUARDED_BY(crit_sect_.get()); + int64_t last_process_time_; + std::vector recent_propagation_delta_ms_ GUARDED_BY(crit_sect_.get()); + std::vector recent_update_time_ms_ GUARDED_BY(crit_sect_.get()); + int process_interval_ms_ GUARDED_BY(crit_sect_.get()); + int total_propagation_delta_ms_ GUARDED_BY(crit_sect_.get()); + + std::list probes_; + int64_t first_packet_time_ms_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RemoteBitrateEstimatorAbsSendTimeImpl); +}; + +RemoteBitrateEstimatorAbsSendTimeImpl::RemoteBitrateEstimatorAbsSendTimeImpl( + RemoteBitrateObserver* observer, + Clock* clock, + RateControlType control_type, + uint32_t min_bitrate_bps) + : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), + observer_(observer), + clock_(clock), + ssrcs_(), + inter_arrival_(), + estimator_(OverUseDetectorOptions()), + detector_(OverUseDetectorOptions()), + incoming_bitrate_(1000, 8000), + remote_rate_(RemoteRateControl::Create(control_type, min_bitrate_bps)), + last_process_time_(-1), + process_interval_ms_(kProcessIntervalMs), + total_propagation_delta_ms_(0), + first_packet_time_ms_(-1) { + assert(observer_); + assert(clock_); +} + +void RemoteBitrateEstimatorAbsSendTimeImpl::ComputeClusters( + std::list* clusters) const { + Cluster current; + int64_t prev_send_time = -1; + int64_t prev_recv_time = -1; + for (std::list::const_iterator it = probes_.begin(); + it != probes_.end(); + ++it) { + if (prev_send_time >= 0) { + int send_delta_ms = it->send_time_ms - prev_send_time; + int recv_delta_ms = it->recv_time_ms - prev_recv_time; + if (send_delta_ms > 1 && recv_delta_ms > 1) { + ++current.num_above_min_delta; + } + if (!IsWithinClusterBounds(send_delta_ms, current)) { + if (current.count >= kMinClusterSize) + AddCluster(clusters, ¤t); + current = Cluster(); + } + current.send_mean_ms += send_delta_ms; + current.recv_mean_ms += recv_delta_ms; + current.mean_size += it->payload_size; + ++current.count; + } + prev_send_time = it->send_time_ms; + prev_recv_time = it->recv_time_ms; + } + if (current.count >= kMinClusterSize) + AddCluster(clusters, ¤t); +} + +int RemoteBitrateEstimatorAbsSendTimeImpl::FindBestProbeBitrate( + const std::list& clusters) const { + int highest_probe_bitrate_bps = 0; + for (std::list::const_iterator it = clusters.begin(); + it != clusters.end(); + ++it) { + if (it->send_mean_ms == 0 || it->recv_mean_ms == 0) + continue; + int send_bitrate_bps = it->mean_size * 8 * 1000 / it->send_mean_ms; + int recv_bitrate_bps = it->mean_size * 8 * 1000 / it->recv_mean_ms; + if (it->num_above_min_delta > it->count / 2 && + (it->recv_mean_ms - it->send_mean_ms <= 1.5f || + it->send_mean_ms - it->recv_mean_ms >= 3.0f)) { + int probe_bitrate_bps = std::min(send_bitrate_bps, recv_bitrate_bps); + if (probe_bitrate_bps > highest_probe_bitrate_bps) + highest_probe_bitrate_bps = probe_bitrate_bps; + LOG(LS_INFO) << "Probe successful, sent at " << send_bitrate_bps + << " bps, received at " << recv_bitrate_bps + << " bps. Mean send delta: " << it->send_mean_ms + << " ms, mean recv delta: " << it->recv_mean_ms + << " ms, num probes: " << it->count; + } else { + LOG(LS_INFO) << "Probe failed, sent at " << send_bitrate_bps + << " bps, received at " << recv_bitrate_bps + << " bps. Mean send delta: " << it->send_mean_ms + << " ms, mean recv delta: " << it->recv_mean_ms + << " ms, num probes: " << it->count; + break; + } + } + return highest_probe_bitrate_bps; +} + +void RemoteBitrateEstimatorAbsSendTimeImpl::ProcessClusters(int64_t now_ms) { + std::list clusters; + ComputeClusters(&clusters); + if (clusters.empty()) { + // If we reach the max number of probe packets and still have no clusters, + // we will remove the oldest one. + if (probes_.size() >= kMaxProbePackets) + probes_.pop_front(); + return; + } + int highest_probe_bitrate_bps = FindBestProbeBitrate(clusters); + bool initial_probe = + !remote_rate_->ValidEstimate() && highest_probe_bitrate_bps > 0; + bool probe_above_estimate = + remote_rate_->ValidEstimate() && + highest_probe_bitrate_bps > + static_cast(remote_rate_->LatestEstimate()); + if (initial_probe || probe_above_estimate) { + LOG(LS_INFO) << "Set new bitrate based on probe: " + << highest_probe_bitrate_bps << " bps."; + remote_rate_->SetEstimate(highest_probe_bitrate_bps, now_ms); + } + // Not probing and received non-probe packet, or finished with current set + // of probes. + if (clusters.size() >= kExpectedNumberOfProbes) + probes_.clear(); +} + +void RemoteBitrateEstimatorAbsSendTimeImpl::IncomingPacket( + int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) { + if (!header.extension.hasAbsoluteSendTime) { + LOG(LS_WARNING) << "RemoteBitrateEstimatorAbsSendTimeImpl: Incoming packet " + "is missing absolute send time extension!"; + } + uint32_t absolute_send_time = header.extension.absoluteSendTime; + assert(absolute_send_time < (1ul << 24)); + int64_t now_ms = clock_->TimeInMilliseconds(); + CriticalSectionScoped cs(crit_sect_.get()); + ssrcs_[header.ssrc] = now_ms; + incoming_bitrate_.Update(payload_size, now_ms); + const BandwidthUsage prior_state = detector_.State(); + + if (first_packet_time_ms_ == -1) + first_packet_time_ms_ = clock_->TimeInMilliseconds(); + + // Shift up send time to use the full 32 bits that inter_arrival works with, + // so wrapping works properly. + uint32_t timestamp = absolute_send_time << kAbsSendTimeInterArrivalUpshift; + uint32_t ts_delta = 0; + int64_t t_delta = 0; + int size_delta = 0; + // For now only try to detect probes while we don't have a valid estimate. + if (!remote_rate_->ValidEstimate() || + now_ms - first_packet_time_ms_ < kInitialProbingIntervalMs) { + int64_t send_time_ms = static_cast(timestamp) * kTimestampToMs; + // TODO(holmer): Use a map instead to get correct order? + if (probes_.empty()) { + LOG(LS_INFO) << "Probe packet received: send time=" << send_time_ms + << " ms, recv time=" << arrival_time_ms << " ms"; + } else { + int send_delta_ms = send_time_ms - probes_.back().send_time_ms; + int recv_delta_ms = arrival_time_ms - probes_.back().recv_time_ms; + LOG(LS_INFO) << "Probe packet received: send time=" << send_time_ms + << " ms, recv time=" << arrival_time_ms + << " ms, send delta=" << send_delta_ms + << " ms, recv delta=" << recv_delta_ms << " ms."; + } + probes_.push_back(Probe(send_time_ms, arrival_time_ms, payload_size)); + ProcessClusters(now_ms); + } + if (!inter_arrival_.get()) { + inter_arrival_.reset(new InterArrival( + (kTimestampGroupLengthMs << kInterArrivalShift) / 1000, + kTimestampToMs, remote_rate_->GetControlType() == kAimdControl)); + } + if (inter_arrival_->ComputeDeltas(timestamp, + arrival_time_ms, + payload_size, + &ts_delta, + &t_delta, + &size_delta)) { + double ts_delta_ms = (1000.0 * ts_delta) / (1 << kInterArrivalShift); + estimator_.Update(t_delta, ts_delta_ms, size_delta, detector_.State()); + detector_.Detect(estimator_.offset(), ts_delta_ms, + estimator_.num_of_deltas()); + UpdateStats(static_cast(t_delta - ts_delta_ms), now_ms); + } + if (detector_.State() == kBwOverusing) { + unsigned int incoming_bitrate = incoming_bitrate_.Rate(now_ms); + if (prior_state != kBwOverusing || + remote_rate_->TimeToReduceFurther(now_ms, incoming_bitrate)) { + // The first overuse should immediately trigger a new estimate. + // We also have to update the estimate immediately if we are overusing + // and the target bitrate is too high compared to what we are receiving. + UpdateEstimate(now_ms); + } + } +} + +int32_t RemoteBitrateEstimatorAbsSendTimeImpl::Process() { + if (TimeUntilNextProcess() > 0) { + return 0; + } + { + CriticalSectionScoped cs(crit_sect_.get()); + UpdateEstimate(clock_->TimeInMilliseconds()); + } + last_process_time_ = clock_->TimeInMilliseconds(); + return 0; +} + +int32_t RemoteBitrateEstimatorAbsSendTimeImpl::TimeUntilNextProcess() { + if (last_process_time_ < 0) { + return 0; + } + { + CriticalSectionScoped cs(crit_sect_.get()); + return last_process_time_ + process_interval_ms_ - + clock_->TimeInMilliseconds(); + } +} + +void RemoteBitrateEstimatorAbsSendTimeImpl::UpdateEstimate(int64_t now_ms) { + if (!inter_arrival_.get()) { + // No packets have been received on the active streams. + return; + } + for (Ssrcs::iterator it = ssrcs_.begin(); it != ssrcs_.end();) { + if ((now_ms - it->second) > kStreamTimeOutMs) { + ssrcs_.erase(it++); + } else { + ++it; + } + } + if (ssrcs_.empty()) { + // We can't update the estimate if we don't have any active streams. + inter_arrival_.reset(); + // We deliberately don't reset the first_packet_time_ms_ here for now since + // we only probe for bandwidth in the beginning of a call right now. + return; + } + + const RateControlInput input(detector_.State(), + incoming_bitrate_.Rate(now_ms), + estimator_.var_noise()); + const RateControlRegion region = remote_rate_->Update(&input, now_ms); + unsigned int target_bitrate = remote_rate_->UpdateBandwidthEstimate(now_ms); + if (remote_rate_->ValidEstimate()) { + process_interval_ms_ = remote_rate_->GetFeedbackInterval(); + observer_->OnReceiveBitrateChanged(Keys(ssrcs_), target_bitrate); + } + detector_.SetRateControlRegion(region); +} + +void RemoteBitrateEstimatorAbsSendTimeImpl::OnRttUpdate(uint32_t rtt) { + CriticalSectionScoped cs(crit_sect_.get()); + remote_rate_->SetRtt(rtt); +} + +void RemoteBitrateEstimatorAbsSendTimeImpl::RemoveStream(unsigned int ssrc) { + CriticalSectionScoped cs(crit_sect_.get()); + ssrcs_.erase(ssrc); +} + +bool RemoteBitrateEstimatorAbsSendTimeImpl::LatestEstimate( + std::vector* ssrcs, + unsigned int* bitrate_bps) const { + CriticalSectionScoped cs(crit_sect_.get()); + assert(ssrcs); + assert(bitrate_bps); + if (!remote_rate_->ValidEstimate()) { + return false; + } + *ssrcs = Keys(ssrcs_); + if (ssrcs_.empty()) { + *bitrate_bps = 0; + } else { + *bitrate_bps = remote_rate_->LatestEstimate(); + } + return true; +} + +bool RemoteBitrateEstimatorAbsSendTimeImpl::GetStats( + ReceiveBandwidthEstimatorStats* output) const { + { + CriticalSectionScoped cs(crit_sect_.get()); + output->recent_propagation_time_delta_ms = recent_propagation_delta_ms_; + output->recent_arrival_time_ms = recent_update_time_ms_; + output->total_propagation_time_delta_ms = total_propagation_delta_ms_; + } + RemoveStaleEntries( + &output->recent_arrival_time_ms, + &output->recent_propagation_time_delta_ms, + clock_->TimeInMilliseconds() - kPropagationDeltaQueueMaxTimeMs); + return true; +} + +void RemoteBitrateEstimatorAbsSendTimeImpl::UpdateStats( + int propagation_delta_ms, int64_t now_ms) { + // The caller must enter crit_sect_ before the call. + + // Remove the oldest entry if the size limit is reached. + if (recent_update_time_ms_.size() == kPropagationDeltaQueueMaxSize) { + recent_update_time_ms_.erase(recent_update_time_ms_.begin()); + recent_propagation_delta_ms_.erase(recent_propagation_delta_ms_.begin()); + } + + recent_propagation_delta_ms_.push_back(propagation_delta_ms); + recent_update_time_ms_.push_back(now_ms); + + RemoveStaleEntries( + &recent_update_time_ms_, + &recent_propagation_delta_ms_, + now_ms - kPropagationDeltaQueueMaxTimeMs); + + total_propagation_delta_ms_ = + std::max(total_propagation_delta_ms_ + propagation_delta_ms, 0); +} + +RemoteBitrateEstimator* AbsoluteSendTimeRemoteBitrateEstimatorFactory::Create( + RemoteBitrateObserver* observer, + Clock* clock, + RateControlType control_type, + uint32_t min_bitrate_bps) const { + LOG(LS_INFO) << "AbsoluteSendTimeRemoteBitrateEstimatorFactory: " + "Instantiating."; + return new RemoteBitrateEstimatorAbsSendTimeImpl(observer, + clock, + control_type, + min_bitrate_bps); +} +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc new file mode 100644 index 000000000..13efcf1fe --- /dev/null +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc @@ -0,0 +1,162 @@ +/* + * 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 "webrtc/base/constructormagic.h" +#include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" + +namespace webrtc { + +class RemoteBitrateEstimatorAbsSendTimeTest : + public RemoteBitrateEstimatorTest { + public: + static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000; + + RemoteBitrateEstimatorAbsSendTimeTest() {} + virtual void SetUp() { + bitrate_estimator_.reset( + AbsoluteSendTimeRemoteBitrateEstimatorFactory().Create( + bitrate_observer_.get(), + &clock_, + kAimdControl, + kRemoteBitrateEstimatorMinBitrateBps)); + } + protected: + DISALLOW_COPY_AND_ASSIGN(RemoteBitrateEstimatorAbsSendTimeTest); +}; + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, InitialBehavior) { + InitialBehaviorTestHelper(508017); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, RateIncreaseReordering) { + RateIncreaseReorderingTestHelper(506422); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, RateIncreaseRtpTimestamps) { + RateIncreaseRtpTimestampsTestHelper(1090); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropOneStream) { + CapacityDropTestHelper(1, false, 700); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropOneStreamWrap) { + CapacityDropTestHelper(1, true, 700); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropTwoStreamsWrap) { + CapacityDropTestHelper(2, true, 700); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThreeStreamsWrap) { + CapacityDropTestHelper(3, true, 700); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThirteenStreamsWrap) { + CapacityDropTestHelper(13, true, 666); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropNineteenStreamsWrap) { + CapacityDropTestHelper(19, true, 666); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThirtyStreamsWrap) { + CapacityDropTestHelper(30, true, 666); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestTimestampGrouping) { + TestTimestampGroupingTestHelper(); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestGetStats) { + TestGetStatsHelper(); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestShortTimeoutAndWrap) { + // Simulate a client leaving and rejoining the call after 35 seconds. This + // will make abs send time wrap, so if streams aren't timed out properly + // the next 30 seconds of packets will be out of order. + TestWrappingHelper(35); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestLongTimeoutAndWrap) { + // Simulate a client leaving and rejoining the call after some multiple of + // 64 seconds later. This will cause a zero difference in abs send times due + // to the wrap, but a big difference in arrival time, if streams aren't + // properly timed out. + TestWrappingHelper(10 * 64); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestProcessAfterTimeout) { + // This time constant must be equal to the ones defined for the + // RemoteBitrateEstimator. + const int kStreamTimeOutMs = 2000; + const int kProcessIntervalMs = 1000; + IncomingPacket(0, 1000, clock_.TimeInMilliseconds(), 0, 0); + clock_.AdvanceTimeMilliseconds(kStreamTimeOutMs + 1); + // Trigger timeout. + EXPECT_EQ(0, bitrate_estimator_->Process()); + clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); + // This shouldn't crash. + EXPECT_EQ(0, bitrate_estimator_->Process()); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestProbeDetection) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // First burst sent at 8 * 1000 / 10 = 800 kbps. + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(10); + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 1000, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000)); + } + + // Second burst sent at 8 * 1000 / 5 = 1600 kbps. + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(5); + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 1000, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000)); + } + + EXPECT_EQ(0, bitrate_estimator_->Process()); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_GT(bitrate_observer_->latest_bitrate(), 1500000u); +} + +// Packets will require 5 ms to be transmitted to the receiver, causing packets +// of the second probe to be dispersed. +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, + TestProbeDetectionTooHighBitrate) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // First burst sent at 8 * 1000 / 10 = 800 kbps. + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(10); + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 1000, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000)); + } + + // Second burst sent at 8 * 1000 / 5 = 1600 kbps. + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(8); + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, + 1000, + now_ms, + 90 * (now_ms - 3 * i), + AbsSendTime(now_ms - 3 * i, 1000)); + } + + EXPECT_EQ(0, bitrate_estimator_->Process()); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_GT(bitrate_observer_->latest_bitrate(), 700000u); + EXPECT_LT(bitrate_observer_->latest_bitrate(), 900000u); +} +} // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp index 9f24f22b4..282705e4c 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_components.gyp @@ -8,15 +8,30 @@ { 'includes': [ - '../../build/common.gypi', + '../../../webrtc/build/common.gypi', ], 'targets': [ { 'target_name': 'rbe_components', 'type': 'static_library', + + 'include_dirs': [ + '<(webrtc_root)/modules/remote_bitrate_estimator', + ], 'sources': [ + '<(webrtc_root)/modules/remote_bitrate_estimator/test/bwe_test_logging.cc', + '<(webrtc_root)/modules/remote_bitrate_estimator/test/bwe_test_logging.h', + 'aimd_rate_control.cc', + 'aimd_rate_control.h', + 'inter_arrival.cc', + 'inter_arrival.h', + 'mimd_rate_control.cc', + 'mimd_rate_control.h', 'overuse_detector.cc', 'overuse_detector.h', + 'overuse_estimator.cc', + 'overuse_estimator.h', + 'remote_bitrate_estimator_abs_send_time.cc', 'remote_bitrate_estimator_single_stream.cc', 'remote_rate_control.cc', 'remote_rate_control.h', diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc index fd7de83b7..14fab7349 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc @@ -7,12 +7,15 @@ * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ - #include +#include "webrtc/base/constructormagic.h" +#include "webrtc/base/thread_annotations.h" #include "webrtc/modules/remote_bitrate_estimator/rate_statistics.h" #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "webrtc/modules/remote_bitrate_estimator/inter_arrival.h" #include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h" +#include "webrtc/modules/remote_bitrate_estimator/overuse_estimator.h" #include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" @@ -21,91 +24,90 @@ #include "webrtc/typedefs.h" namespace webrtc { -namespace { -class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator { - public: - RemoteBitrateEstimatorSingleStream(RemoteBitrateObserver* observer, - Clock* clock, - uint32_t min_bitrate_bps); - virtual ~RemoteBitrateEstimatorSingleStream() {} - // Called for each incoming packet. If this is a new SSRC, a new - // BitrateControl will be created. Updates the incoming payload bitrate - // estimate and the over-use detector. If an over-use is detected the - // remote bitrate estimate will be updated. Note that |payload_size| is the - // packet size excluding headers. +enum { kTimestampGroupLengthMs = 5 }; +static const double kTimestampToMs = 1.0 / 90.0; + +class RemoteBitrateEstimatorImpl : public RemoteBitrateEstimator { + public: + RemoteBitrateEstimatorImpl(RemoteBitrateObserver* observer, + Clock* clock, + RateControlType control_type, + uint32_t min_bitrate_bps); + virtual ~RemoteBitrateEstimatorImpl(); + virtual void IncomingPacket(int64_t arrival_time_ms, size_t payload_size, const RTPHeader& header) OVERRIDE; - - // Triggers a new estimate calculation. - // Implements the Module interface. virtual int32_t Process() OVERRIDE; virtual int32_t TimeUntilNextProcess() OVERRIDE; - // Set the current round-trip time experienced by the stream. - // Implements the StatsObserver interface. virtual void OnRttUpdate(uint32_t rtt) OVERRIDE; - - // Removes all data for |ssrc|. virtual void RemoveStream(unsigned int ssrc) OVERRIDE; - - // Returns true if a valid estimate exists and sets |bitrate_bps| to the - // estimated payload bitrate in bits per second. |ssrcs| is the list of ssrcs - // currently being received and of which the bitrate estimate is based upon. virtual bool LatestEstimate(std::vector* ssrcs, unsigned int* bitrate_bps) const OVERRIDE; - - virtual bool GetStats( - ReceiveBandwidthEstimatorStats* output) const OVERRIDE; + virtual bool GetStats(ReceiveBandwidthEstimatorStats* output) const OVERRIDE; private: - // Map from SSRC to over-use detector and last incoming packet time in - // milliseconds, taken from clock_. - typedef std::map > - SsrcOveruseDetectorMap; + struct Detector { + explicit Detector(int64_t last_packet_time_ms, + const OverUseDetectorOptions& options, + bool enable_burst_grouping) + : last_packet_time_ms(last_packet_time_ms), + inter_arrival(90 * kTimestampGroupLengthMs, kTimestampToMs, + enable_burst_grouping), + estimator(options), + detector(options) {} + int64_t last_packet_time_ms; + InterArrival inter_arrival; + OveruseEstimator estimator; + OveruseDetector detector; + }; - static OveruseDetector* GetDetector( - const SsrcOveruseDetectorMap::iterator it) { - return &it->second.first; - } - - static int64_t GetPacketTimeMs(const SsrcOveruseDetectorMap::iterator it) { - return it->second.second; - } - - static void SetPacketTimeMs(SsrcOveruseDetectorMap::iterator it, - int64_t time_ms) { - it->second.second = time_ms; - } + typedef std::map SsrcOveruseEstimatorMap; // Triggers a new estimate calculation. - void UpdateEstimate(int64_t now_ms); + void UpdateEstimate(int64_t time_now) + EXCLUSIVE_LOCKS_REQUIRED(crit_sect_.get()); - void GetSsrcs(std::vector* ssrcs) const; + void GetSsrcs(std::vector* ssrcs) const + SHARED_LOCKS_REQUIRED(crit_sect_.get()); Clock* clock_; - SsrcOveruseDetectorMap overuse_detectors_; - RateStatistics incoming_bitrate_; - RemoteRateControl remote_rate_; - RemoteBitrateObserver* observer_; + SsrcOveruseEstimatorMap overuse_detectors_ GUARDED_BY(crit_sect_.get()); + RateStatistics incoming_bitrate_ GUARDED_BY(crit_sect_.get()); + scoped_ptr remote_rate_ GUARDED_BY(crit_sect_.get()); + RemoteBitrateObserver* observer_ GUARDED_BY(crit_sect_.get()); scoped_ptr crit_sect_; int64_t last_process_time_; + int process_interval_ms_ GUARDED_BY(crit_sect_.get()); + + DISALLOW_IMPLICIT_CONSTRUCTORS(RemoteBitrateEstimatorImpl); }; -RemoteBitrateEstimatorSingleStream::RemoteBitrateEstimatorSingleStream( +RemoteBitrateEstimatorImpl::RemoteBitrateEstimatorImpl( RemoteBitrateObserver* observer, Clock* clock, + RateControlType control_type, uint32_t min_bitrate_bps) : clock_(clock), - incoming_bitrate_(500, 8000), - remote_rate_(min_bitrate_bps), + incoming_bitrate_(1000, 8000), + remote_rate_(RemoteRateControl::Create(control_type, min_bitrate_bps)), observer_(observer), crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), - last_process_time_(-1) { + last_process_time_(-1), + process_interval_ms_(kProcessIntervalMs) { assert(observer_); } -void RemoteBitrateEstimatorSingleStream::IncomingPacket( +RemoteBitrateEstimatorImpl::~RemoteBitrateEstimatorImpl() { + while (!overuse_detectors_.empty()) { + SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.begin(); + delete it->second; + overuse_detectors_.erase(it); + } +} + +void RemoteBitrateEstimatorImpl::IncomingPacket( int64_t arrival_time_ms, size_t payload_size, const RTPHeader& header) { @@ -114,7 +116,7 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket( header.extension.transmissionTimeOffset; int64_t now_ms = clock_->TimeInMilliseconds(); CriticalSectionScoped cs(crit_sect_.get()); - SsrcOveruseDetectorMap::iterator it = overuse_detectors_.find(ssrc); + SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.find(ssrc); if (it == overuse_detectors_.end()) { // This is a new SSRC. Adding to map. // TODO(holmer): If the channel changes SSRC the old SSRC will still be @@ -122,20 +124,34 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket( // callback will no longer be called for the old SSRC. This will be // automatically cleaned up when we have one RemoteBitrateEstimator per REMB // group. - std::pair insert_result = - overuse_detectors_.insert(std::make_pair(ssrc, - std::make_pair(OveruseDetector(OverUseDetectorOptions()), now_ms))); + std::pair insert_result = + overuse_detectors_.insert(std::make_pair(ssrc, new Detector( + now_ms, + OverUseDetectorOptions(), + remote_rate_->GetControlType() == kAimdControl))); it = insert_result.first; } - SetPacketTimeMs(it, now_ms); - OveruseDetector* overuse_detector = GetDetector(it); + Detector* estimator = it->second; + estimator->last_packet_time_ms = now_ms; incoming_bitrate_.Update(payload_size, now_ms); - const BandwidthUsage prior_state = overuse_detector->State(); - overuse_detector->Update(payload_size, -1, rtp_timestamp, arrival_time_ms); - if (overuse_detector->State() == kBwOverusing) { + const BandwidthUsage prior_state = estimator->detector.State(); + uint32_t timestamp_delta = 0; + int64_t time_delta = 0; + int size_delta = 0; + if (estimator->inter_arrival.ComputeDeltas(rtp_timestamp, arrival_time_ms, + payload_size, ×tamp_delta, + &time_delta, &size_delta)) { + double timestamp_delta_ms = timestamp_delta * kTimestampToMs; + estimator->estimator.Update(time_delta, timestamp_delta_ms, size_delta, + estimator->detector.State()); + estimator->detector.Detect(estimator->estimator.offset(), + timestamp_delta_ms, + estimator->estimator.num_of_deltas()); + } + if (estimator->detector.State() == kBwOverusing) { uint32_t incoming_bitrate = incoming_bitrate_.Rate(now_ms); if (prior_state != kBwOverusing || - remote_rate_.TimeToReduceFurther(now_ms, incoming_bitrate)) { + remote_rate_->TimeToReduceFurther(now_ms, incoming_bitrate)) { // The first overuse should immediately trigger a new estimate. // We also have to update the estimate immediately if we are overusing // and the target bitrate is too high compared to what we are receiving. @@ -144,130 +160,130 @@ void RemoteBitrateEstimatorSingleStream::IncomingPacket( } } -int32_t RemoteBitrateEstimatorSingleStream::Process() { +int32_t RemoteBitrateEstimatorImpl::Process() { if (TimeUntilNextProcess() > 0) { return 0; } - int64_t now_ms = clock_->TimeInMilliseconds(); - UpdateEstimate(now_ms); - last_process_time_ = now_ms; + { + CriticalSectionScoped cs(crit_sect_.get()); + UpdateEstimate(clock_->TimeInMilliseconds()); + } + last_process_time_ = clock_->TimeInMilliseconds(); return 0; } -int32_t RemoteBitrateEstimatorSingleStream::TimeUntilNextProcess() { +int32_t RemoteBitrateEstimatorImpl::TimeUntilNextProcess() { if (last_process_time_ < 0) { return 0; } - return last_process_time_ + kProcessIntervalMs - clock_->TimeInMilliseconds(); + { + CriticalSectionScoped cs_(crit_sect_.get()); + return last_process_time_ + process_interval_ms_ - + clock_->TimeInMilliseconds(); + } } -void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) { - CriticalSectionScoped cs(crit_sect_.get()); +void RemoteBitrateEstimatorImpl::UpdateEstimate(int64_t now_ms) { BandwidthUsage bw_state = kBwNormal; - double sum_noise_var = 0.0; - SsrcOveruseDetectorMap::iterator it = overuse_detectors_.begin(); + double sum_var_noise = 0.0; + SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.begin(); while (it != overuse_detectors_.end()) { - if (GetPacketTimeMs(it) >= 0 && - now_ms - GetPacketTimeMs(it) > kStreamTimeOutMs) { + const int64_t time_of_last_received_packet = + it->second->last_packet_time_ms; + if (time_of_last_received_packet >= 0 && + now_ms - time_of_last_received_packet > kStreamTimeOutMs) { // This over-use detector hasn't received packets for |kStreamTimeOutMs| // milliseconds and is considered stale. + delete it->second; overuse_detectors_.erase(it++); } else { - OveruseDetector* overuse_detector = GetDetector(it); - sum_noise_var += overuse_detector->NoiseVar(); + sum_var_noise += it->second->estimator.var_noise(); // Make sure that we trigger an over-use if any of the over-use detectors // is detecting over-use. - if (overuse_detector->State() > bw_state) { - bw_state = overuse_detector->State(); + if (it->second->detector.State() > bw_state) { + bw_state = it->second->detector.State(); } ++it; } } // We can't update the estimate if we don't have any active streams. if (overuse_detectors_.empty()) { - remote_rate_.Reset(); + remote_rate_.reset(RemoteRateControl::Create( + remote_rate_->GetControlType(), remote_rate_->GetMinBitrate())); return; } - double mean_noise_var = sum_noise_var / + double mean_noise_var = sum_var_noise / static_cast(overuse_detectors_.size()); const RateControlInput input(bw_state, incoming_bitrate_.Rate(now_ms), mean_noise_var); - const RateControlRegion region = remote_rate_.Update(&input, now_ms); - unsigned int target_bitrate = remote_rate_.UpdateBandwidthEstimate(now_ms); - if (remote_rate_.ValidEstimate()) { + const RateControlRegion region = remote_rate_->Update(&input, now_ms); + unsigned int target_bitrate = remote_rate_->UpdateBandwidthEstimate(now_ms); + if (remote_rate_->ValidEstimate()) { + process_interval_ms_ = remote_rate_->GetFeedbackInterval(); std::vector ssrcs; GetSsrcs(&ssrcs); observer_->OnReceiveBitrateChanged(ssrcs, target_bitrate); } for (it = overuse_detectors_.begin(); it != overuse_detectors_.end(); ++it) { - GetDetector(it)->SetRateControlRegion(region); + it->second->detector.SetRateControlRegion(region); } } -void RemoteBitrateEstimatorSingleStream::OnRttUpdate(uint32_t rtt) { +void RemoteBitrateEstimatorImpl::OnRttUpdate(uint32_t rtt) { CriticalSectionScoped cs(crit_sect_.get()); - remote_rate_.SetRtt(rtt); + remote_rate_->SetRtt(rtt); } -void RemoteBitrateEstimatorSingleStream::RemoveStream(unsigned int ssrc) { +void RemoteBitrateEstimatorImpl::RemoveStream(unsigned int ssrc) { CriticalSectionScoped cs(crit_sect_.get()); - // Ignoring the return value which is the number of elements erased. - overuse_detectors_.erase(ssrc); + SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.find(ssrc); + if (it != overuse_detectors_.end()) { + delete it->second; + overuse_detectors_.erase(it); + } } -bool RemoteBitrateEstimatorSingleStream::LatestEstimate( +bool RemoteBitrateEstimatorImpl::LatestEstimate( std::vector* ssrcs, unsigned int* bitrate_bps) const { CriticalSectionScoped cs(crit_sect_.get()); assert(bitrate_bps); - if (!remote_rate_.ValidEstimate()) { + if (!remote_rate_->ValidEstimate()) { return false; } GetSsrcs(ssrcs); if (ssrcs->empty()) *bitrate_bps = 0; else - *bitrate_bps = remote_rate_.LatestEstimate(); + *bitrate_bps = remote_rate_->LatestEstimate(); return true; } -bool RemoteBitrateEstimatorSingleStream::GetStats( +bool RemoteBitrateEstimatorImpl::GetStats( ReceiveBandwidthEstimatorStats* output) const { // Not implemented. return false; } -void RemoteBitrateEstimatorSingleStream::GetSsrcs( +void RemoteBitrateEstimatorImpl::GetSsrcs( std::vector* ssrcs) const { assert(ssrcs); ssrcs->resize(overuse_detectors_.size()); int i = 0; - for (SsrcOveruseDetectorMap::const_iterator it = overuse_detectors_.begin(); + for (SsrcOveruseEstimatorMap::const_iterator it = overuse_detectors_.begin(); it != overuse_detectors_.end(); ++it, ++i) { (*ssrcs)[i] = it->first; } } -} // namespace RemoteBitrateEstimator* RemoteBitrateEstimatorFactory::Create( - RemoteBitrateObserver* observer, - Clock* clock, + webrtc::RemoteBitrateObserver* observer, + webrtc::Clock* clock, RateControlType control_type, uint32_t min_bitrate_bps) const { LOG(LS_INFO) << "RemoteBitrateEstimatorFactory: Instantiating."; - return new RemoteBitrateEstimatorSingleStream(observer, clock, - min_bitrate_bps); -} - -RemoteBitrateEstimator* AbsoluteSendTimeRemoteBitrateEstimatorFactory::Create( - RemoteBitrateObserver* observer, - Clock* clock, - RateControlType control_type, - uint32_t min_bitrate_bps) const { - LOG(LS_INFO) << "AbsoluteSendTimeRemoteBitrateEstimatorFactory: " - "Instantiating."; - return new RemoteBitrateEstimatorSingleStream(observer, clock, - min_bitrate_bps); + return new RemoteBitrateEstimatorImpl(observer, clock, control_type, + min_bitrate_bps); } } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc index 08ba49d4c..2ce7a8b75 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc @@ -8,14 +8,13 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "testing/gtest/include/gtest/gtest.h" - #include "webrtc/base/constructormagic.h" #include "webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" namespace webrtc { -class RemoteBitrateEstimatorSingleTest : public RemoteBitrateEstimatorTest { +class RemoteBitrateEstimatorSingleTest : + public RemoteBitrateEstimatorTest { public: static const uint32_t kRemoteBitrateEstimatorMinBitrateBps = 30000; @@ -32,53 +31,46 @@ class RemoteBitrateEstimatorSingleTest : public RemoteBitrateEstimatorTest { }; TEST_F(RemoteBitrateEstimatorSingleTest, InitialBehavior) { - InitialBehaviorTestHelper(498075); + InitialBehaviorTestHelper(508017); } TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseReordering) { - RateIncreaseReorderingTestHelper(498136); + RateIncreaseReorderingTestHelper(506422); } TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseRtpTimestamps) { RateIncreaseRtpTimestampsTestHelper(1621); } -// Verify that the time it takes for the estimator to reduce the bitrate when -// the capacity is tightened stays the same. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStream) { - CapacityDropTestHelper(1, false, 367); + CapacityDropTestHelper(1, false, 733); } -// Verify that the time it takes for the estimator to reduce the bitrate when -// the capacity is tightened stays the same. This test also verifies that we -// handle wrap-arounds in this scenario. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStreamWrap) { - CapacityDropTestHelper(1, true, 367); + CapacityDropTestHelper(1, true, 733); } -// Verify that the time it takes for the estimator to reduce the bitrate when -// the capacity is tightened stays the same. This test also verifies that we -// handle wrap-arounds in this scenario. This is a multi-stream test. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropTwoStreamsWrap) { - CapacityDropTestHelper(2, true, 267); + CapacityDropTestHelper(2, true, 700); } -// Verify that the time it takes for the estimator to reduce the bitrate when -// the capacity is tightened stays the same. This test also verifies that we -// handle wrap-arounds in this scenario. This is a multi-stream test. TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThreeStreamsWrap) { - CapacityDropTestHelper(3, true, 333); + CapacityDropTestHelper(3, true, 733); } TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirteenStreamsWrap) { - CapacityDropTestHelper(13, true, 300); + CapacityDropTestHelper(13, true, 733); } TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropNineteenStreamsWrap) { - CapacityDropTestHelper(19, true, 300); + CapacityDropTestHelper(19, true, 733); } TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirtyStreamsWrap) { - CapacityDropTestHelper(30, true, 300); + CapacityDropTestHelper(30, true, 733); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, TestTimestampGrouping) { + TestTimestampGroupingTestHelper(); } } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc index 907361732..4aa6b52d0 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc @@ -512,4 +512,155 @@ void RemoteBitrateEstimatorTest::CapacityDropTestHelper( EXPECT_EQ(0u, ssrcs.size()); EXPECT_EQ(0u, latest_bps); } + +void RemoteBitrateEstimatorTest::TestTimestampGroupingTestHelper() { + const int kFramerate = 50; // 50 fps to avoid rounding errors. + const int kFrameIntervalMs = 1000 / kFramerate; + const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate); + uint32_t timestamp = 0; + // Initialize absolute_send_time (24 bits) so that it will definitely wrap + // during the test. + uint32_t absolute_send_time = + AddAbsSendTime((1 << 24), -int(50 * kFrameIntervalAbsSendTime)); + // Initial set of frames to increase the bitrate. + for (int i = 0; i <= 100; ++i) { + IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + bitrate_estimator_->Process(); + clock_.AdvanceTimeMilliseconds(kFrameIntervalMs); + timestamp += 90 * kFrameIntervalMs; + absolute_send_time = AddAbsSendTime(absolute_send_time, + kFrameIntervalAbsSendTime); + } + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_NEAR(450000u, bitrate_observer_->latest_bitrate(), 20000u); + + // Insert batches of frames which were sent very close in time. Also simulate + // capacity over-use to see that we back off correctly. + const int kTimestampGroupLength = 15; + const uint32_t kTimestampGroupLengthAbsSendTime = + AbsSendTime(kTimestampGroupLength, 90000); + const uint32_t kSingleRtpTickAbsSendTime = AbsSendTime(1, 90000); + for (int i = 0; i < 100; ++i) { + for (int j = 0; j < kTimestampGroupLength; ++j) { + // Insert |kTimestampGroupLength| frames with just 1 timestamp ticks in + // between. Should be treated as part of the same group by the estimator. + IncomingPacket(kDefaultSsrc, 100, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + clock_.AdvanceTimeMilliseconds(kFrameIntervalMs / kTimestampGroupLength); + timestamp += 1; + absolute_send_time = AddAbsSendTime(absolute_send_time, + kSingleRtpTickAbsSendTime); + } + // Increase time until next batch to simulate over-use. + clock_.AdvanceTimeMilliseconds(10); + timestamp += 90 * kFrameIntervalMs - kTimestampGroupLength; + absolute_send_time = AddAbsSendTime(absolute_send_time, AddAbsSendTime( + kFrameIntervalAbsSendTime, -int(kTimestampGroupLengthAbsSendTime))); + bitrate_estimator_->Process(); + } + EXPECT_TRUE(bitrate_observer_->updated()); + // Should have reduced the estimate. + EXPECT_EQ(378720u, bitrate_observer_->latest_bitrate()); +} + +void RemoteBitrateEstimatorTest::TestGetStatsHelper() { + const int kFramerate = 100; + const int kFrameIntervalMs = 1000 / kFramerate; + const int kBurstThresholdMs = 5; + const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate); + uint32_t timestamp = 0; + // Initialize absolute_send_time (24 bits) so that it will definitely wrap + // during the test. + uint32_t absolute_send_time = + AddAbsSendTime((1 << 24), + -(50 * static_cast(kFrameIntervalAbsSendTime))); + + // Inject propagation_time_delta of kFrameIntervalMs. + for (size_t i = 0; i < 3; ++i) { + IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + timestamp += kFrameIntervalMs; + // Insert a kFrameIntervalMs propagation_time_delta. + clock_.AdvanceTimeMilliseconds(kFrameIntervalMs * 2); + absolute_send_time = AddAbsSendTime(absolute_send_time, + kFrameIntervalAbsSendTime); + } + ReceiveBandwidthEstimatorStats stats; + EXPECT_TRUE(bitrate_estimator_->GetStats(&stats)); + EXPECT_EQ(1U, stats.recent_propagation_time_delta_ms.size()); + EXPECT_EQ(kFrameIntervalMs, stats.recent_propagation_time_delta_ms[0]); + EXPECT_EQ(1U, stats.recent_arrival_time_ms.size()); + EXPECT_EQ(kFrameIntervalMs, stats.total_propagation_time_delta_ms); + + // Inject negative propagation_time_deltas. The total propagation_time_delta + // should be adjusted to 0. + for (size_t i = 0; i < 3; ++i) { + IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + timestamp += 10 * kFrameIntervalMs; + clock_.AdvanceTimeMilliseconds(kBurstThresholdMs + 1); + absolute_send_time = AddAbsSendTime(absolute_send_time, + 10 * kFrameIntervalAbsSendTime); + } + EXPECT_TRUE(bitrate_estimator_->GetStats(&stats)); + EXPECT_EQ(0, stats.total_propagation_time_delta_ms); + + // Send more than 1000 frames and make sure the stats queues stays within + // limits. + for (size_t i = 0; i < 1001; ++i) { + IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + timestamp += kFrameIntervalMs; + absolute_send_time = AddAbsSendTime(absolute_send_time, + kFrameIntervalAbsSendTime); + } + EXPECT_TRUE(bitrate_estimator_->GetStats(&stats)); + EXPECT_LE(stats.recent_propagation_time_delta_ms.size(), 1000U); + EXPECT_LE(stats.recent_arrival_time_ms.size(), 1000U); + + // Move the clock over the 1000ms limit. + clock_.AdvanceTimeMilliseconds(2000); + EXPECT_TRUE(bitrate_estimator_->GetStats(&stats)); + EXPECT_EQ(0U, stats.recent_propagation_time_delta_ms.size()); +} + +void RemoteBitrateEstimatorTest::TestWrappingHelper( + int silence_time_s) { + const int kFramerate = 100; + const int kFrameIntervalMs = 1000 / kFramerate; + const uint32_t kFrameIntervalAbsSendTime = AbsSendTime(1, kFramerate); + uint32_t absolute_send_time = 0; + uint32_t timestamp = 0; + + for (size_t i = 0; i < 3000; ++i) { + IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + timestamp += kFrameIntervalMs; + clock_.AdvanceTimeMilliseconds(kFrameIntervalMs); + absolute_send_time = AddAbsSendTime(absolute_send_time, + kFrameIntervalAbsSendTime); + bitrate_estimator_->Process(); + } + unsigned int bitrate_before = 0; + std::vector ssrcs; + bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_before); + + clock_.AdvanceTimeMilliseconds(silence_time_s * 1000); + absolute_send_time = AddAbsSendTime(absolute_send_time, + AbsSendTime(silence_time_s, 1)); + bitrate_estimator_->Process(); + for (size_t i = 0; i < 100; ++i) { + IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + timestamp += kFrameIntervalMs; + clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs); + absolute_send_time = AddAbsSendTime(absolute_send_time, + kFrameIntervalAbsSendTime); + bitrate_estimator_->Process(); + } + unsigned int bitrate_after = 0; + bitrate_estimator_->LatestEstimate(&ssrcs, &bitrate_after); + EXPECT_LT(bitrate_after, bitrate_before); +} } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h index a935896ac..c192d6f6c 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h +++ b/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h @@ -189,6 +189,13 @@ class RemoteBitrateEstimatorTest : public ::testing::Test { unsigned int max_bitrate, unsigned int target_bitrate); + + void TestTimestampGroupingTestHelper(); + + void TestGetStatsHelper(); + + void TestWrappingHelper(int silence_time_s); + void InitialBehaviorTestHelper(unsigned int expected_converge_bitrate); void RateIncreaseReorderingTestHelper(unsigned int expected_bitrate); void RateIncreaseRtpTimestampsTestHelper(int expected_iterations); diff --git a/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc b/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc index dda36a765..ea4838dc5 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc +++ b/webrtc/modules/remote_bitrate_estimator/remote_rate_control.cc @@ -10,318 +10,16 @@ #include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h" -#include -#include -#include - -#include - -#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h" +#include "webrtc/modules/remote_bitrate_estimator/mimd_rate_control.h" namespace webrtc { - -const unsigned int kDefaultRttMs = 200; - -RemoteRateControl::RemoteRateControl(uint32_t min_bitrate_bps) - : min_configured_bit_rate_(min_bitrate_bps), - max_configured_bit_rate_(30000000), - current_bit_rate_(max_configured_bit_rate_), - max_hold_rate_(0), - avg_max_bit_rate_(-1.0f), - var_max_bit_rate_(0.4f), - rate_control_state_(kRcHold), - came_from_state_(kRcDecrease), - rate_control_region_(kRcMaxUnknown), - last_bit_rate_change_(-1), - current_input_(kBwNormal, 0, 1.0), - updated_(false), - time_first_incoming_estimate_(-1), - initialized_bit_rate_(false), - avg_change_period_(1000.0f), - last_change_ms_(-1), - beta_(0.9f), - rtt_(kDefaultRttMs) -{ -} - -void RemoteRateControl::Reset() { - *this = RemoteRateControl(min_configured_bit_rate_); - came_from_state_ = kRcHold; -} - -bool RemoteRateControl::ValidEstimate() const { - return initialized_bit_rate_; -} - -bool RemoteRateControl::TimeToReduceFurther(int64_t time_now, - unsigned int incoming_bitrate) const { - const int bitrate_reduction_interval = std::max(std::min(rtt_, 200u), 10u); - if (time_now - last_bit_rate_change_ >= bitrate_reduction_interval) { - return true; - } - if (ValidEstimate()) { - const int threshold = static_cast(1.05 * incoming_bitrate); - const int bitrate_difference = LatestEstimate() - incoming_bitrate; - return bitrate_difference > threshold; - } - return false; -} - -int32_t RemoteRateControl::SetConfiguredBitRates(uint32_t min_bit_rate_bps, - uint32_t max_bit_rate_bps) { - if (min_bit_rate_bps > max_bit_rate_bps) { - return -1; - } - min_configured_bit_rate_ = min_bit_rate_bps; - max_configured_bit_rate_ = max_bit_rate_bps; - current_bit_rate_ = std::min(std::max(min_bit_rate_bps, current_bit_rate_), - max_bit_rate_bps); - return 0; -} - -uint32_t RemoteRateControl::LatestEstimate() const { - return current_bit_rate_; -} - -uint32_t RemoteRateControl::UpdateBandwidthEstimate(int64_t now_ms) { - current_bit_rate_ = ChangeBitRate(current_bit_rate_, - current_input_._incomingBitRate, - current_input_._noiseVar, - now_ms); - return current_bit_rate_; -} - -void RemoteRateControl::SetRtt(unsigned int rtt) { - rtt_ = rtt; -} - -RateControlRegion RemoteRateControl::Update(const RateControlInput* input, - int64_t now_ms) { - assert(input); - - // Set the initial bit rate value to what we're receiving the first half - // second. - if (!initialized_bit_rate_) { - if (time_first_incoming_estimate_ < 0) { - if (input->_incomingBitRate > 0) { - time_first_incoming_estimate_ = now_ms; - } - } else if (now_ms - time_first_incoming_estimate_ > 500 && - input->_incomingBitRate > 0) { - current_bit_rate_ = input->_incomingBitRate; - initialized_bit_rate_ = true; - } - } - - if (updated_ && current_input_._bwState == kBwOverusing) { - // Only update delay factor and incoming bit rate. We always want to react - // on an over-use. - current_input_._noiseVar = input->_noiseVar; - current_input_._incomingBitRate = input->_incomingBitRate; - return rate_control_region_; - } - updated_ = true; - current_input_ = *input; - return rate_control_region_; -} - -uint32_t RemoteRateControl::ChangeBitRate(uint32_t current_bit_rate, - uint32_t incoming_bit_rate, - double noise_var, - int64_t now_ms) { - if (!updated_) { - return current_bit_rate_; - } - updated_ = false; - UpdateChangePeriod(now_ms); - ChangeState(current_input_, now_ms); - // calculated here because it's used in multiple places - const float incoming_bit_rate_kbps = incoming_bit_rate / 1000.0f; - // Calculate the max bit rate std dev given the normalized - // variance and the current incoming bit rate. - const float std_max_bit_rate = sqrt(var_max_bit_rate_ * avg_max_bit_rate_); - bool recovery = false; - switch (rate_control_state_) { - case kRcHold: { - max_hold_rate_ = std::max(max_hold_rate_, incoming_bit_rate); - break; - } - case kRcIncrease: { - if (avg_max_bit_rate_ >= 0) { - if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 3 * std_max_bit_rate) { - ChangeRegion(kRcMaxUnknown); - avg_max_bit_rate_ = -1.0; - } else if (incoming_bit_rate_kbps > avg_max_bit_rate_ + 2.5 * - std_max_bit_rate) { - ChangeRegion(kRcAboveMax); - } - } - const uint32_t response_time = static_cast(avg_change_period_ + - 0.5f) + rtt_ + 300; - double alpha = RateIncreaseFactor(now_ms, last_bit_rate_change_, - response_time, noise_var); - - current_bit_rate = static_cast(current_bit_rate * alpha) + 1000; - if (max_hold_rate_ > 0 && beta_ * max_hold_rate_ > current_bit_rate) { - current_bit_rate = static_cast(beta_ * max_hold_rate_); - avg_max_bit_rate_ = beta_ * max_hold_rate_ / 1000.0f; - ChangeRegion(kRcNearMax); - recovery = true; - } - max_hold_rate_ = 0; - last_bit_rate_change_ = now_ms; - break; - } - case kRcDecrease: { - if (incoming_bit_rate < min_configured_bit_rate_) { - current_bit_rate = min_configured_bit_rate_; - } else { - // Set bit rate to something slightly lower than max - // to get rid of any self-induced delay. - current_bit_rate = static_cast(beta_ * incoming_bit_rate + - 0.5); - if (current_bit_rate > current_bit_rate_) { - // Avoid increasing the rate when over-using. - if (rate_control_region_ != kRcMaxUnknown) { - current_bit_rate = static_cast(beta_ * avg_max_bit_rate_ * - 1000 + 0.5f); - } - current_bit_rate = std::min(current_bit_rate, current_bit_rate_); - } - ChangeRegion(kRcNearMax); - - if (incoming_bit_rate_kbps < avg_max_bit_rate_ - 3 * std_max_bit_rate) { - avg_max_bit_rate_ = -1.0f; - } - - UpdateMaxBitRateEstimate(incoming_bit_rate_kbps); - } - // Stay on hold until the pipes are cleared. - ChangeState(kRcHold); - last_bit_rate_change_ = now_ms; - break; - } - default: - assert(false); - } - if (!recovery && (incoming_bit_rate > 100000 || current_bit_rate > 150000) && - current_bit_rate > 1.5 * incoming_bit_rate) { - // Allow changing the bit rate if we are operating at very low rates - // Don't change the bit rate if the send side is too far off - current_bit_rate = current_bit_rate_; - last_bit_rate_change_ = now_ms; - } - return current_bit_rate; -} - -double RemoteRateControl::RateIncreaseFactor(int64_t now_ms, - int64_t last_ms, - uint32_t reaction_time_ms, - double noise_var) const { - // alpha = 1.02 + B ./ (1 + exp(b*(tr - (c1*s2 + c2)))) - // Parameters - const double B = 0.0407; - const double b = 0.0025; - const double c1 = -6700.0 / (33 * 33); - const double c2 = 800.0; - const double d = 0.85; - - double alpha = 1.005 + B / (1 + exp( b * (d * reaction_time_ms - - (c1 * noise_var + c2)))); - - if (alpha < 1.005) { - alpha = 1.005; - } else if (alpha > 1.3) { - alpha = 1.3; - } - - if (last_ms > -1) { - alpha = pow(alpha, (now_ms - last_ms) / 1000.0); - } - - if (rate_control_region_ == kRcNearMax) { - // We're close to our previous maximum. Try to stabilize the - // bit rate in this region, by increasing in smaller steps. - alpha = alpha - (alpha - 1.0) / 2.0; - } else if (rate_control_region_ == kRcMaxUnknown) { - alpha = alpha + (alpha - 1.0) * 2.0; - } - - return alpha; -} - -void RemoteRateControl::UpdateChangePeriod(int64_t now_ms) { - int64_t change_period = 0; - if (last_change_ms_ > -1) { - change_period = now_ms - last_change_ms_; - } - last_change_ms_ = now_ms; - avg_change_period_ = 0.9f * avg_change_period_ + 0.1f * change_period; -} - -void RemoteRateControl::UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps) { - const float alpha = 0.05f; - if (avg_max_bit_rate_ == -1.0f) { - avg_max_bit_rate_ = incoming_bit_rate_kbps; +RemoteRateControl* RemoteRateControl::Create(RateControlType control_type, + uint32_t min_bitrate_bps) { + if (control_type == kAimdControl) { + return new AimdRateControl(min_bitrate_bps); } else { - avg_max_bit_rate_ = (1 - alpha) * avg_max_bit_rate_ + - alpha * incoming_bit_rate_kbps; + return new MimdRateControl(min_bitrate_bps); } - // Estimate the max bit rate variance and normalize the variance - // with the average max bit rate. - const float norm = std::max(avg_max_bit_rate_, 1.0f); - var_max_bit_rate_ = (1 - alpha) * var_max_bit_rate_ + - alpha * (avg_max_bit_rate_ - incoming_bit_rate_kbps) * - (avg_max_bit_rate_ - incoming_bit_rate_kbps) / norm; - // 0.4 ~= 14 kbit/s at 500 kbit/s - if (var_max_bit_rate_ < 0.4f) { - var_max_bit_rate_ = 0.4f; - } - // 2.5f ~= 35 kbit/s at 500 kbit/s - if (var_max_bit_rate_ > 2.5f) { - var_max_bit_rate_ = 2.5f; - } -} - -void RemoteRateControl::ChangeState(const RateControlInput& input, - int64_t now_ms) { - switch (current_input_._bwState) { - case kBwNormal: - if (rate_control_state_ == kRcHold) { - last_bit_rate_change_ = now_ms; - ChangeState(kRcIncrease); - } - break; - case kBwOverusing: - if (rate_control_state_ != kRcDecrease) { - ChangeState(kRcDecrease); - } - break; - case kBwUnderusing: - ChangeState(kRcHold); - break; - default: - assert(false); - } -} - -void RemoteRateControl::ChangeRegion(RateControlRegion region) { - rate_control_region_ = region; - switch (rate_control_region_) { - case kRcAboveMax: - case kRcMaxUnknown: - beta_ = 0.9f; - break; - case kRcNearMax: - beta_ = 0.95f; - break; - default: - assert(false); - } -} - -void RemoteRateControl::ChangeState(RateControlState new_state) { - came_from_state_ = rate_control_state_; - rate_control_state_ = new_state; } } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h b/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h index d02c6d56b..f9889dbb2 100644 --- a/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h +++ b/webrtc/modules/remote_bitrate_estimator/remote_rate_control.h @@ -8,71 +8,44 @@ * be found in the AUTHORS file in the root of the source tree. */ -#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_ -#define WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_ +#ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_RATE_CONTROL_H_ +#define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_RATE_CONTROL_H_ #include "webrtc/modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" namespace webrtc { class RemoteRateControl { public: - explicit RemoteRateControl(uint32_t min_bitrate_bps); - ~RemoteRateControl() {} + static RemoteRateControl* Create(RateControlType control_type, + uint32_t min_bitrate_bps); - void Reset(); + virtual ~RemoteRateControl() {} // Returns true if there is a valid estimate of the incoming bitrate, false // otherwise. - bool ValidEstimate() const; + virtual bool ValidEstimate() const = 0; + virtual RateControlType GetControlType() const = 0; + virtual uint32_t GetMinBitrate() const = 0; + virtual int GetFeedbackInterval() const = 0; // Returns true if the bitrate estimate hasn't been changed for more than // an RTT, or if the incoming_bitrate is more than 5% above the current // estimate. Should be used to decide if we should reduce the rate further // when over-using. - bool TimeToReduceFurther(int64_t time_now, - unsigned int incoming_bitrate) const; + virtual bool TimeToReduceFurther(int64_t time_now, + uint32_t incoming_bitrate_bps) const = 0; + virtual uint32_t LatestEstimate() const = 0; + virtual uint32_t UpdateBandwidthEstimate(int64_t now_ms) = 0; + virtual void SetRtt(unsigned int rtt) = 0; + virtual RateControlRegion Update(const RateControlInput* input, + int64_t now_ms) = 0; + virtual void SetEstimate(int bitrate_bps, int64_t time_now_ms) = 0; - int32_t SetConfiguredBitRates(uint32_t min_bit_rate, uint32_t max_bit_rate); - uint32_t LatestEstimate() const; - uint32_t UpdateBandwidthEstimate(int64_t now_ms); - void SetRtt(unsigned int rtt); - RateControlRegion Update(const RateControlInput* input, int64_t now_ms); - - private: - uint32_t ChangeBitRate(uint32_t current_bit_rate, - uint32_t incoming_bit_rate, - double delay_factor, - int64_t now_ms); - double RateIncreaseFactor(int64_t now_ms, - int64_t last_ms, - uint32_t reaction_time_ms, - double noise_var) const; - void UpdateChangePeriod(int64_t now_ms); - void UpdateMaxBitRateEstimate(float incoming_bit_rate_kbps); - void ChangeState(const RateControlInput& input, int64_t now_ms); - void ChangeState(RateControlState new_state); - void ChangeRegion(RateControlRegion region); - - uint32_t min_configured_bit_rate_; - uint32_t max_configured_bit_rate_; - uint32_t current_bit_rate_; - uint32_t max_hold_rate_; - float avg_max_bit_rate_; - float var_max_bit_rate_; - RateControlState rate_control_state_; - RateControlState came_from_state_; - RateControlRegion rate_control_region_; - int64_t last_bit_rate_change_; - RateControlInput current_input_; - bool updated_; - int64_t time_first_incoming_estimate_; - bool initialized_bit_rate_; - float avg_change_period_; - int64_t last_change_ms_; - float beta_; - unsigned int rtt_; + protected: + static const int kMaxFeedbackIntervalMs = 1000; }; } // namespace webrtc -#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_REMOTE_RATE_CONTROL_H_ +#endif // WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_RATE_CONTROL_H_