diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/modules/remote_bitrate_estimator | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/remote_bitrate_estimator')
37 files changed, 8015 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/BUILD.gn b/third_party/libwebrtc/modules/remote_bitrate_estimator/BUILD.gn new file mode 100644 index 0000000000..463530b973 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/BUILD.gn @@ -0,0 +1,143 @@ +# 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. + +import("../../webrtc.gni") + +rtc_library("remote_bitrate_estimator") { + sources = [ + "aimd_rate_control.cc", + "aimd_rate_control.h", + "bwe_defines.cc", + "include/bwe_defines.h", + "include/remote_bitrate_estimator.h", + "inter_arrival.cc", + "inter_arrival.h", + "overuse_detector.cc", + "overuse_detector.h", + "overuse_estimator.cc", + "overuse_estimator.h", + "packet_arrival_map.cc", + "packet_arrival_map.h", + "remote_bitrate_estimator_abs_send_time.cc", + "remote_bitrate_estimator_abs_send_time.h", + "remote_bitrate_estimator_single_stream.cc", + "remote_bitrate_estimator_single_stream.h", + "remote_estimator_proxy.cc", + "remote_estimator_proxy.h", + "test/bwe_test_logging.h", + ] + + if (rtc_enable_bwe_test_logging) { + defines = [ "BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=1" ] + + sources += [ "test/bwe_test_logging.cc" ] + } else { + defines = [ "BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=0" ] + } + + deps = [ + "../../api:field_trials_view", + "../../api:network_state_predictor_api", + "../../api:rtp_headers", + "../../api/transport:field_trial_based_config", + "../../api/transport:network_control", + "../../api/units:data_rate", + "../../api/units:data_size", + "../../api/units:time_delta", + "../../api/units:timestamp", + "../../modules:module_api", + "../../modules:module_api_public", + "../../modules/congestion_controller/goog_cc:link_capacity_estimator", + "../../modules/rtp_rtcp:rtp_rtcp_format", + "../../rtc_base:checks", + "../../rtc_base:logging", + "../../rtc_base:macromagic", + "../../rtc_base:platform_thread", + "../../rtc_base:race_checker", + "../../rtc_base:rate_statistics", + "../../rtc_base:rtc_numerics", + "../../rtc_base:safe_minmax", + "../../rtc_base:stringutils", + "../../rtc_base/experiments:field_trial_parser", + "../../rtc_base/synchronization:mutex", + "../../system_wrappers", + "../../system_wrappers:field_trial", + "../../system_wrappers:metrics", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/strings", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +if (!build_with_chromium) { + rtc_library("bwe_rtp") { + testonly = true + sources = [ + "tools/bwe_rtp.cc", + "tools/bwe_rtp.h", + ] + deps = [ + "../../test:rtp_test_utils", + "../rtp_rtcp:rtp_rtcp_format", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/flags:flag", + "//third_party/abseil-cpp/absl/flags:parse", + ] + } + + rtc_executable("rtp_to_text") { + testonly = true + sources = [ "tools/rtp_to_text.cc" ] + deps = [ + ":bwe_rtp", + "../../rtc_base:macromagic", + "../../rtc_base:stringutils", + "../../test:rtp_test_utils", + "../rtp_rtcp:rtp_rtcp_format", + ] + } +} + +if (rtc_include_tests) { + rtc_library("remote_bitrate_estimator_unittests") { + testonly = true + + sources = [ + "aimd_rate_control_unittest.cc", + "inter_arrival_unittest.cc", + "overuse_detector_unittest.cc", + "packet_arrival_map_test.cc", + "remote_bitrate_estimator_abs_send_time_unittest.cc", + "remote_bitrate_estimator_single_stream_unittest.cc", + "remote_bitrate_estimator_unittest_helper.cc", + "remote_bitrate_estimator_unittest_helper.h", + "remote_estimator_proxy_unittest.cc", + ] + deps = [ + ":remote_bitrate_estimator", + "..:module_api_public", + "../../api/transport:field_trial_based_config", + "../../api/transport:mock_network_control", + "../../api/transport:network_control", + "../../api/units:data_rate", + "../../api/units:data_size", + "../../api/units:time_delta", + "../../api/units:timestamp", + "../../rtc_base:checks", + "../../rtc_base:random", + "../../system_wrappers", + "../../test:field_trial", + "../../test:fileutils", + "../../test:test_support", + "../pacing", + "../rtp_rtcp:rtp_rtcp_format", + ] + } +} diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/DEPS b/third_party/libwebrtc/modules/remote_bitrate_estimator/DEPS new file mode 100644 index 0000000000..35a62119e5 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+logging/rtc_event_log", + "+system_wrappers", + # Avoid directly using field_trial. Instead use FieldTrialsView. + "-system_wrappers/include/field_trial.h", +] diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/OWNERS b/third_party/libwebrtc/modules/remote_bitrate_estimator/OWNERS new file mode 100644 index 0000000000..993e2fd77b --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/OWNERS @@ -0,0 +1,5 @@ +danilchap@webrtc.org +stefan@webrtc.org +terelius@webrtc.org +mflodman@webrtc.org +perkj@webrtc.org diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc new file mode 100644 index 0000000000..6c3638b59f --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc @@ -0,0 +1,431 @@ +/* + * 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 "modules/remote_bitrate_estimator/aimd_rate_control.h" + +#include <inttypes.h> + +#include <algorithm> +#include <cmath> +#include <cstdio> +#include <string> + +#include "absl/strings/match.h" +#include "api/transport/network_types.h" +#include "api/units/data_rate.h" +#include "modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "modules/remote_bitrate_estimator/overuse_detector.h" +#include "rtc_base/checks.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { +namespace { + +constexpr TimeDelta kDefaultRtt = TimeDelta::Millis(200); +constexpr double kDefaultBackoffFactor = 0.85; + +constexpr char kBweBackOffFactorExperiment[] = "WebRTC-BweBackOffFactor"; + +bool IsEnabled(const FieldTrialsView& field_trials, absl::string_view key) { + return absl::StartsWith(field_trials.Lookup(key), "Enabled"); +} + +double ReadBackoffFactor(const FieldTrialsView& key_value_config) { + std::string experiment_string = + key_value_config.Lookup(kBweBackOffFactorExperiment); + double backoff_factor; + int parsed_values = + sscanf(experiment_string.c_str(), "Enabled-%lf", &backoff_factor); + if (parsed_values == 1) { + if (backoff_factor >= 1.0) { + RTC_LOG(LS_WARNING) << "Back-off factor must be less than 1."; + } else if (backoff_factor <= 0.0) { + RTC_LOG(LS_WARNING) << "Back-off factor must be greater than 0."; + } else { + return backoff_factor; + } + } + RTC_LOG(LS_WARNING) << "Failed to parse parameters for AimdRateControl " + "experiment from field trial string. Using default."; + return kDefaultBackoffFactor; +} + +} // namespace + +AimdRateControl::AimdRateControl(const FieldTrialsView* key_value_config) + : AimdRateControl(key_value_config, /* send_side =*/false) {} + +AimdRateControl::AimdRateControl(const FieldTrialsView* key_value_config, + bool send_side) + : min_configured_bitrate_(kCongestionControllerMinBitrate), + max_configured_bitrate_(DataRate::KilobitsPerSec(30000)), + current_bitrate_(max_configured_bitrate_), + latest_estimated_throughput_(current_bitrate_), + link_capacity_(), + rate_control_state_(RateControlState::kRcHold), + time_last_bitrate_change_(Timestamp::MinusInfinity()), + time_last_bitrate_decrease_(Timestamp::MinusInfinity()), + time_first_throughput_estimate_(Timestamp::MinusInfinity()), + bitrate_is_initialized_(false), + beta_(IsEnabled(*key_value_config, kBweBackOffFactorExperiment) + ? ReadBackoffFactor(*key_value_config) + : kDefaultBackoffFactor), + in_alr_(false), + rtt_(kDefaultRtt), + send_side_(send_side), + no_bitrate_increase_in_alr_( + IsEnabled(*key_value_config, + "WebRTC-DontIncreaseDelayBasedBweInAlr")), + initial_backoff_interval_("initial_backoff_interval"), + link_capacity_fix_("link_capacity_fix") { + ParseFieldTrial( + {&disable_estimate_bounded_increase_, &estimate_bounded_increase_ratio_, + &ignore_throughput_limit_if_network_estimate_, + &ignore_network_estimate_decrease_, &increase_to_network_estimate_}, + key_value_config->Lookup("WebRTC-Bwe-EstimateBoundedIncrease")); + // E.g + // WebRTC-BweAimdRateControlConfig/initial_backoff_interval:100ms/ + ParseFieldTrial({&initial_backoff_interval_, &link_capacity_fix_}, + key_value_config->Lookup("WebRTC-BweAimdRateControlConfig")); + if (initial_backoff_interval_) { + RTC_LOG(LS_INFO) << "Using aimd rate control with initial back-off interval" + " " + << ToString(*initial_backoff_interval_) << "."; + } + RTC_LOG(LS_INFO) << "Using aimd rate control with back off factor " << beta_; +} + +AimdRateControl::~AimdRateControl() {} + +void AimdRateControl::SetStartBitrate(DataRate start_bitrate) { + current_bitrate_ = start_bitrate; + latest_estimated_throughput_ = current_bitrate_; + bitrate_is_initialized_ = true; +} + +void AimdRateControl::SetMinBitrate(DataRate min_bitrate) { + min_configured_bitrate_ = min_bitrate; + current_bitrate_ = std::max(min_bitrate, current_bitrate_); +} + +bool AimdRateControl::ValidEstimate() const { + return bitrate_is_initialized_; +} + +TimeDelta AimdRateControl::GetFeedbackInterval() const { + // Estimate how often we can send RTCP if we allocate up to 5% of bandwidth + // to feedback. + const DataSize kRtcpSize = DataSize::Bytes(80); + const DataRate rtcp_bitrate = current_bitrate_ * 0.05; + const TimeDelta interval = kRtcpSize / rtcp_bitrate; + const TimeDelta kMinFeedbackInterval = TimeDelta::Millis(200); + const TimeDelta kMaxFeedbackInterval = TimeDelta::Millis(1000); + return interval.Clamped(kMinFeedbackInterval, kMaxFeedbackInterval); +} + +bool AimdRateControl::TimeToReduceFurther(Timestamp at_time, + DataRate estimated_throughput) const { + const TimeDelta bitrate_reduction_interval = + rtt_.Clamped(TimeDelta::Millis(10), TimeDelta::Millis(200)); + if (at_time - time_last_bitrate_change_ >= bitrate_reduction_interval) { + return true; + } + if (ValidEstimate()) { + // TODO(terelius/holmer): Investigate consequences of increasing + // the threshold to 0.95 * LatestEstimate(). + const DataRate threshold = 0.5 * LatestEstimate(); + return estimated_throughput < threshold; + } + return false; +} + +bool AimdRateControl::InitialTimeToReduceFurther(Timestamp at_time) const { + if (!initial_backoff_interval_) { + return ValidEstimate() && + TimeToReduceFurther(at_time, + LatestEstimate() / 2 - DataRate::BitsPerSec(1)); + } + // TODO(terelius): We could use the RTT (clamped to suitable limits) instead + // of a fixed bitrate_reduction_interval. + if (time_last_bitrate_decrease_.IsInfinite() || + at_time - time_last_bitrate_decrease_ >= *initial_backoff_interval_) { + return true; + } + return false; +} + +DataRate AimdRateControl::LatestEstimate() const { + return current_bitrate_; +} + +void AimdRateControl::SetRtt(TimeDelta rtt) { + rtt_ = rtt; +} + +DataRate AimdRateControl::Update(const RateControlInput* input, + Timestamp at_time) { + RTC_CHECK(input); + + // Set the initial bit rate value to what we're receiving the first half + // second. + // TODO(bugs.webrtc.org/9379): The comment above doesn't match to the code. + if (!bitrate_is_initialized_) { + const TimeDelta kInitializationTime = TimeDelta::Seconds(5); + RTC_DCHECK_LE(kBitrateWindowMs, kInitializationTime.ms()); + if (time_first_throughput_estimate_.IsInfinite()) { + if (input->estimated_throughput) + time_first_throughput_estimate_ = at_time; + } else if (at_time - time_first_throughput_estimate_ > + kInitializationTime && + input->estimated_throughput) { + current_bitrate_ = *input->estimated_throughput; + bitrate_is_initialized_ = true; + } + } + + ChangeBitrate(*input, at_time); + return current_bitrate_; +} + +void AimdRateControl::SetInApplicationLimitedRegion(bool in_alr) { + in_alr_ = in_alr; +} + +void AimdRateControl::SetEstimate(DataRate bitrate, Timestamp at_time) { + bitrate_is_initialized_ = true; + DataRate prev_bitrate = current_bitrate_; + current_bitrate_ = ClampBitrate(bitrate); + time_last_bitrate_change_ = at_time; + if (current_bitrate_ < prev_bitrate) { + time_last_bitrate_decrease_ = at_time; + } +} + +void AimdRateControl::SetNetworkStateEstimate( + const absl::optional<NetworkStateEstimate>& estimate) { + network_estimate_ = estimate; +} + +double AimdRateControl::GetNearMaxIncreaseRateBpsPerSecond() const { + RTC_DCHECK(!current_bitrate_.IsZero()); + const TimeDelta kFrameInterval = TimeDelta::Seconds(1) / 30; + DataSize frame_size = current_bitrate_ * kFrameInterval; + const DataSize kPacketSize = DataSize::Bytes(1200); + double packets_per_frame = std::ceil(frame_size / kPacketSize); + DataSize avg_packet_size = frame_size / packets_per_frame; + + // Approximate the over-use estimator delay to 100 ms. + TimeDelta response_time = rtt_ + TimeDelta::Millis(100); + + response_time = response_time * 2; + double increase_rate_bps_per_second = + (avg_packet_size / response_time).bps<double>(); + double kMinIncreaseRateBpsPerSecond = 4000; + return std::max(kMinIncreaseRateBpsPerSecond, increase_rate_bps_per_second); +} + +TimeDelta AimdRateControl::GetExpectedBandwidthPeriod() const { + const TimeDelta kMinPeriod = TimeDelta::Seconds(2); + const TimeDelta kDefaultPeriod = TimeDelta::Seconds(3); + const TimeDelta kMaxPeriod = TimeDelta::Seconds(50); + + double increase_rate_bps_per_second = GetNearMaxIncreaseRateBpsPerSecond(); + if (!last_decrease_) + return kDefaultPeriod; + double time_to_recover_decrease_seconds = + last_decrease_->bps() / increase_rate_bps_per_second; + TimeDelta period = TimeDelta::Seconds(time_to_recover_decrease_seconds); + return period.Clamped(kMinPeriod, kMaxPeriod); +} + +void AimdRateControl::ChangeBitrate(const RateControlInput& input, + Timestamp at_time) { + absl::optional<DataRate> new_bitrate; + DataRate estimated_throughput = + input.estimated_throughput.value_or(latest_estimated_throughput_); + if (input.estimated_throughput) + latest_estimated_throughput_ = *input.estimated_throughput; + + // An over-use should always trigger us to reduce the bitrate, even though + // we have not yet established our first estimate. By acting on the over-use, + // we will end up with a valid estimate. + if (!bitrate_is_initialized_ && + input.bw_state != BandwidthUsage::kBwOverusing) + return; + + ChangeState(input, at_time); + + switch (rate_control_state_) { + case RateControlState::kRcHold: + break; + + case RateControlState::kRcIncrease: { + if (estimated_throughput > link_capacity_.UpperBound()) + link_capacity_.Reset(); + + // We limit the new bitrate based on the troughput to avoid unlimited + // bitrate increases. We allow a bit more lag at very low rates to not too + // easily get stuck if the encoder produces uneven outputs. + DataRate increase_limit = + 1.5 * estimated_throughput + DataRate::KilobitsPerSec(10); + if (ignore_throughput_limit_if_network_estimate_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + // If we have a Network estimate, we do allow the estimate to increase. + increase_limit = network_estimate_->link_capacity_upper * + estimate_bounded_increase_ratio_.Get(); + } else if (send_side_ && in_alr_ && no_bitrate_increase_in_alr_) { + // Do not increase the delay based estimate in alr since the estimator + // will not be able to get transport feedback necessary to detect if + // the new estimate is correct. + // If we have previously increased above the limit (for instance due to + // probing), we don't allow further changes. + increase_limit = current_bitrate_; + } + + if (current_bitrate_ < increase_limit) { + DataRate increased_bitrate = DataRate::MinusInfinity(); + if (increase_to_network_estimate_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + increased_bitrate = increase_limit; + } else if (link_capacity_.has_estimate()) { + // The link_capacity estimate is reset if the measured throughput + // is too far from the estimate. We can therefore assume that our + // target rate is reasonably close to link capacity and use additive + // increase. + DataRate additive_increase = + AdditiveRateIncrease(at_time, time_last_bitrate_change_); + increased_bitrate = current_bitrate_ + additive_increase; + } else { + // If we don't have an estimate of the link capacity, use faster ramp + // up to discover the capacity. + DataRate multiplicative_increase = MultiplicativeRateIncrease( + at_time, time_last_bitrate_change_, current_bitrate_); + increased_bitrate = current_bitrate_ + multiplicative_increase; + } + new_bitrate = std::min(increased_bitrate, increase_limit); + } + time_last_bitrate_change_ = at_time; + break; + } + + case RateControlState::kRcDecrease: { + DataRate decreased_bitrate = DataRate::PlusInfinity(); + + // Set bit rate to something slightly lower than the measured throughput + // to get rid of any self-induced delay. + decreased_bitrate = estimated_throughput * beta_; + if (decreased_bitrate > current_bitrate_ && !link_capacity_fix_) { + // TODO(terelius): The link_capacity estimate may be based on old + // throughput measurements. Relying on them may lead to unnecessary + // BWE drops. + if (link_capacity_.has_estimate()) { + decreased_bitrate = beta_ * link_capacity_.estimate(); + } + } + // Avoid increasing the rate when over-using. + if (decreased_bitrate < current_bitrate_) { + new_bitrate = decreased_bitrate; + } + + if (bitrate_is_initialized_ && estimated_throughput < current_bitrate_) { + if (!new_bitrate.has_value()) { + last_decrease_ = DataRate::Zero(); + } else { + last_decrease_ = current_bitrate_ - *new_bitrate; + } + } + if (estimated_throughput < link_capacity_.LowerBound()) { + // The current throughput is far from the estimated link capacity. Clear + // the estimate to allow an immediate update in OnOveruseDetected. + link_capacity_.Reset(); + } + + bitrate_is_initialized_ = true; + link_capacity_.OnOveruseDetected(estimated_throughput); + // Stay on hold until the pipes are cleared. + rate_control_state_ = RateControlState::kRcHold; + time_last_bitrate_change_ = at_time; + time_last_bitrate_decrease_ = at_time; + break; + } + default: + RTC_DCHECK_NOTREACHED(); + } + + current_bitrate_ = ClampBitrate(new_bitrate.value_or(current_bitrate_)); +} + +DataRate AimdRateControl::ClampBitrate(DataRate new_bitrate) const { + if (!disable_estimate_bounded_increase_ && network_estimate_ && + network_estimate_->link_capacity_upper.IsFinite()) { + DataRate upper_bound = network_estimate_->link_capacity_upper * + estimate_bounded_increase_ratio_.Get(); + if (ignore_network_estimate_decrease_) { + upper_bound = std::max(upper_bound, current_bitrate_); + } + new_bitrate = std::min(upper_bound, new_bitrate); + } + if (network_estimate_ && network_estimate_->link_capacity_lower.IsFinite() && + new_bitrate < current_bitrate_) { + new_bitrate = std::min( + current_bitrate_, + std::max(new_bitrate, network_estimate_->link_capacity_lower * beta_)); + } + new_bitrate = std::max(new_bitrate, min_configured_bitrate_); + return new_bitrate; +} + +DataRate AimdRateControl::MultiplicativeRateIncrease( + Timestamp at_time, + Timestamp last_time, + DataRate current_bitrate) const { + double alpha = 1.08; + if (last_time.IsFinite()) { + auto time_since_last_update = at_time - last_time; + alpha = pow(alpha, std::min(time_since_last_update.seconds<double>(), 1.0)); + } + DataRate multiplicative_increase = + std::max(current_bitrate * (alpha - 1.0), DataRate::BitsPerSec(1000)); + return multiplicative_increase; +} + +DataRate AimdRateControl::AdditiveRateIncrease(Timestamp at_time, + Timestamp last_time) const { + double time_period_seconds = (at_time - last_time).seconds<double>(); + double data_rate_increase_bps = + GetNearMaxIncreaseRateBpsPerSecond() * time_period_seconds; + return DataRate::BitsPerSec(data_rate_increase_bps); +} + +void AimdRateControl::ChangeState(const RateControlInput& input, + Timestamp at_time) { + switch (input.bw_state) { + case BandwidthUsage::kBwNormal: + if (rate_control_state_ == RateControlState::kRcHold) { + time_last_bitrate_change_ = at_time; + rate_control_state_ = RateControlState::kRcIncrease; + } + break; + case BandwidthUsage::kBwOverusing: + if (rate_control_state_ != RateControlState::kRcDecrease) { + rate_control_state_ = RateControlState::kRcDecrease; + } + break; + case BandwidthUsage::kBwUnderusing: + rate_control_state_ = RateControlState::kRcHold; + break; + default: + RTC_DCHECK_NOTREACHED(); + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h new file mode 100644 index 0000000000..8321fd5239 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h @@ -0,0 +1,122 @@ +/* + * 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 MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ + +#include <stdint.h> + +#include "absl/types/optional.h" +#include "api/field_trials_view.h" +#include "api/transport/network_types.h" +#include "api/units/data_rate.h" +#include "api/units/timestamp.h" +#include "modules/congestion_controller/goog_cc/link_capacity_estimator.h" +#include "modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "rtc_base/experiments/field_trial_parser.h" + +namespace webrtc { +// A rate control 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: + explicit AimdRateControl(const FieldTrialsView* key_value_config); + AimdRateControl(const FieldTrialsView* key_value_config, bool send_side); + ~AimdRateControl(); + + // Returns true if the target bitrate has been initialized. This happens + // either if it has been explicitly set via SetStartBitrate/SetEstimate, or if + // we have measured a throughput. + bool ValidEstimate() const; + void SetStartBitrate(DataRate start_bitrate); + void SetMinBitrate(DataRate min_bitrate); + TimeDelta GetFeedbackInterval() const; + + // Returns true if the bitrate estimate hasn't been changed for more than + // an RTT, or if the estimated_throughput is less than half of the current + // estimate. Should be used to decide if we should reduce the rate further + // when over-using. + bool TimeToReduceFurther(Timestamp at_time, + DataRate estimated_throughput) const; + // As above. To be used if overusing before we have measured a throughput. + bool InitialTimeToReduceFurther(Timestamp at_time) const; + + DataRate LatestEstimate() const; + void SetRtt(TimeDelta rtt); + DataRate Update(const RateControlInput* input, Timestamp at_time); + void SetInApplicationLimitedRegion(bool in_alr); + void SetEstimate(DataRate bitrate, Timestamp at_time); + void SetNetworkStateEstimate( + const absl::optional<NetworkStateEstimate>& estimate); + + // Returns the increase rate when used bandwidth is near the link capacity. + double GetNearMaxIncreaseRateBpsPerSecond() const; + // Returns the expected time between overuse signals (assuming steady state). + TimeDelta GetExpectedBandwidthPeriod() const; + + private: + enum class RateControlState { kRcHold, kRcIncrease, kRcDecrease }; + + friend class GoogCcStatePrinter; + // Update the target bitrate based on, among other things, the current rate + // control state, the current target bitrate and the estimated throughput. + // 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 + // current throughput. When in the "hold" state the bitrate will be kept + // constant to allow built up queues to drain. + void ChangeBitrate(const RateControlInput& input, Timestamp at_time); + + DataRate ClampBitrate(DataRate new_bitrate) const; + DataRate MultiplicativeRateIncrease(Timestamp at_time, + Timestamp last_ms, + DataRate current_bitrate) const; + DataRate AdditiveRateIncrease(Timestamp at_time, Timestamp last_time) const; + void UpdateChangePeriod(Timestamp at_time); + void ChangeState(const RateControlInput& input, Timestamp at_time); + + DataRate min_configured_bitrate_; + DataRate max_configured_bitrate_; + DataRate current_bitrate_; + DataRate latest_estimated_throughput_; + LinkCapacityEstimator link_capacity_; + absl::optional<NetworkStateEstimate> network_estimate_; + RateControlState rate_control_state_; + Timestamp time_last_bitrate_change_; + Timestamp time_last_bitrate_decrease_; + Timestamp time_first_throughput_estimate_; + bool bitrate_is_initialized_; + double beta_; + bool in_alr_; + TimeDelta rtt_; + const bool send_side_; + // Allow the delay based estimate to only increase as long as application + // limited region (alr) is not detected. + const bool no_bitrate_increase_in_alr_; + // If false, uses estimated link capacity upper bound * + // `estimate_bounded_increase_ratio_` as upper limit for the estimate. + FieldTrialFlag disable_estimate_bounded_increase_{"Disabled"}; + FieldTrialParameter<double> estimate_bounded_increase_ratio_{"ratio", 1.0}; + FieldTrialParameter<bool> ignore_throughput_limit_if_network_estimate_{ + "ignore_acked", false}; + FieldTrialParameter<bool> increase_to_network_estimate_{"immediate_incr", + false}; + FieldTrialParameter<bool> ignore_network_estimate_decrease_{"ignore_decr", + false}; + absl::optional<DataRate> last_decrease_; + FieldTrialOptional<TimeDelta> initial_backoff_interval_; + FieldTrialFlag link_capacity_fix_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_AIMD_RATE_CONTROL_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc new file mode 100644 index 0000000000..aa80dae55b --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc @@ -0,0 +1,484 @@ +/* + * Copyright (c) 2016 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 "modules/remote_bitrate_estimator/aimd_rate_control.h" + +#include <memory> + +#include "api/transport/field_trial_based_config.h" +#include "api/units/data_rate.h" +#include "system_wrappers/include/clock.h" +#include "test/field_trial.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +constexpr int64_t kClockInitialTime = 123456; + +constexpr int kMinBwePeriodMs = 2000; +constexpr int kDefaultPeriodMs = 3000; +constexpr int kMaxBwePeriodMs = 50000; + +// After an overuse, we back off to 85% to the received bitrate. +constexpr double kFractionAfterOveruse = 0.85; + +struct AimdRateControlStates { + std::unique_ptr<AimdRateControl> aimd_rate_control; + std::unique_ptr<SimulatedClock> simulated_clock; + FieldTrialBasedConfig field_trials; +}; + +AimdRateControlStates CreateAimdRateControlStates(bool send_side = false) { + AimdRateControlStates states; + states.aimd_rate_control.reset( + new AimdRateControl(&states.field_trials, send_side)); + states.simulated_clock.reset(new SimulatedClock(kClockInitialTime)); + return states; +} +absl::optional<DataRate> OptionalRateFromOptionalBps( + absl::optional<int> bitrate_bps) { + if (bitrate_bps) { + return DataRate::BitsPerSec(*bitrate_bps); + } else { + return absl::nullopt; + } +} +void UpdateRateControl(const AimdRateControlStates& states, + const BandwidthUsage& bandwidth_usage, + absl::optional<uint32_t> throughput_estimate, + int64_t now_ms) { + RateControlInput input(bandwidth_usage, + OptionalRateFromOptionalBps(throughput_estimate)); + states.aimd_rate_control->Update(&input, Timestamp::Millis(now_ms)); +} +void SetEstimate(const AimdRateControlStates& states, int bitrate_bps) { + states.aimd_rate_control->SetEstimate(DataRate::BitsPerSec(bitrate_bps), + states.simulated_clock->CurrentTime()); +} + +} // namespace + +TEST(AimdRateControlTest, MinNearMaxIncreaseRateOnLowBandwith) { + auto states = CreateAimdRateControlStates(); + constexpr int kBitrate = 30000; + SetEstimate(states, kBitrate); + EXPECT_EQ(4000, + states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); +} + +TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn90kbpsAnd200msRtt) { + auto states = CreateAimdRateControlStates(); + constexpr int kBitrate = 90000; + SetEstimate(states, kBitrate); + EXPECT_EQ(5000, + states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); +} + +TEST(AimdRateControlTest, NearMaxIncreaseRateIs5kbpsOn60kbpsAnd100msRtt) { + auto states = CreateAimdRateControlStates(); + constexpr int kBitrate = 60000; + SetEstimate(states, kBitrate); + states.aimd_rate_control->SetRtt(TimeDelta::Millis(100)); + EXPECT_EQ(5000, + states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); +} + +TEST(AimdRateControlTest, GetIncreaseRateAndBandwidthPeriod) { + // Smoothing experiment disabled + auto states = CreateAimdRateControlStates(); + constexpr int kBitrate = 300000; + SetEstimate(states, kBitrate); + UpdateRateControl(states, BandwidthUsage::kBwOverusing, kBitrate, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_NEAR(14000, + states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond(), + 1000); + EXPECT_EQ(kDefaultPeriodMs, + states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); +} + +TEST(AimdRateControlTest, BweLimitedByAckedBitrate) { + auto states = CreateAimdRateControlStates(); + constexpr int kAckedBitrate = 10000; + SetEstimate(states, kAckedBitrate); + while (states.simulated_clock->TimeInMilliseconds() - kClockInitialTime < + 20000) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + ASSERT_TRUE(states.aimd_rate_control->ValidEstimate()); + EXPECT_EQ(static_cast<uint32_t>(1.5 * kAckedBitrate + 10000), + states.aimd_rate_control->LatestEstimate().bps()); +} + +TEST(AimdRateControlTest, BweNotLimitedByDecreasingAckedBitrate) { + auto states = CreateAimdRateControlStates(); + constexpr int kAckedBitrate = 100000; + SetEstimate(states, kAckedBitrate); + while (states.simulated_clock->TimeInMilliseconds() - kClockInitialTime < + 20000) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + ASSERT_TRUE(states.aimd_rate_control->ValidEstimate()); + // If the acked bitrate decreases the BWE shouldn't be reduced to 1.5x + // what's being acked, but also shouldn't get to increase more. + uint32_t prev_estimate = states.aimd_rate_control->LatestEstimate().bps(); + UpdateRateControl(states, BandwidthUsage::kBwNormal, kAckedBitrate / 2, + states.simulated_clock->TimeInMilliseconds()); + uint32_t new_estimate = states.aimd_rate_control->LatestEstimate().bps(); + EXPECT_NEAR(new_estimate, static_cast<uint32_t>(1.5 * kAckedBitrate + 10000), + 2000); + EXPECT_EQ(new_estimate, prev_estimate); +} + +TEST(AimdRateControlTest, DefaultPeriodUntilFirstOveruse) { + // Smoothing experiment disabled + auto states = CreateAimdRateControlStates(); + states.aimd_rate_control->SetStartBitrate(DataRate::KilobitsPerSec(300)); + EXPECT_EQ(kDefaultPeriodMs, + states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + UpdateRateControl(states, BandwidthUsage::kBwOverusing, 280000, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_NE(kDefaultPeriodMs, + states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); +} + +TEST(AimdRateControlTest, ExpectedPeriodAfter20kbpsDropAnd5kbpsIncrease) { + auto states = CreateAimdRateControlStates(); + constexpr int kInitialBitrate = 110000; + SetEstimate(states, kInitialBitrate); + states.simulated_clock->AdvanceTimeMilliseconds(100); + // Make the bitrate drop by 20 kbps to get to 90 kbps. + // The rate increase at 90 kbps should be 5 kbps, so the period should be 4 s. + constexpr int kAckedBitrate = + (kInitialBitrate - 20000) / kFractionAfterOveruse; + UpdateRateControl(states, BandwidthUsage::kBwOverusing, kAckedBitrate, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_EQ(5000, + states.aimd_rate_control->GetNearMaxIncreaseRateBpsPerSecond()); + EXPECT_EQ(4000, states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); +} + +TEST(AimdRateControlTest, BandwidthPeriodIsNotBelowMin) { + auto states = CreateAimdRateControlStates(); + constexpr int kInitialBitrate = 10000; + SetEstimate(states, kInitialBitrate); + states.simulated_clock->AdvanceTimeMilliseconds(100); + // Make a small (1.5 kbps) bitrate drop to 8.5 kbps. + UpdateRateControl(states, BandwidthUsage::kBwOverusing, kInitialBitrate - 1, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_EQ(kMinBwePeriodMs, + states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); +} + +TEST(AimdRateControlTest, BandwidthPeriodIsNotAboveMaxNoSmoothingExp) { + auto states = CreateAimdRateControlStates(); + constexpr int kInitialBitrate = 10010000; + SetEstimate(states, kInitialBitrate); + states.simulated_clock->AdvanceTimeMilliseconds(100); + // Make a large (10 Mbps) bitrate drop to 10 kbps. + constexpr int kAckedBitrate = 10000 / kFractionAfterOveruse; + UpdateRateControl(states, BandwidthUsage::kBwOverusing, kAckedBitrate, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_EQ(kMaxBwePeriodMs, + states.aimd_rate_control->GetExpectedBandwidthPeriod().ms()); +} + +TEST(AimdRateControlTest, SendingRateBoundedWhenThroughputNotEstimated) { + auto states = CreateAimdRateControlStates(); + constexpr int kInitialBitrateBps = 123000; + UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, + states.simulated_clock->TimeInMilliseconds()); + // AimdRateControl sets the initial bit rate to what it receives after + // five seconds has passed. + // TODO(bugs.webrtc.org/9379): The comment in the AimdRateControl does not + // match the constant. + constexpr int kInitializationTimeMs = 5000; + states.simulated_clock->AdvanceTimeMilliseconds(kInitializationTimeMs + 1); + UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, + states.simulated_clock->TimeInMilliseconds()); + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_LE(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps * 1.5 + 10000); +} + +TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlr) { + // When alr is detected, the delay based estimator is not allowed to increase + // bwe since there will be no feedback from the network if the new estimate + // is correct. + test::ScopedFieldTrials override_field_trials( + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, + states.simulated_clock->TimeInMilliseconds()); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); + + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); +} + +TEST(AimdRateControlTest, SetEstimateIncreaseBweInAlr) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); + SetEstimate(states, 2 * kInitialBitrateBps); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + 2 * kInitialBitrateBps); +} + +TEST(AimdRateControlTest, SetEstimateUpperLimitedByNetworkEstimate) { + auto states = CreateAimdRateControlStates(/*send_side=*/true); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 500'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper); +} + +TEST(AimdRateControlTest, SetEstimateLowerLimitedByNetworkEstimate) { + auto states = CreateAimdRateControlStates(/*send_side=*/true); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 100'000); + // 0.85 is default backoff factor. (`beta_`) + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_lower * 0.85); +} + +TEST(AimdRateControlTest, + SetEstimateIgnoredIfLowerThanNetworkEstimateAndCurrent) { + auto states = CreateAimdRateControlStates(/*send_side=*/true); + SetEstimate(states, 200'000); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + // Ignore the next SetEstimate, since the estimate is lower than 85% of + // the network estimate. + SetEstimate(states, 100'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate().kbps(), 200); +} + +TEST(AimdRateControlTest, SetEstimateIgnoresNetworkEstimatesLowerThanCurrent) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,ignore_decr:true/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + states.aimd_rate_control->SetStartBitrate(DataRate::KilobitsPerSec(30)); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(400); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + SetEstimate(states, 500'000); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); + + NetworkStateEstimate lower_network_estimate; + lower_network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(300); + states.aimd_rate_control->SetNetworkStateEstimate(lower_network_estimate); + SetEstimate(states, 500'000); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + +TEST(AimdRateControlTest, EstimateIncreaseWhileNotInAlr) { + // Allow the estimate to increase as long as alr is not detected to ensure + // tha BWE can not get stuck at a certain bitrate. + test::ScopedFieldTrials override_field_trials( + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(false); + UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, + states.simulated_clock->TimeInMilliseconds()); + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_GT(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); +} + +TEST(AimdRateControlTest, EstimateNotLimitedByNetworkEstimateIfDisabled) { + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/Disabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(false); + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(150); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_GT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper); +} + +TEST(AimdRateControlTest, + EstimateSlowlyIncreaseToUpperLinkCapacityEstimateIfConfigured) { + // Even if alr is detected, the delay based estimator is allowed to increase + // up to a percentage of upper link capacity. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,immediate_incr:false/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 10; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + EXPECT_LT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); + } + for (int i = 0; i < 50; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + +TEST(AimdRateControlTest, + EstimateImmediatelyIncreaseToUpperLinkCapacityEstimateIfConfigured) { + // Even if alr is detected, the delay based estimator is allowed to increase + // up to a percentage of upper link capacity. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,immediate_incr:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_upper * 0.85); +} + +TEST(AimdRateControlTest, EstimateNotLoweredByNetworkEstimate) { + // The delay based estimator is allowed to increase up to a percentage of + // upper link capacity but does not decrease unless the delay detector + // discover an overuse. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/" + "ratio:0.85,ignore_acked:true,ignore_decr:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + constexpr int kEstimatedThroughputBps = 30'000; + SetEstimate(states, kInitialBitrateBps); + + NetworkStateEstimate network_estimate; + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(200); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + DataRate estimate_after_increase = states.aimd_rate_control->LatestEstimate(); + ASSERT_EQ(estimate_after_increase, + network_estimate.link_capacity_upper * 0.85); + + // A lower network estimate does not decrease the estimate immediately, + // but the estimate is not allowed to increase. + network_estimate.link_capacity_upper = DataRate::KilobitsPerSec(100); + network_estimate.link_capacity_lower = DataRate::KilobitsPerSec(80); + states.aimd_rate_control->SetNetworkStateEstimate(network_estimate); + for (int i = 0; i < 10; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + EXPECT_EQ(states.aimd_rate_control->LatestEstimate(), + estimate_after_increase); + } + + // If the detector detects and overuse, BWE drops to a value relative the + // network estimate. + UpdateRateControl(states, BandwidthUsage::kBwOverusing, + kEstimatedThroughputBps, + states.simulated_clock->TimeInMilliseconds()); + EXPECT_LT(states.aimd_rate_control->LatestEstimate(), + network_estimate.link_capacity_lower); + EXPECT_GT(states.aimd_rate_control->LatestEstimate().bps(), + kEstimatedThroughputBps); +} + +TEST(AimdRateControlTest, EstimateDoesNotIncreaseInAlrIfNetworkEstimateNotSet) { + // When alr is detected, the delay based estimator is not allowed to increase + // bwe since there will be no feedback from the network if the new estimate + // is correct. + test::ScopedFieldTrials override_field_trials( + "WebRTC-Bwe-EstimateBoundedIncrease/ratio:0.85,ignore_acked:true/" + "WebRTC-DontIncreaseDelayBasedBweInAlr/Enabled/"); + auto states = CreateAimdRateControlStates(/*send_side=*/true); + constexpr int kInitialBitrateBps = 123000; + SetEstimate(states, kInitialBitrateBps); + states.aimd_rate_control->SetInApplicationLimitedRegion(true); + UpdateRateControl(states, BandwidthUsage::kBwNormal, kInitialBitrateBps, + states.simulated_clock->TimeInMilliseconds()); + ASSERT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); + + for (int i = 0; i < 100; ++i) { + UpdateRateControl(states, BandwidthUsage::kBwNormal, absl::nullopt, + states.simulated_clock->TimeInMilliseconds()); + states.simulated_clock->AdvanceTimeMilliseconds(100); + } + EXPECT_EQ(states.aimd_rate_control->LatestEstimate().bps(), + kInitialBitrateBps); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/bwe_defines.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/bwe_defines.cc new file mode 100644 index 0000000000..db92f46717 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/bwe_defines.cc @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016 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 "modules/remote_bitrate_estimator/include/bwe_defines.h" + +namespace webrtc { + +const char kBweTypeHistogram[] = "WebRTC.BWE.Types"; + +RateControlInput::RateControlInput( + BandwidthUsage bw_state, + const absl::optional<DataRate>& estimated_throughput) + : bw_state(bw_state), estimated_throughput(estimated_throughput) {} + +RateControlInput::~RateControlInput() = default; + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/include/bwe_defines.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/include/bwe_defines.h new file mode 100644 index 0000000000..d3dd96be75 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/include/bwe_defines.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_ + +#include <stdint.h> + +#include "absl/types/optional.h" +#include "api/network_state_predictor.h" +#include "api/units/data_rate.h" + +namespace webrtc { + +constexpr DataRate kCongestionControllerMinBitrate = DataRate::BitsPerSec(5000); + +static const int64_t kBitrateWindowMs = 1000; + +extern const char kBweTypeHistogram[]; + +enum BweNames { + kReceiverNoExtension = 0, + kReceiverTOffset = 1, + kReceiverAbsSendTime = 2, + kSendSideTransportSeqNum = 3, + kBweNamesMax = 4 +}; + +struct RateControlInput { + RateControlInput(BandwidthUsage bw_state, + const absl::optional<DataRate>& estimated_throughput); + ~RateControlInput(); + + BandwidthUsage bw_state; + absl::optional<DataRate> estimated_throughput; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_BWE_DEFINES_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h new file mode 100644 index 0000000000..0d4e15e9e1 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +// This class estimates the incoming available bandwidth. + +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_ + +#include <map> +#include <memory> +#include <vector> + +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "modules/include/module_common_types.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/rtcp_packet.h" + +namespace webrtc { + +class Clock; + +// RemoteBitrateObserver is used to signal changes in bitrate estimates for +// the incoming streams. +class RemoteBitrateObserver { + public: + // Called when a receive channel group has a new bitrate estimate for the + // incoming streams. + virtual void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs, + uint32_t bitrate) = 0; + + virtual ~RemoteBitrateObserver() {} +}; + +class RemoteBitrateEstimator : public CallStatsObserver { + public: + ~RemoteBitrateEstimator() override {} + + // Called for each incoming packet. 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. + // Note that `arrival_time_ms` can be of an arbitrary time base. + virtual void IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) = 0; + + // Removes all data for `ssrc`. + virtual void RemoveStream(uint32_t ssrc) = 0; + + // Returns latest estimate or DataRate::Zero() if estimation is unavailable. + virtual DataRate LatestEstimate() const = 0; + + virtual TimeDelta Process() = 0; + + protected: + static const int64_t kProcessIntervalMs = 500; + static const int64_t kStreamTimeOutMs = 2000; +}; + +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INCLUDE_REMOTE_BITRATE_ESTIMATOR_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.cc new file mode 100644 index 0000000000..41c092bf91 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.cc @@ -0,0 +1,163 @@ +/* + * 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 "modules/remote_bitrate_estimator/inter_arrival.h" + +#include "modules/include/module_common_types_public.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +static const int kBurstDeltaThresholdMs = 5; +static const int kMaxBurstDurationMs = 100; + +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), + num_consecutive_reordered_packets_(0) {} + +bool InterArrival::ComputeDeltas(uint32_t timestamp, + int64_t arrival_time_ms, + int64_t system_time_ms, + size_t packet_size, + uint32_t* timestamp_delta, + int64_t* arrival_time_delta_ms, + int* packet_size_delta) { + RTC_DCHECK(timestamp_delta); + RTC_DCHECK(arrival_time_delta_ms); + RTC_DCHECK(packet_size_delta); + 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; + current_timestamp_group_.first_arrival_ms = arrival_time_ms; + } 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; + // Check system time differences to see if we have an unproportional jump + // in arrival time. In that case reset the inter-arrival computations. + int64_t system_time_delta_ms = + current_timestamp_group_.last_system_time_ms - + prev_timestamp_group_.last_system_time_ms; + if (*arrival_time_delta_ms - system_time_delta_ms >= + kArrivalTimeOffsetThresholdMs) { + RTC_LOG(LS_WARNING) + << "The arrival time clock offset has changed (diff = " + << *arrival_time_delta_ms - system_time_delta_ms + << " ms), resetting."; + Reset(); + return false; + } + if (*arrival_time_delta_ms < 0) { + // The group of packets has been reordered since receiving its local + // arrival timestamp. + ++num_consecutive_reordered_packets_; + if (num_consecutive_reordered_packets_ >= kReorderedResetThreshold) { + RTC_LOG(LS_WARNING) + << "Packets are being reordered on the path from the " + "socket to the bandwidth estimator. Ignoring this " + "packet for bandwidth estimation, resetting."; + Reset(); + } + return false; + } else { + num_consecutive_reordered_packets_ = 0; + } + RTC_DCHECK_GE(*arrival_time_delta_ms, 0); + *packet_size_delta = static_cast<int>(current_timestamp_group_.size) - + static_cast<int>(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_.first_arrival_ms = arrival_time_ms; + 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; + current_timestamp_group_.last_system_time_ms = system_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; + } + RTC_DCHECK_GE(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; + if (propagation_delta_ms < 0 && + arrival_time_delta_ms <= kBurstDeltaThresholdMs && + arrival_time_ms - current_timestamp_group_.first_arrival_ms < + kMaxBurstDurationMs) + return true; + return false; +} + +void InterArrival::Reset() { + num_consecutive_reordered_packets_ = 0; + current_timestamp_group_ = TimestampGroup(); + prev_timestamp_group_ = TimestampGroup(); +} +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.h new file mode 100644 index 0000000000..d31a8b63c3 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.h @@ -0,0 +1,95 @@ +/* + * 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 MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ + +#include <stddef.h> +#include <stdint.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: + // After this many packet groups received out of order InterArrival will + // reset, assuming that clocks have made a jump. + static constexpr int kReorderedResetThreshold = 3; + static constexpr int64_t kArrivalTimeOffsetThresholdMs = 3000; + + // 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); + + InterArrival() = delete; + InterArrival(const InterArrival&) = delete; + InterArrival& operator=(const InterArrival&) = delete; + + // 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, + int64_t system_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), + first_arrival_ms(-1), + complete_time_ms(-1) {} + + bool IsFirstPacket() const { return complete_time_ms == -1; } + + size_t size; + uint32_t first_timestamp; + uint32_t timestamp; + int64_t first_arrival_ms; + int64_t complete_time_ms; + int64_t last_system_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; + + void Reset(); + + const uint32_t kTimestampGroupLengthTicks; + TimestampGroup current_timestamp_group_; + TimestampGroup prev_timestamp_group_; + double timestamp_to_ms_coeff_; + bool burst_grouping_; + int num_consecutive_reordered_packets_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_INTER_ARRIVAL_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival_unittest.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival_unittest.cc new file mode 100644 index 0000000000..72a772ed21 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival_unittest.cc @@ -0,0 +1,531 @@ +/* + * 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 "modules/remote_bitrate_estimator/inter_arrival.h" + +#include <memory> + +#include "test/gtest.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<double>(1 << kInterArrivalShift); + +class InterArrivalTest : public ::testing::Test { + protected: + virtual void SetUp() { + inter_arrival_.reset( + new InterArrival(kTimestampGroupLengthUs / 1000, 1.0, true)); + 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); + } + + std::unique_ptr<InterArrival> inter_arrival_; + + private: + static uint32_t MakeRtpTimestamp(int64_t us) { + return static_cast<uint32_t>(static_cast<uint64_t>(us * 90 + 500) / 1000); + } + + static uint32_t MakeAbsSendTime(int64_t us) { + uint32_t absolute_send_time = + static_cast<uint32_t>(((static_cast<uint64_t>(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, 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, 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); + } + + std::unique_ptr<InterArrival> inter_arrival_rtp_; + std::unique_ptr<InterArrival> 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); +} + +TEST_F(InterArrivalTest, PositiveArrivalTimeJump) { + const size_t kPacketSize = 1000; + uint32_t send_time_ms = 10000; + int64_t arrival_time_ms = 20000; + int64_t system_time_ms = 30000; + + uint32_t send_delta; + int64_t arrival_delta; + int size_delta; + EXPECT_FALSE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + + const int kTimeDeltaMs = 30; + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + EXPECT_FALSE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs + InterArrival::kArrivalTimeOffsetThresholdMs; + system_time_ms += kTimeDeltaMs; + EXPECT_TRUE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); + EXPECT_EQ(kTimeDeltaMs, arrival_delta); + EXPECT_EQ(size_delta, 0); + + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + // The previous arrival time jump should now be detected and cause a reset. + EXPECT_FALSE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + + // The two next packets will not give a valid delta since we're in the initial + // state. + for (int i = 0; i < 2; ++i) { + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + EXPECT_FALSE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + } + + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + EXPECT_TRUE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); + EXPECT_EQ(kTimeDeltaMs, arrival_delta); + EXPECT_EQ(size_delta, 0); +} + +TEST_F(InterArrivalTest, NegativeArrivalTimeJump) { + const size_t kPacketSize = 1000; + uint32_t send_time_ms = 10000; + int64_t arrival_time_ms = 20000; + int64_t system_time_ms = 30000; + + uint32_t send_delta; + int64_t arrival_delta; + int size_delta; + EXPECT_FALSE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + + const int kTimeDeltaMs = 30; + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + EXPECT_FALSE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + EXPECT_TRUE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); + EXPECT_EQ(kTimeDeltaMs, arrival_delta); + EXPECT_EQ(size_delta, 0); + + // Three out of order will fail, after that we will be reset and two more will + // fail before we get our first valid delta after the reset. + arrival_time_ms -= 1000; + for (int i = 0; i < InterArrival::kReorderedResetThreshold + 3; ++i) { + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + // The previous arrival time jump should now be detected and cause a reset. + EXPECT_FALSE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + } + + send_time_ms += kTimeDeltaMs; + arrival_time_ms += kTimeDeltaMs; + system_time_ms += kTimeDeltaMs; + EXPECT_TRUE(inter_arrival_->ComputeDeltas( + send_time_ms, arrival_time_ms, system_time_ms, kPacketSize, &send_delta, + &arrival_delta, &size_delta)); + EXPECT_EQ(kTimeDeltaMs, static_cast<int>(send_delta)); + EXPECT_EQ(kTimeDeltaMs, arrival_delta); + EXPECT_EQ(size_delta, 0); +} +} // namespace testing +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.cc new file mode 100644 index 0000000000..bd2d756876 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.cc @@ -0,0 +1,111 @@ +/* + * 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 "modules/remote_bitrate_estimator/overuse_detector.h" + +#include <math.h> +#include <stdio.h> + +#include <algorithm> +#include <string> + +#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h" +#include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_minmax.h" + +namespace webrtc { + +const double kMaxAdaptOffsetMs = 15.0; +const double kOverUsingTimeThreshold = 10; +const int kMaxNumDeltas = 60; + +OveruseDetector::OveruseDetector(const FieldTrialsView* key_value_config) + // Experiment is on by default, but can be disabled with finch by setting + // the field trial string to "WebRTC-AdaptiveBweThreshold/Disabled/". + : k_up_(0.0087), + k_down_(0.039), + overusing_time_threshold_(kOverUsingTimeThreshold), + threshold_(12.5), + last_update_ms_(-1), + prev_offset_(0.0), + time_over_using_(-1), + overuse_counter_(0), + hypothesis_(BandwidthUsage::kBwNormal) {} + +OveruseDetector::~OveruseDetector() {} + +BandwidthUsage OveruseDetector::State() const { + return hypothesis_; +} + +BandwidthUsage OveruseDetector::Detect(double offset, + double ts_delta, + int num_of_deltas, + int64_t now_ms) { + if (num_of_deltas < 2) { + return BandwidthUsage::kBwNormal; + } + const double T = std::min(num_of_deltas, kMaxNumDeltas) * offset; + BWE_TEST_LOGGING_PLOT(1, "T", now_ms, T); + BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_); + 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 { + // Increment timer + time_over_using_ += ts_delta; + } + overuse_counter_++; + if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) { + if (offset >= prev_offset_) { + time_over_using_ = 0; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwOverusing; + } + } + } else if (T < -threshold_) { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwUnderusing; + } else { + time_over_using_ = -1; + overuse_counter_ = 0; + hypothesis_ = BandwidthUsage::kBwNormal; + } + prev_offset_ = offset; + + UpdateThreshold(T, now_ms); + + return hypothesis_; +} + +void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) { + if (last_update_ms_ == -1) + last_update_ms_ = now_ms; + + if (fabs(modified_offset) > threshold_ + kMaxAdaptOffsetMs) { + // Avoid adapting the threshold to big latency spikes, caused e.g., + // by a sudden capacity drop. + last_update_ms_ = now_ms; + return; + } + + const double k = fabs(modified_offset) < threshold_ ? k_down_ : k_up_; + const int64_t kMaxTimeDeltaMs = 100; + int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs); + threshold_ += k * (fabs(modified_offset) - threshold_) * time_delta_ms; + threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f); + last_update_ms_ = now_ms; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.h new file mode 100644 index 0000000000..07ae8734c4 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.h @@ -0,0 +1,58 @@ +/* + * 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. + */ +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ + +#include <stdint.h> + +#include "api/field_trials_view.h" +#include "api/network_state_predictor.h" + +namespace webrtc { + +class OveruseDetector { + public: + explicit OveruseDetector(const FieldTrialsView* key_value_config); + virtual ~OveruseDetector(); + + OveruseDetector(const OveruseDetector&) = delete; + OveruseDetector& operator=(const OveruseDetector&) = delete; + + // 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, + int64_t now_ms); + + // Returns the current detector state. + BandwidthUsage State() const; + + private: + void UpdateThreshold(double modified_offset, int64_t now_ms); + void InitializeExperiment(const FieldTrialsView& key_value_config); + + const double k_up_; + const double k_down_; + const double overusing_time_threshold_; + double threshold_; + int64_t last_update_ms_; + double prev_offset_; + double time_over_using_; + int overuse_counter_; + BandwidthUsage hypothesis_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_DETECTOR_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc new file mode 100644 index 0000000000..e91d4f0d22 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc @@ -0,0 +1,682 @@ +/* + * 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 "modules/remote_bitrate_estimator/overuse_detector.h" + +#include <stdio.h> +#include <string.h> + +#include <algorithm> +#include <cstdlib> +#include <memory> + +#include "api/transport/field_trial_based_config.h" +#include "modules/remote_bitrate_estimator/inter_arrival.h" +#include "modules/remote_bitrate_estimator/overuse_estimator.h" +#include "rtc_base/random.h" +#include "test/gtest.h" + +namespace webrtc { +namespace testing { + +const double kRtpTimestampToMs = 1.0 / 90.0; + +class OveruseDetectorTest : public ::testing::Test { + public: + OveruseDetectorTest() + : now_ms_(0), + receive_time_ms_(0), + rtp_timestamp_(10 * 90), + overuse_detector_(), + overuse_estimator_(new OveruseEstimator(options_)), + inter_arrival_(new InterArrival(5 * 90, kRtpTimestampToMs, true)), + random_(123456789) {} + + protected: + void SetUp() override { + overuse_detector_.reset(new OveruseDetector(&field_trials_)); + } + + 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<int64_t>( + receive_time_ms_, + now_ms_ + static_cast<int64_t>( + random_.Gaussian(0, standard_deviation_ms) + 0.5)); + if (BandwidthUsage::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<int64_t>( + receive_time_ms_, + now_ms_ + static_cast<int64_t>( + random_.Gaussian(0, standard_deviation_ms) + 0.5)); + if (BandwidthUsage::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, 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(), receive_time_ms); + overuse_detector_->Detect( + overuse_estimator_->offset(), timestamp_delta_ms, + overuse_estimator_->num_of_deltas(), receive_time_ms); + } + } + + const FieldTrialBasedConfig field_trials_; + int64_t now_ms_; + int64_t receive_time_ms_; + uint32_t rtp_timestamp_; + OverUseDetectorOptions options_; + std::unique_ptr<OveruseDetector> overuse_detector_; + std::unique_ptr<OveruseEstimator> overuse_estimator_; + std::unique_ptr<InterArrival> inter_arrival_; + Random random_; +}; + +TEST_F(OveruseDetectorTest, GaussianRandom) { + int buckets[100]; + memset(buckets, 0, sizeof(buckets)); + for (int i = 0; i < 100000; ++i) { + int index = random_.Gaussian(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(BandwidthUsage::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(BandwidthUsage::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(BandwidthUsage::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(7, 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(7, frames_until_overuse); +} + +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 = random_.Rand(0, 1); + now_ms_ += frame_duration_ms - offset; + } else { + now_ms_ += frame_duration_ms + offset; + } + EXPECT_EQ(BandwidthUsage::kBwNormal, overuse_detector_->State()); + } + // Simulate a higher send pace, that is too high. + // Total build up of 30 ms. + for (int j = 0; j < 3; ++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(BandwidthUsage::kBwNormal, overuse_detector_->State()); + } + UpdateDetector(rtp_timestamp, now_ms_, packet_size); + EXPECT_EQ(BandwidthUsage::kBwOverusing, overuse_detector_->State()); +} + +TEST_F(OveruseDetectorTest, 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_EQ(20, frames_until_overuse); +} + +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_EQ(4, frames_until_overuse); +} + +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_EQ(44, frames_until_overuse); +} + +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_EQ(4, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, 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_EQ(20, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, 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_EQ(44, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, 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_EQ(20, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, 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_EQ(44, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, 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_EQ(19, frames_until_overuse); +} + +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_EQ(5, frames_until_overuse); +} + +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_EQ(44, frames_until_overuse); +} + +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_EQ(10, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, 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_EQ(19, frames_until_overuse); +} + +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_EQ(5, frames_until_overuse); +} + +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_EQ(44, frames_until_overuse); +} + +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_EQ(10, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, 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_EQ(19, frames_until_overuse); +} + +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_EQ(5, frames_until_overuse); +} + +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_EQ(44, frames_until_overuse); +} + +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_EQ(10, frames_until_overuse); +} + +TEST_F(OveruseDetectorTest, ThresholdAdapts) { + const double kOffset = 0.21; + double kTsDelta = 3000.0; + int64_t now_ms = 0; + int num_deltas = 60; + const int kBatchLength = 10; + + // Pass in a positive offset and verify it triggers overuse. + bool overuse_detected = false; + for (int i = 0; i < kBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(kOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + EXPECT_TRUE(overuse_detected); + + // Force the threshold to increase by passing in a higher offset. + overuse_detected = false; + for (int i = 0; i < kBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(1.1 * kOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + EXPECT_TRUE(overuse_detected); + + // Verify that the same offset as before no longer triggers overuse. + overuse_detected = false; + for (int i = 0; i < kBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(kOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + EXPECT_FALSE(overuse_detected); + + // Pass in a low offset to make the threshold adapt down. + for (int i = 0; i < 15 * kBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(0.7 * kOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + EXPECT_FALSE(overuse_detected); + + // Make sure the original offset now again triggers overuse. + for (int i = 0; i < kBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(kOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + EXPECT_TRUE(overuse_detected); +} + +TEST_F(OveruseDetectorTest, DoesntAdaptToSpikes) { + const double kOffset = 1.0; + const double kLargeOffset = 20.0; + double kTsDelta = 3000.0; + int64_t now_ms = 0; + int num_deltas = 60; + const int kBatchLength = 10; + const int kShortBatchLength = 3; + + // Pass in a positive offset and verify it triggers overuse. + bool overuse_detected = false; + for (int i = 0; i < kBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(kOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + + // Pass in a large offset. This shouldn't have a too big impact on the + // threshold, but still trigger an overuse. + now_ms += 100; + overuse_detected = false; + for (int i = 0; i < kShortBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(kLargeOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + EXPECT_TRUE(overuse_detected); + + // Pass in a positive normal offset and verify it still triggers. + overuse_detected = false; + for (int i = 0; i < kBatchLength; ++i) { + BandwidthUsage overuse_state = + overuse_detector_->Detect(kOffset, kTsDelta, num_deltas, now_ms); + if (overuse_state == BandwidthUsage::kBwOverusing) { + overuse_detected = true; + } + ++num_deltas; + now_ms += 5; + } + EXPECT_TRUE(overuse_detected); +} +} // namespace testing +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.cc new file mode 100644 index 0000000000..684143fcce --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.cc @@ -0,0 +1,164 @@ +/* + * 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 "modules/remote_bitrate_estimator/overuse_estimator.h" + +#include <math.h> +#include <string.h> + +#include <algorithm> + +#include "api/network_state_predictor.h" +#include "modules/remote_bitrate_estimator/test/bwe_test_logging.h" +#include "rtc_base/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, + int64_t now_ms) { + const double min_frame_period = UpdateMinFramePeriod(ts_delta); + const double t_ts_delta = t_delta - ts_delta; + BWE_TEST_LOGGING_PLOT(1, "dm_ms", now_ms, t_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 == BandwidthUsage::kBwOverusing && + offset_ < prev_offset_) || + (current_hypothesis == BandwidthUsage::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]}; + + BWE_TEST_LOGGING_PLOT(1, "d_ms", now_ms, slope_ * h[0] - offset_); + + const double residual = t_ts_delta - slope_ * h[0] - offset_; + + const bool in_stable_state = + (current_hypothesis == BandwidthUsage::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; + RTC_DCHECK(positive_semi_definite); + if (!positive_semi_definite) { + RTC_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; + + BWE_TEST_LOGGING_PLOT(1, "kc", now_ms, K[0]); + BWE_TEST_LOGGING_PLOT(1, "km", now_ms, K[1]); + BWE_TEST_LOGGING_PLOT(1, "slope_1/bps", now_ms, slope_); + BWE_TEST_LOGGING_PLOT(1, "var_noise", now_ms, var_noise_); +} + +double OveruseEstimator::UpdateMinFramePeriod(double ts_delta) { + double min_frame_period = ts_delta; + if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) { + ts_delta_hist_.pop_front(); + } + for (const double old_ts_delta : ts_delta_hist_) { + min_frame_period = std::min(old_ts_delta, 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_ < 1) { + var_noise_ = 1; + } +} +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.h new file mode 100644 index 0000000000..c021f00da7 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.h @@ -0,0 +1,83 @@ +/* + * 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 MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_ + +#include <stdint.h> + +#include <deque> + +#include "api/network_state_predictor.h" + +namespace webrtc { + +// Bandwidth over-use detector options. These are used to drive +// experimentation with bandwidth estimation parameters. +// TODO(terelius): This is only used in overuse_estimator.cc, and only in the +// default constructed state. Can we move the relevant variables into that +// class and delete this? +struct OverUseDetectorOptions { + OverUseDetectorOptions() = default; + double initial_slope = 8.0 / 512.0; + double initial_offset = 0; + double initial_e[2][2] = {{100.0, 0.0}, {0.0, 1e-1}}; + double initial_process_noise[2] = {1e-13, 1e-3}; + double initial_avg_noise = 0.0; + double initial_var_noise = 50.0; +}; + +class OveruseEstimator { + public: + explicit OveruseEstimator(const OverUseDetectorOptions& options); + ~OveruseEstimator(); + + OveruseEstimator(const OveruseEstimator&) = delete; + OveruseEstimator& operator=(const OveruseEstimator&) = delete; + + // 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, + int64_t now_ms); + + // 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::deque<double> ts_delta_hist_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_OVERUSE_ESTIMATOR_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.cc new file mode 100644 index 0000000000..182297d303 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.cc @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021 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 "modules/remote_bitrate_estimator/packet_arrival_map.h" + +#include <algorithm> +#include <cstdint> + +#include "api/units/timestamp.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +void PacketArrivalTimeMap::AddPacket(int64_t sequence_number, + Timestamp arrival_time) { + RTC_DCHECK_GE(arrival_time, Timestamp::Zero()); + if (!has_seen_packet()) { + // First packet. + Reallocate(kMinCapacity); + begin_sequence_number_ = sequence_number; + end_sequence_number_ = sequence_number + 1; + arrival_times_[Index(sequence_number)] = arrival_time; + return; + } + + if (sequence_number >= begin_sequence_number() && + sequence_number < end_sequence_number()) { + // The packet is within the buffer - no need to expand it. + arrival_times_[Index(sequence_number)] = arrival_time; + return; + } + + if (sequence_number < begin_sequence_number()) { + // The packet goes before the current buffer. Expand to add packet, but only + // if it fits within kMaxNumberOfPackets. + int64_t new_size = end_sequence_number() - sequence_number; + if (new_size > kMaxNumberOfPackets) { + // Don't expand the buffer further, as that would remove newly received + // packets. + return; + } + AdjustToSize(new_size); + + arrival_times_[Index(sequence_number)] = arrival_time; + SetNotReceived(sequence_number + 1, begin_sequence_number_); + begin_sequence_number_ = sequence_number; + return; + } + + // The packet goes after the buffer. + RTC_DCHECK_GE(sequence_number, end_sequence_number_); + int64_t new_end_sequence_number = sequence_number + 1; + + if (new_end_sequence_number >= end_sequence_number_ + kMaxNumberOfPackets) { + // All old packets have to be removed. + begin_sequence_number_ = sequence_number; + end_sequence_number_ = new_end_sequence_number; + arrival_times_[Index(sequence_number)] = arrival_time; + return; + } + + if (begin_sequence_number_ < new_end_sequence_number - kMaxNumberOfPackets) { + // Remove oldest entries + begin_sequence_number_ = new_end_sequence_number - kMaxNumberOfPackets; + RTC_DCHECK_GT(end_sequence_number_, begin_sequence_number_); + } + + AdjustToSize(new_end_sequence_number - begin_sequence_number_); + + // Packets can be received out-of-order. If this isn't the next expected + // packet, add enough placeholders to fill the gap. + SetNotReceived(end_sequence_number_, sequence_number); + end_sequence_number_ = new_end_sequence_number; + arrival_times_[Index(sequence_number)] = arrival_time; +} + +void PacketArrivalTimeMap::SetNotReceived( + int64_t begin_sequence_number_inclusive, + int64_t end_sequence_number_exclusive) { + static constexpr Timestamp value = Timestamp::MinusInfinity(); + + int begin_index = Index(begin_sequence_number_inclusive); + int end_index = Index(end_sequence_number_exclusive); + + if (begin_index <= end_index) { + // Entries to clear are in single block: + // [......{-----}....] + std::fill(arrival_times_.get() + begin_index, + arrival_times_.get() + end_index, value); + } else { + // Entries to clear span across arrival_times_ border: + // [--}..........{---] + std::fill(arrival_times_.get() + begin_index, + arrival_times_.get() + capacity(), value); + std::fill(arrival_times_.get(), arrival_times_.get() + end_index, value); + } +} + +void PacketArrivalTimeMap::RemoveOldPackets(int64_t sequence_number, + Timestamp arrival_time_limit) { + int64_t check_to = std::min(sequence_number, end_sequence_number_); + while (begin_sequence_number_ < check_to && + arrival_times_[Index(begin_sequence_number_)] <= arrival_time_limit) { + ++begin_sequence_number_; + } + AdjustToSize(end_sequence_number_ - begin_sequence_number_); +} + +void PacketArrivalTimeMap::EraseTo(int64_t sequence_number) { + if (sequence_number < begin_sequence_number_) { + return; + } + if (sequence_number >= end_sequence_number_) { + // Erase all. + begin_sequence_number_ = end_sequence_number_; + return; + } + // Remove some. + begin_sequence_number_ = sequence_number; + AdjustToSize(end_sequence_number_ - begin_sequence_number_); +} + +void PacketArrivalTimeMap::AdjustToSize(int new_size) { + if (new_size > capacity()) { + int new_capacity = capacity(); + while (new_capacity < new_size) + new_capacity *= 2; + Reallocate(new_capacity); + } + if (capacity() > std::max(kMinCapacity, 4 * new_size)) { + int new_capacity = capacity(); + while (new_capacity > 2 * std::max(new_size, kMinCapacity)) { + new_capacity /= 2; + } + Reallocate(new_capacity); + } + RTC_DCHECK_LE(new_size, capacity()); +} + +void PacketArrivalTimeMap::Reallocate(int new_capacity) { + int new_capacity_minus_1 = new_capacity - 1; + // Check capacity is a power of 2. + RTC_DCHECK_EQ(new_capacity & new_capacity_minus_1, 0); + // Create uninitialized memory. + // All valid entries should be set by `AddPacket` before use. + void* raw = operator new[](new_capacity * sizeof(Timestamp)); + Timestamp* new_buffer = static_cast<Timestamp*>(raw); + + for (int64_t sequence_number = begin_sequence_number_; + sequence_number < end_sequence_number_; ++sequence_number) { + new_buffer[sequence_number & new_capacity_minus_1] = + arrival_times_[sequence_number & capacity_minus_1_]; + } + arrival_times_.reset(new_buffer); + capacity_minus_1_ = new_capacity_minus_1; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.h new file mode 100644 index 0000000000..4ae47172bc --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2021 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 MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_ + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <memory> + +#include "api/units/timestamp.h" +#include "rtc_base/checks.h" + +namespace webrtc { + +// PacketArrivalTimeMap is an optimized map of packet sequence number to arrival +// time, limited in size to never exceed `kMaxNumberOfPackets`. It will grow as +// needed, and remove old packets, and will expand to allow earlier packets to +// be added (out-of-order). +// +// Not yet received packets have the arrival time zero. The queue will not span +// larger than necessary and the last packet should always be received. The +// first packet in the queue doesn't have to be received in case of receiving +// packets out-of-order. +class PacketArrivalTimeMap { + public: + struct PacketArrivalTime { + Timestamp arrival_time; + int64_t sequence_number; + }; + // Impossible to request feedback older than what can be represented by 15 + // bits. + static constexpr int kMaxNumberOfPackets = (1 << 15); + + PacketArrivalTimeMap() = default; + PacketArrivalTimeMap(const PacketArrivalTimeMap&) = delete; + PacketArrivalTimeMap& operator=(const PacketArrivalTimeMap&) = delete; + ~PacketArrivalTimeMap() = default; + + // Indicates if the packet with `sequence_number` has already been received. + bool has_received(int64_t sequence_number) const { + return sequence_number >= begin_sequence_number() && + sequence_number < end_sequence_number() && + arrival_times_[Index(sequence_number)] >= Timestamp::Zero(); + } + + // Returns the sequence number of the first entry in the map, i.e. the + // sequence number that a `begin()` iterator would represent. + int64_t begin_sequence_number() const { return begin_sequence_number_; } + + // Returns the sequence number of the element just after the map, i.e. the + // sequence number that an `end()` iterator would represent. + int64_t end_sequence_number() const { return end_sequence_number_; } + + // Returns an element by `sequence_number`, which must be valid, i.e. + // between [begin_sequence_number, end_sequence_number). + Timestamp get(int64_t sequence_number) { + RTC_DCHECK_GE(sequence_number, begin_sequence_number()); + RTC_DCHECK_LT(sequence_number, end_sequence_number()); + return arrival_times_[Index(sequence_number)]; + } + + // Returns timestamp and sequence number of the received packet with sequence + // number equal or larger than `sequence_number`. `sequence_number` must be in + // range [begin_sequence_number, end_sequence_number). + PacketArrivalTime FindNextAtOrAfter(int64_t sequence_number) const { + RTC_DCHECK_GE(sequence_number, begin_sequence_number()); + RTC_DCHECK_LT(sequence_number, end_sequence_number()); + while (true) { + Timestamp t = arrival_times_[Index(sequence_number)]; + if (t >= Timestamp::Zero()) { + return {.arrival_time = t, .sequence_number = sequence_number}; + } + ++sequence_number; + } + } + + // Clamps `sequence_number` between [begin_sequence_number, + // end_sequence_number]. + int64_t clamp(int64_t sequence_number) const { + return std::clamp(sequence_number, begin_sequence_number(), + end_sequence_number()); + } + + // Erases all elements from the beginning of the map until `sequence_number`. + void EraseTo(int64_t sequence_number); + + // Records the fact that a packet with `sequence_number` arrived at + // `arrival_time_ms`. + void AddPacket(int64_t sequence_number, Timestamp arrival_time); + + // Removes packets from the beginning of the map as long as they are received + // before `sequence_number` and with an age older than `arrival_time_limit` + void RemoveOldPackets(int64_t sequence_number, Timestamp arrival_time_limit); + + private: + static constexpr int kMinCapacity = 128; + + // Returns index in the `arrival_times_` for value for `sequence_number`. + int Index(int64_t sequence_number) const { + // Note that sequence_number might be negative, thus taking '%' requires + // extra handling and can be slow. Because capacity is a power of two, it + // is much faster to use '&' operator. + return sequence_number & capacity_minus_1_; + } + + void SetNotReceived(int64_t begin_sequence_number_inclusive, + int64_t end_sequence_number_exclusive); + + // Adjust capacity to match new_size, may reduce capacity. + // On return guarantees capacity >= new_size. + void AdjustToSize(int new_size); + void Reallocate(int new_capacity); + + int capacity() const { return capacity_minus_1_ + 1; } + bool has_seen_packet() const { return arrival_times_ != nullptr; } + + // Circular buffer. Packet with sequence number `sequence_number` + // is stored in the slot `sequence_number % capacity_` + std::unique_ptr<Timestamp[]> arrival_times_ = nullptr; + + // Allocated size of the `arrival_times_` + // capacity_ is a power of 2 in range [kMinCapacity, kMaxNumberOfPackets] + // `capacity - 1` is used much more often than `capacity`, thus that value is + // stored. + int capacity_minus_1_ = -1; + + // The unwrapped sequence number for valid range of sequence numbers. + // arrival_times_ entries only valid for sequence numbers in range + // `begin_sequence_number_ <= sequence_number < end_sequence_number_` + int64_t begin_sequence_number_ = 0; + int64_t end_sequence_number_ = 0; +}; + +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_PACKET_ARRIVAL_MAP_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map_test.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map_test.cc new file mode 100644 index 0000000000..d86f0397e7 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map_test.cc @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2021 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 "modules/remote_bitrate_estimator/packet_arrival_map.h" + +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +TEST(PacketArrivalMapTest, IsConsistentWhenEmpty) { + PacketArrivalTimeMap map; + + EXPECT_EQ(map.begin_sequence_number(), map.end_sequence_number()); + EXPECT_FALSE(map.has_received(0)); + EXPECT_EQ(map.clamp(-5), 0); + EXPECT_EQ(map.clamp(5), 0); +} + +TEST(PacketArrivalMapTest, InsertsFirstItemIntoMap) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Millis(10)); + EXPECT_EQ(map.begin_sequence_number(), 42); + EXPECT_EQ(map.end_sequence_number(), 43); + + EXPECT_FALSE(map.has_received(41)); + EXPECT_TRUE(map.has_received(42)); + EXPECT_FALSE(map.has_received(44)); + + EXPECT_EQ(map.clamp(-100), 42); + EXPECT_EQ(map.clamp(42), 42); + EXPECT_EQ(map.clamp(100), 43); +} + +TEST(PacketArrivalMapTest, InsertsWithGaps) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Zero()); + map.AddPacket(45, Timestamp::Millis(11)); + EXPECT_EQ(map.begin_sequence_number(), 42); + EXPECT_EQ(map.end_sequence_number(), 46); + + EXPECT_FALSE(map.has_received(41)); + EXPECT_TRUE(map.has_received(42)); + EXPECT_FALSE(map.has_received(43)); + EXPECT_FALSE(map.has_received(44)); + EXPECT_TRUE(map.has_received(45)); + EXPECT_FALSE(map.has_received(46)); + + EXPECT_EQ(map.get(42), Timestamp::Zero()); + EXPECT_LT(map.get(43), Timestamp::Zero()); + EXPECT_LT(map.get(44), Timestamp::Zero()); + EXPECT_EQ(map.get(45), Timestamp::Millis(11)); + + EXPECT_EQ(map.clamp(-100), 42); + EXPECT_EQ(map.clamp(44), 44); + EXPECT_EQ(map.clamp(100), 46); +} + +TEST(PacketArrivalMapTest, FindNextAtOrAfterWithGaps) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Zero()); + map.AddPacket(45, Timestamp::Millis(11)); + EXPECT_EQ(map.begin_sequence_number(), 42); + EXPECT_EQ(map.end_sequence_number(), 46); + + PacketArrivalTimeMap::PacketArrivalTime packet = map.FindNextAtOrAfter(42); + EXPECT_EQ(packet.arrival_time, Timestamp::Zero()); + EXPECT_EQ(packet.sequence_number, 42); + + packet = map.FindNextAtOrAfter(43); + EXPECT_EQ(packet.arrival_time, Timestamp::Millis(11)); + EXPECT_EQ(packet.sequence_number, 45); +} + +TEST(PacketArrivalMapTest, InsertsWithinBuffer) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Millis(10)); + map.AddPacket(45, Timestamp::Millis(11)); + + map.AddPacket(43, Timestamp::Millis(12)); + map.AddPacket(44, Timestamp::Millis(13)); + + EXPECT_EQ(map.begin_sequence_number(), 42); + EXPECT_EQ(map.end_sequence_number(), 46); + + EXPECT_FALSE(map.has_received(41)); + EXPECT_TRUE(map.has_received(42)); + EXPECT_TRUE(map.has_received(43)); + EXPECT_TRUE(map.has_received(44)); + EXPECT_TRUE(map.has_received(45)); + EXPECT_FALSE(map.has_received(46)); + + EXPECT_EQ(map.get(42), Timestamp::Millis(10)); + EXPECT_EQ(map.get(43), Timestamp::Millis(12)); + EXPECT_EQ(map.get(44), Timestamp::Millis(13)); + EXPECT_EQ(map.get(45), Timestamp::Millis(11)); +} + +TEST(PacketArrivalMapTest, GrowsBufferAndRemoveOld) { + PacketArrivalTimeMap map; + + constexpr int64_t kLargeSeq = 42 + PacketArrivalTimeMap::kMaxNumberOfPackets; + map.AddPacket(42, Timestamp::Millis(10)); + map.AddPacket(43, Timestamp::Millis(11)); + map.AddPacket(44, Timestamp::Millis(12)); + map.AddPacket(45, Timestamp::Millis(13)); + map.AddPacket(kLargeSeq, Timestamp::Millis(12)); + + EXPECT_EQ(map.begin_sequence_number(), 43); + EXPECT_EQ(map.end_sequence_number(), kLargeSeq + 1); + EXPECT_EQ(map.end_sequence_number() - map.begin_sequence_number(), + PacketArrivalTimeMap::kMaxNumberOfPackets); + + EXPECT_FALSE(map.has_received(41)); + EXPECT_FALSE(map.has_received(42)); + EXPECT_TRUE(map.has_received(43)); + EXPECT_TRUE(map.has_received(44)); + EXPECT_TRUE(map.has_received(45)); + EXPECT_FALSE(map.has_received(46)); + EXPECT_TRUE(map.has_received(kLargeSeq)); + EXPECT_FALSE(map.has_received(kLargeSeq + 1)); +} + +TEST(PacketArrivalMapTest, SequenceNumberJumpsDeletesAll) { + PacketArrivalTimeMap map; + + constexpr int64_t kLargeSeq = + 42 + 2 * PacketArrivalTimeMap::kMaxNumberOfPackets; + map.AddPacket(42, Timestamp::Millis(10)); + map.AddPacket(kLargeSeq, Timestamp::Millis(12)); + + EXPECT_EQ(map.begin_sequence_number(), kLargeSeq); + EXPECT_EQ(map.end_sequence_number(), kLargeSeq + 1); + + EXPECT_FALSE(map.has_received(42)); + EXPECT_TRUE(map.has_received(kLargeSeq)); + EXPECT_FALSE(map.has_received(kLargeSeq + 1)); +} + +TEST(PacketArrivalMapTest, ExpandsBeforeBeginning) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Millis(10)); + map.AddPacket(-1000, Timestamp::Millis(13)); + + EXPECT_EQ(map.begin_sequence_number(), -1000); + EXPECT_EQ(map.end_sequence_number(), 43); + + EXPECT_FALSE(map.has_received(-1001)); + EXPECT_TRUE(map.has_received(-1000)); + EXPECT_FALSE(map.has_received(-999)); + EXPECT_TRUE(map.has_received(42)); + EXPECT_FALSE(map.has_received(43)); +} + +TEST(PacketArrivalMapTest, ExpandingBeforeBeginningKeepsReceived) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Millis(10)); + constexpr int64_t kSmallSeq = + static_cast<int64_t>(42) - 2 * PacketArrivalTimeMap::kMaxNumberOfPackets; + map.AddPacket(kSmallSeq, Timestamp::Millis(13)); + + EXPECT_EQ(map.begin_sequence_number(), 42); + EXPECT_EQ(map.end_sequence_number(), 43); +} + +TEST(PacketArrivalMapTest, ErasesToRemoveElements) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Millis(10)); + map.AddPacket(43, Timestamp::Millis(11)); + map.AddPacket(44, Timestamp::Millis(12)); + map.AddPacket(45, Timestamp::Millis(13)); + + map.EraseTo(44); + + EXPECT_EQ(map.begin_sequence_number(), 44); + EXPECT_EQ(map.end_sequence_number(), 46); + + EXPECT_FALSE(map.has_received(43)); + EXPECT_TRUE(map.has_received(44)); + EXPECT_TRUE(map.has_received(45)); + EXPECT_FALSE(map.has_received(46)); +} + +TEST(PacketArrivalMapTest, ErasesInEmptyMap) { + PacketArrivalTimeMap map; + + EXPECT_EQ(map.begin_sequence_number(), map.end_sequence_number()); + + map.EraseTo(map.end_sequence_number()); + EXPECT_EQ(map.begin_sequence_number(), map.end_sequence_number()); +} + +TEST(PacketArrivalMapTest, IsTolerantToWrongArgumentsForErase) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Millis(10)); + map.AddPacket(43, Timestamp::Millis(11)); + + map.EraseTo(1); + + EXPECT_EQ(map.begin_sequence_number(), 42); + EXPECT_EQ(map.end_sequence_number(), 44); + + map.EraseTo(100); + + EXPECT_EQ(map.begin_sequence_number(), 44); + EXPECT_EQ(map.end_sequence_number(), 44); +} + +TEST(PacketArrivalMapTest, EraseAllRemembersBeginningSeqNbr) { + PacketArrivalTimeMap map; + + map.AddPacket(42, Timestamp::Millis(10)); + map.AddPacket(43, Timestamp::Millis(11)); + map.AddPacket(44, Timestamp::Millis(12)); + map.AddPacket(45, Timestamp::Millis(13)); + + map.EraseTo(46); + + map.AddPacket(50, Timestamp::Millis(10)); + + EXPECT_EQ(map.begin_sequence_number(), 46); + EXPECT_EQ(map.end_sequence_number(), 51); + + EXPECT_FALSE(map.has_received(45)); + EXPECT_FALSE(map.has_received(46)); + EXPECT_FALSE(map.has_received(47)); + EXPECT_FALSE(map.has_received(48)); + EXPECT_FALSE(map.has_received(49)); + EXPECT_TRUE(map.has_received(50)); + EXPECT_FALSE(map.has_received(51)); +} + +TEST(PacketArrivalMapTest, EraseToMissingSequenceNumber) { + PacketArrivalTimeMap map; + + map.AddPacket(37, Timestamp::Millis(10)); + map.AddPacket(39, Timestamp::Millis(11)); + map.AddPacket(40, Timestamp::Millis(12)); + map.AddPacket(41, Timestamp::Millis(13)); + + map.EraseTo(38); + + map.AddPacket(42, Timestamp::Millis(40)); + + EXPECT_EQ(map.begin_sequence_number(), 38); + EXPECT_EQ(map.end_sequence_number(), 43); + + EXPECT_FALSE(map.has_received(37)); + EXPECT_FALSE(map.has_received(38)); + EXPECT_TRUE(map.has_received(39)); + EXPECT_TRUE(map.has_received(40)); + EXPECT_TRUE(map.has_received(41)); + EXPECT_TRUE(map.has_received(42)); +} + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc new file mode 100644 index 0000000000..e9fb1b99f6 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc @@ -0,0 +1,399 @@ +/* + * 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 "modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h" + +#include <math.h> + +#include <algorithm> +#include <memory> +#include <utility> + +#include "api/transport/field_trial_based_config.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread_annotations.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { + +constexpr TimeDelta kMinClusterDelta = TimeDelta::Millis(1); +constexpr TimeDelta kInitialProbingInterval = TimeDelta::Seconds(2); +constexpr int kTimestampGroupLengthMs = 5; +constexpr int kAbsSendTimeInterArrivalUpshift = 8; +constexpr int kInterArrivalShift = + RTPHeaderExtension::kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift; +constexpr int kMinClusterSize = 4; +constexpr int kMaxProbePackets = 15; +constexpr int kExpectedNumberOfProbes = 3; +constexpr double kTimestampToMs = + 1000.0 / static_cast<double>(1 << kInterArrivalShift); + +absl::optional<DataRate> OptionalRateFromOptionalBps( + absl::optional<int> bitrate_bps) { + if (bitrate_bps) { + return DataRate::BitsPerSec(*bitrate_bps); + } else { + return absl::nullopt; + } +} + +template <typename K, typename V> +std::vector<K> Keys(const std::map<K, V>& map) { + std::vector<K> keys; + keys.reserve(map.size()); + for (const auto& kv_pair : map) { + keys.push_back(kv_pair.first); + } + return keys; +} + +} // namespace + +RemoteBitrateEstimatorAbsSendTime::~RemoteBitrateEstimatorAbsSendTime() = + default; + +bool RemoteBitrateEstimatorAbsSendTime::IsWithinClusterBounds( + TimeDelta send_delta, + const Cluster& cluster_aggregate) { + if (cluster_aggregate.count == 0) + return true; + TimeDelta cluster_mean = + cluster_aggregate.send_mean / cluster_aggregate.count; + return (send_delta - cluster_mean).Abs() < TimeDelta::Micros(2'500); +} + +void RemoteBitrateEstimatorAbsSendTime::MaybeAddCluster( + const Cluster& cluster_aggregate, + std::list<Cluster>& clusters) { + if (cluster_aggregate.count < kMinClusterSize || + cluster_aggregate.send_mean <= TimeDelta::Zero() || + cluster_aggregate.recv_mean <= TimeDelta::Zero()) { + return; + } + + Cluster cluster; + cluster.send_mean = cluster_aggregate.send_mean / cluster_aggregate.count; + cluster.recv_mean = cluster_aggregate.recv_mean / cluster_aggregate.count; + cluster.mean_size = cluster_aggregate.mean_size / cluster_aggregate.count; + cluster.count = cluster_aggregate.count; + cluster.num_above_min_delta = cluster_aggregate.num_above_min_delta; + clusters.push_back(cluster); +} + +RemoteBitrateEstimatorAbsSendTime::RemoteBitrateEstimatorAbsSendTime( + RemoteBitrateObserver* observer, + Clock* clock) + : clock_(clock), + observer_(observer), + detector_(&field_trials_), + remote_rate_(&field_trials_) { + RTC_DCHECK(clock_); + RTC_DCHECK(observer_); + RTC_LOG(LS_INFO) << "RemoteBitrateEstimatorAbsSendTime: Instantiating."; +} + +std::list<RemoteBitrateEstimatorAbsSendTime::Cluster> +RemoteBitrateEstimatorAbsSendTime::ComputeClusters() const { + std::list<Cluster> clusters; + Cluster cluster_aggregate; + Timestamp prev_send_time = Timestamp::MinusInfinity(); + Timestamp prev_recv_time = Timestamp::MinusInfinity(); + for (const Probe& probe : probes_) { + if (prev_send_time.IsFinite()) { + TimeDelta send_delta = probe.send_time - prev_send_time; + TimeDelta recv_delta = probe.recv_time - prev_recv_time; + if (send_delta >= kMinClusterDelta && recv_delta >= kMinClusterDelta) { + ++cluster_aggregate.num_above_min_delta; + } + if (!IsWithinClusterBounds(send_delta, cluster_aggregate)) { + MaybeAddCluster(cluster_aggregate, clusters); + cluster_aggregate = Cluster(); + } + cluster_aggregate.send_mean += send_delta; + cluster_aggregate.recv_mean += recv_delta; + cluster_aggregate.mean_size += probe.payload_size; + ++cluster_aggregate.count; + } + prev_send_time = probe.send_time; + prev_recv_time = probe.recv_time; + } + MaybeAddCluster(cluster_aggregate, clusters); + return clusters; +} + +const RemoteBitrateEstimatorAbsSendTime::Cluster* +RemoteBitrateEstimatorAbsSendTime::FindBestProbe( + const std::list<Cluster>& clusters) const { + DataRate highest_probe_bitrate = DataRate::Zero(); + const Cluster* best = nullptr; + for (const auto& cluster : clusters) { + if (cluster.send_mean == TimeDelta::Zero() || + cluster.recv_mean == TimeDelta::Zero()) { + continue; + } + if (cluster.num_above_min_delta > cluster.count / 2 && + (cluster.recv_mean - cluster.send_mean <= TimeDelta::Millis(2) && + cluster.send_mean - cluster.recv_mean <= TimeDelta::Millis(5))) { + DataRate probe_bitrate = + std::min(cluster.SendBitrate(), cluster.RecvBitrate()); + if (probe_bitrate > highest_probe_bitrate) { + highest_probe_bitrate = probe_bitrate; + best = &cluster; + } + } else { + RTC_LOG(LS_INFO) << "Probe failed, sent at " + << cluster.SendBitrate().bps() << " bps, received at " + << cluster.RecvBitrate().bps() + << " bps. Mean send delta: " << cluster.send_mean.ms() + << " ms, mean recv delta: " << cluster.recv_mean.ms() + << " ms, num probes: " << cluster.count; + break; + } + } + return best; +} + +RemoteBitrateEstimatorAbsSendTime::ProbeResult +RemoteBitrateEstimatorAbsSendTime::ProcessClusters(Timestamp now) { + std::list<Cluster> clusters = ComputeClusters(); + 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 ProbeResult::kNoUpdate; + } + + if (const Cluster* best = FindBestProbe(clusters)) { + DataRate probe_bitrate = std::min(best->SendBitrate(), best->RecvBitrate()); + // Make sure that a probe sent on a lower bitrate than our estimate can't + // reduce the estimate. + if (IsBitrateImproving(probe_bitrate)) { + RTC_LOG(LS_INFO) << "Probe successful, sent at " + << best->SendBitrate().bps() << " bps, received at " + << best->RecvBitrate().bps() + << " bps. Mean send delta: " << best->send_mean.ms() + << " ms, mean recv delta: " << best->recv_mean.ms() + << " ms, num probes: " << best->count; + remote_rate_.SetEstimate(probe_bitrate, now); + return ProbeResult::kBitrateUpdated; + } + } + + // Not probing and received non-probe packet, or finished with current set + // of probes. + if (clusters.size() >= kExpectedNumberOfProbes) + probes_.clear(); + return ProbeResult::kNoUpdate; +} + +bool RemoteBitrateEstimatorAbsSendTime::IsBitrateImproving( + DataRate probe_bitrate) const { + bool initial_probe = + !remote_rate_.ValidEstimate() && probe_bitrate > DataRate::Zero(); + bool bitrate_above_estimate = remote_rate_.ValidEstimate() && + probe_bitrate > remote_rate_.LatestEstimate(); + return initial_probe || bitrate_above_estimate; +} + +void RemoteBitrateEstimatorAbsSendTime::IncomingPacket( + int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) { + RTC_DCHECK_RUNS_SERIALIZED(&network_race_); + if (!header.extension.hasAbsoluteSendTime) { + RTC_LOG(LS_WARNING) + << "RemoteBitrateEstimatorAbsSendTimeImpl: Incoming packet " + "is missing absolute send time extension!"; + return; + } + IncomingPacketInfo(Timestamp::Millis(arrival_time_ms), + header.extension.absoluteSendTime, + DataSize::Bytes(payload_size), header.ssrc); +} + +void RemoteBitrateEstimatorAbsSendTime::IncomingPacketInfo( + Timestamp arrival_time, + uint32_t send_time_24bits, + DataSize payload_size, + uint32_t ssrc) { + RTC_CHECK(send_time_24bits < (1ul << 24)); + if (!uma_recorded_) { + RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram, BweNames::kReceiverAbsSendTime, + BweNames::kBweNamesMax); + uma_recorded_ = true; + } + // Shift up send time to use the full 32 bits that inter_arrival works with, + // so wrapping works properly. + uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift; + Timestamp send_time = + Timestamp::Millis(static_cast<int64_t>(timestamp) * kTimestampToMs); + + Timestamp now = clock_->CurrentTime(); + // TODO(holmer): SSRCs are only needed for REMB, should be broken out from + // here. + + // Check if incoming bitrate estimate is valid, and if it needs to be reset. + absl::optional<uint32_t> incoming_bitrate = + incoming_bitrate_.Rate(arrival_time.ms()); + if (incoming_bitrate) { + incoming_bitrate_initialized_ = true; + } else if (incoming_bitrate_initialized_) { + // Incoming bitrate had a previous valid value, but now not enough data + // point are left within the current window. Reset incoming bitrate + // estimator so that the window size will only contain new data points. + incoming_bitrate_.Reset(); + incoming_bitrate_initialized_ = false; + } + incoming_bitrate_.Update(payload_size.bytes(), arrival_time.ms()); + + if (first_packet_time_.IsInfinite()) { + first_packet_time_ = now; + } + + uint32_t ts_delta = 0; + int64_t t_delta = 0; + int size_delta = 0; + bool update_estimate = false; + DataRate target_bitrate = DataRate::Zero(); + std::vector<uint32_t> ssrcs; + { + MutexLock lock(&mutex_); + + TimeoutStreams(now); + RTC_DCHECK(inter_arrival_); + RTC_DCHECK(estimator_); + ssrcs_.insert_or_assign(ssrc, now); + + // For now only try to detect probes while we don't have a valid estimate. + // We currently assume that only packets larger than 200 bytes are paced by + // the sender. + static constexpr DataSize kMinProbePacketSize = DataSize::Bytes(200); + if (payload_size > kMinProbePacketSize && + (!remote_rate_.ValidEstimate() || + now - first_packet_time_ < kInitialProbingInterval)) { + // TODO(holmer): Use a map instead to get correct order? + if (total_probes_received_ < kMaxProbePackets) { + TimeDelta send_delta = TimeDelta::Millis(-1); + TimeDelta recv_delta = TimeDelta::Millis(-1); + if (!probes_.empty()) { + send_delta = send_time - probes_.back().send_time; + recv_delta = arrival_time - probes_.back().recv_time; + } + RTC_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_.emplace_back(send_time, arrival_time, payload_size); + ++total_probes_received_; + // Make sure that a probe which updated the bitrate immediately has an + // effect by calling the OnReceiveBitrateChanged callback. + if (ProcessClusters(now) == ProbeResult::kBitrateUpdated) + update_estimate = true; + } + if (inter_arrival_->ComputeDeltas(timestamp, arrival_time.ms(), now.ms(), + payload_size.bytes(), &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(), + arrival_time.ms()); + detector_.Detect(estimator_->offset(), ts_delta_ms, + estimator_->num_of_deltas(), arrival_time.ms()); + } + + if (!update_estimate) { + // Check if it's time for a periodic update or if we should update because + // of an over-use. + if (last_update_.IsInfinite() || + now.ms() - last_update_.ms() > + remote_rate_.GetFeedbackInterval().ms()) { + update_estimate = true; + } else if (detector_.State() == BandwidthUsage::kBwOverusing) { + absl::optional<uint32_t> incoming_rate = + incoming_bitrate_.Rate(arrival_time.ms()); + if (incoming_rate && remote_rate_.TimeToReduceFurther( + now, DataRate::BitsPerSec(*incoming_rate))) { + update_estimate = true; + } + } + } + + if (update_estimate) { + // 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. + const RateControlInput input( + detector_.State(), OptionalRateFromOptionalBps( + incoming_bitrate_.Rate(arrival_time.ms()))); + target_bitrate = remote_rate_.Update(&input, now); + update_estimate = remote_rate_.ValidEstimate(); + ssrcs = Keys(ssrcs_); + } + } + if (update_estimate) { + last_update_ = now; + observer_->OnReceiveBitrateChanged(ssrcs, target_bitrate.bps<uint32_t>()); + } +} + +TimeDelta RemoteBitrateEstimatorAbsSendTime::Process() { + return TimeDelta::PlusInfinity(); +} + +void RemoteBitrateEstimatorAbsSendTime::TimeoutStreams(Timestamp now) { + for (auto it = ssrcs_.begin(); it != ssrcs_.end();) { + if (now - it->second > TimeDelta::Millis(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_ = std::make_unique<InterArrival>( + (kTimestampGroupLengthMs << kInterArrivalShift) / 1000, kTimestampToMs, + true); + estimator_ = std::make_unique<OveruseEstimator>(OverUseDetectorOptions()); + // 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. + } +} + +void RemoteBitrateEstimatorAbsSendTime::OnRttUpdate(int64_t avg_rtt_ms, + int64_t /*max_rtt_ms*/) { + MutexLock lock(&mutex_); + remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms)); +} + +void RemoteBitrateEstimatorAbsSendTime::RemoveStream(uint32_t ssrc) { + MutexLock lock(&mutex_); + ssrcs_.erase(ssrc); +} + +DataRate RemoteBitrateEstimatorAbsSendTime::LatestEstimate() const { + // Currently accessed only from the worker thread (see Call::GetStats()). + MutexLock lock(&mutex_); + if (!remote_rate_.ValidEstimate() || ssrcs_.empty()) { + return DataRate::Zero(); + } + return remote_rate_.LatestEstimate(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h new file mode 100644 index 0000000000..fd33c84b04 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015 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 MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_ABS_SEND_TIME_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_ABS_SEND_TIME_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <list> +#include <map> +#include <memory> +#include <vector> + +#include "api/rtp_headers.h" +#include "api/transport/field_trial_based_config.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/remote_bitrate_estimator/aimd_rate_control.h" +#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "modules/remote_bitrate_estimator/inter_arrival.h" +#include "modules/remote_bitrate_estimator/overuse_detector.h" +#include "modules/remote_bitrate_estimator/overuse_estimator.h" +#include "rtc_base/checks.h" +#include "rtc_base/race_checker.h" +#include "rtc_base/rate_statistics.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { + +class RemoteBitrateEstimatorAbsSendTime : public RemoteBitrateEstimator { + public: + RemoteBitrateEstimatorAbsSendTime(RemoteBitrateObserver* observer, + Clock* clock); + + RemoteBitrateEstimatorAbsSendTime() = delete; + RemoteBitrateEstimatorAbsSendTime(const RemoteBitrateEstimatorAbsSendTime&) = + delete; + RemoteBitrateEstimatorAbsSendTime& operator=( + const RemoteBitrateEstimatorAbsSendTime&) = delete; + + ~RemoteBitrateEstimatorAbsSendTime() override; + + void IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) override; + TimeDelta Process() override; + void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override; + void RemoveStream(uint32_t ssrc) override; + DataRate LatestEstimate() const override; + + private: + struct Probe { + Probe(Timestamp send_time, Timestamp recv_time, DataSize payload_size) + : send_time(send_time), + recv_time(recv_time), + payload_size(payload_size) {} + + Timestamp send_time; + Timestamp recv_time; + DataSize payload_size; + }; + + struct Cluster { + DataRate SendBitrate() const { return mean_size / send_mean; } + DataRate RecvBitrate() const { return mean_size / recv_mean; } + + TimeDelta send_mean = TimeDelta::Zero(); + TimeDelta recv_mean = TimeDelta::Zero(); + // TODO(holmer): Add some variance metric as well? + DataSize mean_size = DataSize::Zero(); + int count = 0; + int num_above_min_delta = 0; + }; + + enum class ProbeResult { kBitrateUpdated, kNoUpdate }; + + static bool IsWithinClusterBounds(TimeDelta send_delta, + const Cluster& cluster_aggregate); + + static void MaybeAddCluster(const Cluster& cluster_aggregate, + std::list<Cluster>& clusters); + + void IncomingPacketInfo(Timestamp arrival_time, + uint32_t send_time_24bits, + DataSize payload_size, + uint32_t ssrc); + + std::list<Cluster> ComputeClusters() const; + + const Cluster* FindBestProbe(const std::list<Cluster>& clusters) const; + + // Returns true if a probe which changed the estimate was detected. + ProbeResult ProcessClusters(Timestamp now) + RTC_EXCLUSIVE_LOCKS_REQUIRED(&mutex_); + + bool IsBitrateImproving(DataRate probe_bitrate) const + RTC_EXCLUSIVE_LOCKS_REQUIRED(&mutex_); + + void TimeoutStreams(Timestamp now) RTC_EXCLUSIVE_LOCKS_REQUIRED(&mutex_); + + rtc::RaceChecker network_race_; + Clock* const clock_; + const FieldTrialBasedConfig field_trials_; + RemoteBitrateObserver* const observer_; + std::unique_ptr<InterArrival> inter_arrival_; + std::unique_ptr<OveruseEstimator> estimator_; + OveruseDetector detector_; + RateStatistics incoming_bitrate_{kBitrateWindowMs, 8000}; + bool incoming_bitrate_initialized_ = false; + std::list<Probe> probes_; + size_t total_probes_received_ = 0; + Timestamp first_packet_time_ = Timestamp::MinusInfinity(); + Timestamp last_update_ = Timestamp::MinusInfinity(); + bool uma_recorded_ = false; + + mutable Mutex mutex_; + std::map<uint32_t, Timestamp> ssrcs_ RTC_GUARDED_BY(&mutex_); + AimdRateControl remote_rate_ RTC_GUARDED_BY(&mutex_); +}; + +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_ABS_SEND_TIME_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc new file mode 100644 index 0000000000..d8ef23cc92 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc @@ -0,0 +1,296 @@ +/* + * 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 "modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h" + +#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" +#include "test/gtest.h" + +namespace webrtc { + +class RemoteBitrateEstimatorAbsSendTimeTest + : public RemoteBitrateEstimatorTest { + public: + RemoteBitrateEstimatorAbsSendTimeTest() {} + + RemoteBitrateEstimatorAbsSendTimeTest( + const RemoteBitrateEstimatorAbsSendTimeTest&) = delete; + RemoteBitrateEstimatorAbsSendTimeTest& operator=( + const RemoteBitrateEstimatorAbsSendTimeTest&) = delete; + + virtual void SetUp() { + bitrate_estimator_.reset(new RemoteBitrateEstimatorAbsSendTime( + bitrate_observer_.get(), &clock_)); + } + + protected: +}; + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, InitialBehavior) { + InitialBehaviorTestHelper(674840); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, RateIncreaseReordering) { + RateIncreaseReorderingTestHelper(674840); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, RateIncreaseRtpTimestamps) { + RateIncreaseRtpTimestampsTestHelper(1237); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropOneStream) { + CapacityDropTestHelper(1, false, 633, 0); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropPosOffsetChange) { + CapacityDropTestHelper(1, false, 267, 30000); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropNegOffsetChange) { + CapacityDropTestHelper(1, false, 267, -30000); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropOneStreamWrap) { + CapacityDropTestHelper(1, true, 633, 0); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropTwoStreamsWrap) { + CapacityDropTestHelper(2, true, 700, 0); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThreeStreamsWrap) { + CapacityDropTestHelper(3, true, 633, 0); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThirteenStreamsWrap) { + CapacityDropTestHelper(13, true, 667, 0); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropNineteenStreamsWrap) { + CapacityDropTestHelper(19, true, 667, 0); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, CapacityDropThirtyStreamsWrap) { + CapacityDropTestHelper(30, true, 667, 0); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestTimestampGrouping) { + TestTimestampGroupingTestHelper(); +} + +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 int64_t kStreamTimeOutMs = 2000; + const int64_t kProcessIntervalMs = 1000; + IncomingPacket(0, 1000, clock_.TimeInMilliseconds(), 0, 0); + clock_.AdvanceTimeMilliseconds(kStreamTimeOutMs + 1); + // Trigger timeout. + bitrate_estimator_->Process(); + clock_.AdvanceTimeMilliseconds(kProcessIntervalMs); + // This shouldn't crash. + 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)); + } + + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_GT(bitrate_observer_->latest_bitrate(), 1500000u); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, + TestProbeDetectionNonPacedPackets) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // First burst sent at 8 * 1000 / 10 = 800 kbps, but with every other packet + // not being paced which could mess things up. + 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)); + // Non-paced packet, arriving 5 ms after. + clock_.AdvanceTimeMilliseconds(5); + IncomingPacket(0, 100, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000)); + } + + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_GT(bitrate_observer_->latest_bitrate(), 800000u); +} + +// 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(); + int64_t send_time_ms = 0; + // First burst sent at 8 * 1000 / 10 = 800 kbps. + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(10); + now_ms = clock_.TimeInMilliseconds(); + send_time_ms += 10; + IncomingPacket(0, 1000, now_ms, 90 * send_time_ms, + AbsSendTime(send_time_ms, 1000)); + } + + // Second burst sent at 8 * 1000 / 5 = 1600 kbps, arriving at 8 * 1000 / 8 = + // 1000 kbps. + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(8); + now_ms = clock_.TimeInMilliseconds(); + send_time_ms += 5; + IncomingPacket(0, 1000, now_ms, send_time_ms, + AbsSendTime(send_time_ms, 1000)); + } + + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_NEAR(bitrate_observer_->latest_bitrate(), 800000u, 10000); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, + TestProbeDetectionSlightlyFasterArrival) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // First burst sent at 8 * 1000 / 10 = 800 kbps. + // Arriving at 8 * 1000 / 5 = 1600 kbps. + int64_t send_time_ms = 0; + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(5); + send_time_ms += 10; + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 1000, now_ms, 90 * send_time_ms, + AbsSendTime(send_time_ms, 1000)); + } + + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_GT(bitrate_observer_->latest_bitrate(), 800000u); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestProbeDetectionFasterArrival) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // First burst sent at 8 * 1000 / 10 = 800 kbps. + // Arriving at 8 * 1000 / 5 = 1600 kbps. + int64_t send_time_ms = 0; + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(1); + send_time_ms += 10; + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 1000, now_ms, 90 * send_time_ms, + AbsSendTime(send_time_ms, 1000)); + } + + bitrate_estimator_->Process(); + EXPECT_FALSE(bitrate_observer_->updated()); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, TestProbeDetectionSlowerArrival) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // First burst sent at 8 * 1000 / 5 = 1600 kbps. + // Arriving at 8 * 1000 / 7 = 1142 kbps. + int64_t send_time_ms = 0; + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(7); + send_time_ms += 5; + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 1000, now_ms, 90 * send_time_ms, + AbsSendTime(send_time_ms, 1000)); + } + + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_NEAR(bitrate_observer_->latest_bitrate(), 1140000, 10000); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, + TestProbeDetectionSlowerArrivalHighBitrate) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // Burst sent at 8 * 1000 / 1 = 8000 kbps. + // Arriving at 8 * 1000 / 2 = 4000 kbps. + int64_t send_time_ms = 0; + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(2); + send_time_ms += 1; + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 1000, now_ms, 90 * send_time_ms, + AbsSendTime(send_time_ms, 1000)); + } + + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_NEAR(bitrate_observer_->latest_bitrate(), 4000000u, 10000); +} + +TEST_F(RemoteBitrateEstimatorAbsSendTimeTest, ProbingIgnoresSmallPackets) { + const int kProbeLength = 5; + int64_t now_ms = clock_.TimeInMilliseconds(); + // Probing with 200 bytes every 10 ms, should be ignored by the probe + // detection. + for (int i = 0; i < kProbeLength; ++i) { + clock_.AdvanceTimeMilliseconds(10); + now_ms = clock_.TimeInMilliseconds(); + IncomingPacket(0, 200, now_ms, 90 * now_ms, AbsSendTime(now_ms, 1000)); + } + + bitrate_estimator_->Process(); + EXPECT_FALSE(bitrate_observer_->updated()); + + // Followed by a probe with 1000 bytes packets, should be detected as a + // probe. + 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)); + } + + // Wait long enough so that we can call Process again. + clock_.AdvanceTimeMilliseconds(1000); + + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_NEAR(bitrate_observer_->latest_bitrate(), 800000u, 10000); +} +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build new file mode 100644 index 0000000000..e572e90c31 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build @@ -0,0 +1,244 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1" +DEFINES["BWE_TEST_LOGGING_COMPILE_TIME_ENABLE"] = "0" +DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True +DEFINES["RTC_ENABLE_VP9"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_LIBRARY_IMPL"] = True +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0" + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "!/third_party/libwebrtc/gen", + "/ipc/chromium/src", + "/third_party/libwebrtc/", + "/third_party/libwebrtc/third_party/abseil-cpp/", + "/tools/profiler/public" +] + +SOURCES += [ + "/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc", + "/third_party/libwebrtc/modules/remote_bitrate_estimator/bwe_defines.cc", + "/third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.cc", + "/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.cc", + "/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.cc", + "/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.cc", + "/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc", + "/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1" + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_GNU_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0" + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_AURA"] = "1" + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_UDEV"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + + OS_LIBS += [ + "rt" + ] + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "2" + DEFINES["UNICODE"] = True + DEFINES["USE_AURA"] = "1" + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP" + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_HAS_NODISCARD"] = True + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + + OS_LIBS += [ + "crypt32", + "iphlpapi", + "secur32", + "winmm" + ] + +if CONFIG["CPU_ARCH"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["CPU_ARCH"] == "mips32": + + DEFINES["MIPS32_LE"] = True + DEFINES["MIPS_FPU_LE"] = True + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["CPU_ARCH"] == "mips64": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["CPU_ARCH"] == "x86": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["CPU_ARCH"] == "x86_64": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0" + +if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_X11"] = "1" + +if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Android": + + OS_LIBS += [ + "android_support", + "unwind" + ] + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Android": + + CXXFLAGS += [ + "-msse2" + ] + + OS_LIBS += [ + "android_support" + ] + +if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux": + + CXXFLAGS += [ + "-msse2" + ] + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["_GNU_SOURCE"] = True + +Library("remote_bitrate_estimator_gn") diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc new file mode 100644 index 0000000000..8f15912a49 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc @@ -0,0 +1,245 @@ +/* + * 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 "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h" + + +#include <cstdint> +#include <utility> + +#include "absl/types/optional.h" +#include "modules/remote_bitrate_estimator/aimd_rate_control.h" +#include "modules/remote_bitrate_estimator/include/bwe_defines.h" +#include "modules/remote_bitrate_estimator/inter_arrival.h" +#include "modules/remote_bitrate_estimator/overuse_detector.h" +#include "modules/remote_bitrate_estimator/overuse_estimator.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "system_wrappers/include/clock.h" +#include "system_wrappers/include/metrics.h" + +namespace webrtc { +namespace { +absl::optional<DataRate> OptionalRateFromOptionalBps( + absl::optional<int> bitrate_bps) { + if (bitrate_bps) { + return DataRate::BitsPerSec(*bitrate_bps); + } else { + return absl::nullopt; + } +} +} // namespace + +enum { kTimestampGroupLengthMs = 5 }; +static const double kTimestampToMs = 1.0 / 90.0; + +struct RemoteBitrateEstimatorSingleStream::Detector { + explicit Detector(int64_t last_packet_time_ms, + const OverUseDetectorOptions& options, + bool enable_burst_grouping, + const FieldTrialsView* key_value_config) + : last_packet_time_ms(last_packet_time_ms), + inter_arrival(90 * kTimestampGroupLengthMs, + kTimestampToMs, + enable_burst_grouping), + estimator(options), + detector(key_value_config) {} + int64_t last_packet_time_ms; + InterArrival inter_arrival; + OveruseEstimator estimator; + OveruseDetector detector; +}; + +RemoteBitrateEstimatorSingleStream::RemoteBitrateEstimatorSingleStream( + RemoteBitrateObserver* observer, + Clock* clock) + : clock_(clock), + incoming_bitrate_(kBitrateWindowMs, 8000), + last_valid_incoming_bitrate_(0), + remote_rate_(&field_trials_), + observer_(observer), + last_process_time_(-1), + process_interval_ms_(kProcessIntervalMs), + uma_recorded_(false) { + RTC_LOG(LS_INFO) << "RemoteBitrateEstimatorSingleStream: Instantiating."; +} + +RemoteBitrateEstimatorSingleStream::~RemoteBitrateEstimatorSingleStream() { + while (!overuse_detectors_.empty()) { + SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.begin(); + delete it->second; + overuse_detectors_.erase(it); + } +} + +void RemoteBitrateEstimatorSingleStream::IncomingPacket( + int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) { + if (!uma_recorded_) { + BweNames type = BweNames::kReceiverTOffset; + if (!header.extension.hasTransmissionTimeOffset) + type = BweNames::kReceiverNoExtension; + RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram, type, BweNames::kBweNamesMax); + uma_recorded_ = true; + } + uint32_t ssrc = header.ssrc; + uint32_t rtp_timestamp = + header.timestamp + header.extension.transmissionTimeOffset; + int64_t now_ms = clock_->TimeInMilliseconds(); + MutexLock lock(&mutex_); + 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 + // around in this map until the channel is deleted. This is OK since the + // 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<SsrcOveruseEstimatorMap::iterator, bool> insert_result = + overuse_detectors_.insert( + std::make_pair(ssrc, new Detector(now_ms, OverUseDetectorOptions(), + true, &field_trials_))); + it = insert_result.first; + } + Detector* estimator = it->second; + estimator->last_packet_time_ms = now_ms; + + // Check if incoming bitrate estimate is valid, and if it needs to be reset. + absl::optional<uint32_t> incoming_bitrate = incoming_bitrate_.Rate(now_ms); + if (incoming_bitrate) { + last_valid_incoming_bitrate_ = *incoming_bitrate; + } else if (last_valid_incoming_bitrate_ > 0) { + // Incoming bitrate had a previous valid value, but now not enough data + // point are left within the current window. Reset incoming bitrate + // estimator so that the window size will only contain new data points. + incoming_bitrate_.Reset(); + last_valid_incoming_bitrate_ = 0; + } + incoming_bitrate_.Update(payload_size, now_ms); + + 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, now_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(), now_ms); + estimator->detector.Detect(estimator->estimator.offset(), + timestamp_delta_ms, + estimator->estimator.num_of_deltas(), now_ms); + } + if (estimator->detector.State() == BandwidthUsage::kBwOverusing) { + absl::optional<uint32_t> incoming_bitrate_bps = + incoming_bitrate_.Rate(now_ms); + if (incoming_bitrate_bps && + (prior_state != BandwidthUsage::kBwOverusing || + remote_rate_.TimeToReduceFurther( + Timestamp::Millis(now_ms), + DataRate::BitsPerSec(*incoming_bitrate_bps)))) { + // 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); + } + } +} + +TimeDelta RemoteBitrateEstimatorSingleStream::Process() { + MutexLock lock(&mutex_); + int64_t now_ms = clock_->TimeInMilliseconds(); + int64_t next_process_time_ms = last_process_time_ + process_interval_ms_; + if (last_process_time_ == -1 || now_ms >= next_process_time_ms) { + UpdateEstimate(now_ms); + last_process_time_ = now_ms; + return TimeDelta::Millis(process_interval_ms_); + } + + return TimeDelta::Millis(next_process_time_ms - now_ms); +} + +void RemoteBitrateEstimatorSingleStream::UpdateEstimate(int64_t now_ms) { + BandwidthUsage bw_state = BandwidthUsage::kBwNormal; + SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.begin(); + while (it != overuse_detectors_.end()) { + 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 { + // Make sure that we trigger an over-use if any of the over-use detectors + // is detecting over-use. + 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()) { + return; + } + + const RateControlInput input( + bw_state, OptionalRateFromOptionalBps(incoming_bitrate_.Rate(now_ms))); + uint32_t target_bitrate = + remote_rate_.Update(&input, Timestamp::Millis(now_ms)).bps<uint32_t>(); + if (remote_rate_.ValidEstimate()) { + process_interval_ms_ = remote_rate_.GetFeedbackInterval().ms(); + RTC_DCHECK_GT(process_interval_ms_, 0); + std::vector<uint32_t> ssrcs; + GetSsrcs(&ssrcs); + if (observer_) + observer_->OnReceiveBitrateChanged(ssrcs, target_bitrate); + } +} + +void RemoteBitrateEstimatorSingleStream::OnRttUpdate(int64_t avg_rtt_ms, + int64_t max_rtt_ms) { + MutexLock lock(&mutex_); + remote_rate_.SetRtt(TimeDelta::Millis(avg_rtt_ms)); +} + +void RemoteBitrateEstimatorSingleStream::RemoveStream(unsigned int ssrc) { + MutexLock lock(&mutex_); + SsrcOveruseEstimatorMap::iterator it = overuse_detectors_.find(ssrc); + if (it != overuse_detectors_.end()) { + delete it->second; + overuse_detectors_.erase(it); + } +} + +DataRate RemoteBitrateEstimatorSingleStream::LatestEstimate() const { + MutexLock lock(&mutex_); + if (!remote_rate_.ValidEstimate() || overuse_detectors_.empty()) { + return DataRate::Zero(); + } + return remote_rate_.LatestEstimate(); +} + +void RemoteBitrateEstimatorSingleStream::GetSsrcs( + std::vector<uint32_t>* ssrcs) const { + RTC_DCHECK(ssrcs); + ssrcs->resize(overuse_detectors_.size()); + int i = 0; + for (SsrcOveruseEstimatorMap::const_iterator it = overuse_detectors_.begin(); + it != overuse_detectors_.end(); ++it, ++i) { + (*ssrcs)[i] = it->first; + } +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h new file mode 100644 index 0000000000..699f259d48 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015 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 MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <map> +#include <vector> + +#include "api/transport/field_trial_based_config.h" +#include "api/units/data_rate.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/remote_bitrate_estimator/aimd_rate_control.h" +#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "rtc_base/rate_statistics.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +class Clock; +struct RTPHeader; + +class RemoteBitrateEstimatorSingleStream : public RemoteBitrateEstimator { + public: + RemoteBitrateEstimatorSingleStream(RemoteBitrateObserver* observer, + Clock* clock); + + RemoteBitrateEstimatorSingleStream() = delete; + RemoteBitrateEstimatorSingleStream( + const RemoteBitrateEstimatorSingleStream&) = delete; + RemoteBitrateEstimatorSingleStream& operator=( + const RemoteBitrateEstimatorSingleStream&) = delete; + + ~RemoteBitrateEstimatorSingleStream() override; + + void IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) override; + TimeDelta Process() override; + void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override; + void RemoveStream(uint32_t ssrc) override; + DataRate LatestEstimate() const override; + + private: + struct Detector; + + typedef std::map<uint32_t, Detector*> SsrcOveruseEstimatorMap; + + // Triggers a new estimate calculation. + void UpdateEstimate(int64_t time_now) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + void GetSsrcs(std::vector<uint32_t>* ssrcs) const + RTC_SHARED_LOCKS_REQUIRED(mutex_); + + Clock* const clock_; + const FieldTrialBasedConfig field_trials_; + SsrcOveruseEstimatorMap overuse_detectors_ RTC_GUARDED_BY(mutex_); + RateStatistics incoming_bitrate_ RTC_GUARDED_BY(mutex_); + uint32_t last_valid_incoming_bitrate_ RTC_GUARDED_BY(mutex_); + AimdRateControl remote_rate_ RTC_GUARDED_BY(mutex_); + RemoteBitrateObserver* const observer_ RTC_GUARDED_BY(mutex_); + mutable Mutex mutex_; + int64_t last_process_time_; + int64_t process_interval_ms_ RTC_GUARDED_BY(mutex_); + bool uma_recorded_; +}; + +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_SINGLE_STREAM_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc new file mode 100644 index 0000000000..64ef39d935 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc @@ -0,0 +1,78 @@ +/* + * 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 "modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h" + +#include "modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" +#include "test/gtest.h" + +namespace webrtc { + +class RemoteBitrateEstimatorSingleTest : public RemoteBitrateEstimatorTest { + public: + RemoteBitrateEstimatorSingleTest() {} + + RemoteBitrateEstimatorSingleTest(const RemoteBitrateEstimatorSingleTest&) = + delete; + RemoteBitrateEstimatorSingleTest& operator=( + const RemoteBitrateEstimatorSingleTest&) = delete; + + virtual void SetUp() { + bitrate_estimator_.reset(new RemoteBitrateEstimatorSingleStream( + bitrate_observer_.get(), &clock_)); + } + + protected: +}; + +TEST_F(RemoteBitrateEstimatorSingleTest, InitialBehavior) { + InitialBehaviorTestHelper(508017); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseReordering) { + RateIncreaseReorderingTestHelper(506422); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, RateIncreaseRtpTimestamps) { + RateIncreaseRtpTimestampsTestHelper(1267); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStream) { + CapacityDropTestHelper(1, false, 633, 0); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropOneStreamWrap) { + CapacityDropTestHelper(1, true, 633, 0); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropTwoStreamsWrap) { + CapacityDropTestHelper(2, true, 767, 0); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThreeStreamsWrap) { + CapacityDropTestHelper(3, true, 567, 0); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirteenStreamsWrap) { + CapacityDropTestHelper(13, true, 567, 0); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropNineteenStreamsWrap) { + CapacityDropTestHelper(19, true, 700, 0); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, CapacityDropThirtyStreamsWrap) { + CapacityDropTestHelper(30, true, 733, 0); +} + +TEST_F(RemoteBitrateEstimatorSingleTest, TestTimestampGrouping) { + TestTimestampGroupingTestHelper(); +} +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc new file mode 100644 index 0000000000..899037f5a7 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc @@ -0,0 +1,594 @@ +/* + * 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 "modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h" + +#include <algorithm> +#include <limits> +#include <utility> + +#include "rtc_base/checks.h" + +namespace webrtc { + +const size_t kMtu = 1200; +const uint32_t kAcceptedBitrateErrorBps = 50000; + +// Number of packets needed before we have a valid estimate. +const int kNumInitialPackets = 2; + +namespace testing { + +void TestBitrateObserver::OnReceiveBitrateChanged( + const std::vector<uint32_t>& ssrcs, + uint32_t bitrate) { + latest_bitrate_ = bitrate; + updated_ = true; +} + +RtpStream::RtpStream(int fps, + int bitrate_bps, + uint32_t ssrc, + uint32_t frequency, + uint32_t timestamp_offset, + int64_t rtcp_receive_time) + : fps_(fps), + bitrate_bps_(bitrate_bps), + ssrc_(ssrc), + frequency_(frequency), + next_rtp_time_(0), + next_rtcp_time_(rtcp_receive_time), + rtp_timestamp_offset_(timestamp_offset), + kNtpFracPerMs(4.294967296E6) { + RTC_DCHECK_GT(fps_, 0); +} + +void RtpStream::set_rtp_timestamp_offset(uint32_t offset) { + rtp_timestamp_offset_ = offset; +} + +// Generates a new frame for this stream. If called too soon after the +// previous frame, no frame will be generated. The frame is split into +// packets. +int64_t RtpStream::GenerateFrame(int64_t time_now_us, PacketList* packets) { + if (time_now_us < next_rtp_time_) { + return next_rtp_time_; + } + RTC_DCHECK(packets); + size_t bits_per_frame = (bitrate_bps_ + fps_ / 2) / fps_; + size_t n_packets = + std::max<size_t>((bits_per_frame + 4 * kMtu) / (8 * kMtu), 1u); + size_t packet_size = (bits_per_frame + 4 * n_packets) / (8 * n_packets); + for (size_t i = 0; i < n_packets; ++i) { + RtpPacket* packet = new RtpPacket; + packet->send_time = time_now_us + kSendSideOffsetUs; + packet->size = packet_size; + packet->rtp_timestamp = + rtp_timestamp_offset_ + + static_cast<uint32_t>(((frequency_ / 1000) * packet->send_time + 500) / + 1000); + packet->ssrc = ssrc_; + packets->push_back(packet); + } + next_rtp_time_ = time_now_us + (1000000 + fps_ / 2) / fps_; + return next_rtp_time_; +} + +// The send-side time when the next frame can be generated. +int64_t RtpStream::next_rtp_time() const { + return next_rtp_time_; +} + +// Generates an RTCP packet. +RtpStream::RtcpPacket* RtpStream::Rtcp(int64_t time_now_us) { + if (time_now_us < next_rtcp_time_) { + return NULL; + } + RtcpPacket* rtcp = new RtcpPacket; + int64_t send_time_us = time_now_us + kSendSideOffsetUs; + rtcp->timestamp = + rtp_timestamp_offset_ + + static_cast<uint32_t>(((frequency_ / 1000) * send_time_us + 500) / 1000); + rtcp->ntp_secs = send_time_us / 1000000; + rtcp->ntp_frac = + static_cast<int64_t>((send_time_us % 1000000) * kNtpFracPerMs); + rtcp->ssrc = ssrc_; + next_rtcp_time_ = time_now_us + kRtcpIntervalUs; + return rtcp; +} + +void RtpStream::set_bitrate_bps(int bitrate_bps) { + ASSERT_GE(bitrate_bps, 0); + bitrate_bps_ = bitrate_bps; +} + +int RtpStream::bitrate_bps() const { + return bitrate_bps_; +} + +uint32_t RtpStream::ssrc() const { + return ssrc_; +} + +bool RtpStream::Compare(const std::pair<uint32_t, RtpStream*>& left, + const std::pair<uint32_t, RtpStream*>& right) { + return left.second->next_rtp_time_ < right.second->next_rtp_time_; +} + +StreamGenerator::StreamGenerator(int capacity, int64_t time_now) + : capacity_(capacity), prev_arrival_time_us_(time_now) {} + +StreamGenerator::~StreamGenerator() { + for (StreamMap::iterator it = streams_.begin(); it != streams_.end(); ++it) { + delete it->second; + } + streams_.clear(); +} + +// Add a new stream. +void StreamGenerator::AddStream(RtpStream* stream) { + streams_[stream->ssrc()] = stream; +} + +// Set the link capacity. +void StreamGenerator::set_capacity_bps(int capacity_bps) { + ASSERT_GT(capacity_bps, 0); + capacity_ = capacity_bps; +} + +// Divides `bitrate_bps` among all streams. The allocated bitrate per stream +// is decided by the current allocation ratios. +void StreamGenerator::SetBitrateBps(int bitrate_bps) { + ASSERT_GE(streams_.size(), 0u); + int total_bitrate_before = 0; + for (StreamMap::iterator it = streams_.begin(); it != streams_.end(); ++it) { + total_bitrate_before += it->second->bitrate_bps(); + } + int64_t bitrate_before = 0; + int total_bitrate_after = 0; + for (StreamMap::iterator it = streams_.begin(); it != streams_.end(); ++it) { + bitrate_before += it->second->bitrate_bps(); + int64_t bitrate_after = + (bitrate_before * bitrate_bps + total_bitrate_before / 2) / + total_bitrate_before; + it->second->set_bitrate_bps(bitrate_after - total_bitrate_after); + total_bitrate_after += it->second->bitrate_bps(); + } + ASSERT_EQ(bitrate_before, total_bitrate_before); + EXPECT_EQ(total_bitrate_after, bitrate_bps); +} + +// Set the RTP timestamp offset for the stream identified by `ssrc`. +void StreamGenerator::set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset) { + streams_[ssrc]->set_rtp_timestamp_offset(offset); +} + +// TODO(holmer): Break out the channel simulation part from this class to make +// it possible to simulate different types of channels. +int64_t StreamGenerator::GenerateFrame(RtpStream::PacketList* packets, + int64_t time_now_us) { + RTC_DCHECK(packets); + RTC_DCHECK(packets->empty()); + RTC_DCHECK_GT(capacity_, 0); + StreamMap::iterator it = + std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare); + (*it).second->GenerateFrame(time_now_us, packets); + for (RtpStream::PacketList::iterator packet_it = packets->begin(); + packet_it != packets->end(); ++packet_it) { + int capacity_bpus = capacity_ / 1000; + int64_t required_network_time_us = + (8 * 1000 * (*packet_it)->size + capacity_bpus / 2) / capacity_bpus; + prev_arrival_time_us_ = + std::max(time_now_us + required_network_time_us, + prev_arrival_time_us_ + required_network_time_us); + (*packet_it)->arrival_time = prev_arrival_time_us_; + } + it = std::min_element(streams_.begin(), streams_.end(), RtpStream::Compare); + return std::max((*it).second->next_rtp_time(), time_now_us); +} +} // namespace testing + +RemoteBitrateEstimatorTest::RemoteBitrateEstimatorTest() + : clock_(100000000), + bitrate_observer_(new testing::TestBitrateObserver), + stream_generator_( + new testing::StreamGenerator(1e6, // Capacity. + clock_.TimeInMicroseconds())), + arrival_time_offset_ms_(0) {} + +RemoteBitrateEstimatorTest::~RemoteBitrateEstimatorTest() {} + +void RemoteBitrateEstimatorTest::AddDefaultStream() { + stream_generator_->AddStream( + new testing::RtpStream(30, // Frames per second. + 3e5, // Bitrate. + 1, // SSRC. + 90000, // RTP frequency. + 0xFFFFF000, // Timestamp offset. + 0)); // RTCP receive time. +} + +uint32_t RemoteBitrateEstimatorTest::AbsSendTime(int64_t t, int64_t denom) { + return (((t << 18) + (denom >> 1)) / denom) & 0x00fffffful; +} + +uint32_t RemoteBitrateEstimatorTest::AddAbsSendTime(uint32_t t1, uint32_t t2) { + return (t1 + t2) & 0x00fffffful; +} + +const uint32_t RemoteBitrateEstimatorTest::kDefaultSsrc = 1; + +void RemoteBitrateEstimatorTest::IncomingPacket(uint32_t ssrc, + size_t payload_size, + int64_t arrival_time, + uint32_t rtp_timestamp, + uint32_t absolute_send_time) { + RTPHeader header; + memset(&header, 0, sizeof(header)); + header.ssrc = ssrc; + header.timestamp = rtp_timestamp; + header.extension.hasAbsoluteSendTime = true; + header.extension.absoluteSendTime = absolute_send_time; + RTC_CHECK_GE(arrival_time + arrival_time_offset_ms_, 0); + bitrate_estimator_->IncomingPacket(arrival_time + arrival_time_offset_ms_, + payload_size, header); +} + +// Generates a frame of packets belonging to a stream at a given bitrate and +// with a given ssrc. The stream is pushed through a very simple simulated +// network, and is then given to the receive-side bandwidth estimator. +// Returns true if an over-use was seen, false otherwise. +// The StreamGenerator::updated() should be used to check for any changes in +// target bitrate after the call to this function. +bool RemoteBitrateEstimatorTest::GenerateAndProcessFrame(uint32_t ssrc, + uint32_t bitrate_bps) { + RTC_DCHECK_GT(bitrate_bps, 0); + stream_generator_->SetBitrateBps(bitrate_bps); + testing::RtpStream::PacketList packets; + int64_t next_time_us = + stream_generator_->GenerateFrame(&packets, clock_.TimeInMicroseconds()); + bool overuse = false; + while (!packets.empty()) { + testing::RtpStream::RtpPacket* packet = packets.front(); + bitrate_observer_->Reset(); + // The simulated clock should match the time of packet->arrival_time + // since both are used in IncomingPacket(). + clock_.AdvanceTimeMicroseconds(packet->arrival_time - + clock_.TimeInMicroseconds()); + IncomingPacket(packet->ssrc, packet->size, + (packet->arrival_time + 500) / 1000, packet->rtp_timestamp, + AbsSendTime(packet->send_time, 1000000)); + if (bitrate_observer_->updated()) { + if (bitrate_observer_->latest_bitrate() < bitrate_bps) + overuse = true; + } + delete packet; + packets.pop_front(); + } + bitrate_estimator_->Process(); + clock_.AdvanceTimeMicroseconds(next_time_us - clock_.TimeInMicroseconds()); + return overuse; +} + +// Run the bandwidth estimator with a stream of `number_of_frames` frames, or +// until it reaches `target_bitrate`. +// Can for instance be used to run the estimator for some time to get it +// into a steady state. +uint32_t RemoteBitrateEstimatorTest::SteadyStateRun(uint32_t ssrc, + int max_number_of_frames, + uint32_t start_bitrate, + uint32_t min_bitrate, + uint32_t max_bitrate, + uint32_t target_bitrate) { + uint32_t bitrate_bps = start_bitrate; + bool bitrate_update_seen = false; + // Produce `number_of_frames` frames and give them to the estimator. + for (int i = 0; i < max_number_of_frames; ++i) { + bool overuse = GenerateAndProcessFrame(ssrc, bitrate_bps); + if (overuse) { + EXPECT_LT(bitrate_observer_->latest_bitrate(), max_bitrate); + EXPECT_GT(bitrate_observer_->latest_bitrate(), min_bitrate); + bitrate_bps = bitrate_observer_->latest_bitrate(); + bitrate_update_seen = true; + } else if (bitrate_observer_->updated()) { + bitrate_bps = bitrate_observer_->latest_bitrate(); + bitrate_observer_->Reset(); + } + if (bitrate_update_seen && bitrate_bps > target_bitrate) { + break; + } + } + EXPECT_TRUE(bitrate_update_seen); + return bitrate_bps; +} + +void RemoteBitrateEstimatorTest::InitialBehaviorTestHelper( + uint32_t expected_converge_bitrate) { + 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; + uint32_t absolute_send_time = 0; + EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero()); + clock_.AdvanceTimeMilliseconds(1000); + bitrate_estimator_->Process(); + EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero()); + EXPECT_FALSE(bitrate_observer_->updated()); + bitrate_observer_->Reset(); + clock_.AdvanceTimeMilliseconds(1000); + // Inserting packets for 5 seconds to get a valid estimate. + for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) { + if (i == kNumInitialPackets) { + bitrate_estimator_->Process(); + EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero()); + EXPECT_FALSE(bitrate_observer_->updated()); + bitrate_observer_->Reset(); + } + + IncomingPacket(kDefaultSsrc, kMtu, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + clock_.AdvanceTimeMilliseconds(1000 / kFramerate); + timestamp += 90 * kFrameIntervalMs; + absolute_send_time = + AddAbsSendTime(absolute_send_time, kFrameIntervalAbsSendTime); + } + bitrate_estimator_->Process(); + uint32_t bitrate_bps = bitrate_estimator_->LatestEstimate().bps<uint32_t>(); + EXPECT_NEAR(expected_converge_bitrate, bitrate_bps, kAcceptedBitrateErrorBps); + EXPECT_TRUE(bitrate_observer_->updated()); + bitrate_observer_->Reset(); + EXPECT_EQ(bitrate_observer_->latest_bitrate(), bitrate_bps); + bitrate_estimator_->RemoveStream(kDefaultSsrc); + EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero()); +} + +void RemoteBitrateEstimatorTest::RateIncreaseReorderingTestHelper( + uint32_t expected_bitrate_bps) { + 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; + uint32_t absolute_send_time = 0; + // Inserting packets for five seconds to get a valid estimate. + for (int i = 0; i < 5 * kFramerate + 1 + kNumInitialPackets; ++i) { + // TODO(sprang): Remove this hack once the single stream estimator is gone, + // as it doesn't do anything in Process(). + if (i == kNumInitialPackets) { + // Process after we have enough frames to get a valid input rate estimate. + bitrate_estimator_->Process(); + EXPECT_FALSE(bitrate_observer_->updated()); // No valid estimate. + } + + IncomingPacket(kDefaultSsrc, kMtu, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + clock_.AdvanceTimeMilliseconds(kFrameIntervalMs); + timestamp += 90 * kFrameIntervalMs; + absolute_send_time = + AddAbsSendTime(absolute_send_time, kFrameIntervalAbsSendTime); + } + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_->latest_bitrate(), + kAcceptedBitrateErrorBps); + for (int i = 0; i < 10; ++i) { + clock_.AdvanceTimeMilliseconds(2 * kFrameIntervalMs); + timestamp += 2 * 90 * kFrameIntervalMs; + absolute_send_time = + AddAbsSendTime(absolute_send_time, 2 * kFrameIntervalAbsSendTime); + IncomingPacket(kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), timestamp, + absolute_send_time); + IncomingPacket( + kDefaultSsrc, 1000, clock_.TimeInMilliseconds(), + timestamp - 90 * kFrameIntervalMs, + AddAbsSendTime(absolute_send_time, + -static_cast<int>(kFrameIntervalAbsSendTime))); + } + bitrate_estimator_->Process(); + EXPECT_TRUE(bitrate_observer_->updated()); + EXPECT_NEAR(expected_bitrate_bps, bitrate_observer_->latest_bitrate(), + kAcceptedBitrateErrorBps); +} + +// Make sure we initially increase the bitrate as expected. +void RemoteBitrateEstimatorTest::RateIncreaseRtpTimestampsTestHelper( + int expected_iterations) { + // This threshold corresponds approximately to increasing linearly with + // bitrate(i) = 1.04 * bitrate(i-1) + 1000 + // until bitrate(i) > 500000, with bitrate(1) ~= 30000. + uint32_t bitrate_bps = 30000; + int iterations = 0; + AddDefaultStream(); + // Feed the estimator with a stream of packets and verify that it reaches + // 500 kbps at the expected time. + while (bitrate_bps < 5e5) { + bool overuse = GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps); + if (overuse) { + EXPECT_GT(bitrate_observer_->latest_bitrate(), bitrate_bps); + bitrate_bps = bitrate_observer_->latest_bitrate(); + bitrate_observer_->Reset(); + } else if (bitrate_observer_->updated()) { + bitrate_bps = bitrate_observer_->latest_bitrate(); + bitrate_observer_->Reset(); + } + ++iterations; + ASSERT_LE(iterations, expected_iterations); + } + ASSERT_EQ(expected_iterations, iterations); +} + +void RemoteBitrateEstimatorTest::CapacityDropTestHelper( + int number_of_streams, + bool wrap_time_stamp, + uint32_t expected_bitrate_drop_delta, + int64_t receiver_clock_offset_change_ms) { + const int kFramerate = 30; + const int kStartBitrate = 900e3; + const int kMinExpectedBitrate = 800e3; + const int kMaxExpectedBitrate = 1100e3; + const uint32_t kInitialCapacityBps = 1000e3; + const uint32_t kReducedCapacityBps = 500e3; + + int steady_state_time = 0; + if (number_of_streams <= 1) { + steady_state_time = 10; + AddDefaultStream(); + } else { + steady_state_time = 10 * number_of_streams; + int bitrate_sum = 0; + int kBitrateDenom = number_of_streams * (number_of_streams - 1); + for (int i = 0; i < number_of_streams; i++) { + // First stream gets half available bitrate, while the rest share the + // remaining half i.e.: 1/2 = Sum[n/(N*(N-1))] for n=1..N-1 (rounded up) + int bitrate = kStartBitrate / 2; + if (i > 0) { + bitrate = (kStartBitrate * i + kBitrateDenom / 2) / kBitrateDenom; + } + uint32_t mask = ~0ull << (32 - i); + stream_generator_->AddStream( + new testing::RtpStream(kFramerate, // Frames per second. + bitrate, // Bitrate. + kDefaultSsrc + i, // SSRC. + 90000, // RTP frequency. + 0xFFFFF000u ^ mask, // Timestamp offset. + 0)); // RTCP receive time. + bitrate_sum += bitrate; + } + ASSERT_EQ(bitrate_sum, kStartBitrate); + } + if (wrap_time_stamp) { + stream_generator_->set_rtp_timestamp_offset( + kDefaultSsrc, + std::numeric_limits<uint32_t>::max() - steady_state_time * 90000); + } + + // Run in steady state to make the estimator converge. + stream_generator_->set_capacity_bps(kInitialCapacityBps); + uint32_t bitrate_bps = SteadyStateRun( + kDefaultSsrc, steady_state_time * kFramerate, kStartBitrate, + kMinExpectedBitrate, kMaxExpectedBitrate, kInitialCapacityBps); + EXPECT_NEAR(kInitialCapacityBps, bitrate_bps, 130000u); + bitrate_observer_->Reset(); + + // Add an offset to make sure the BWE can handle it. + arrival_time_offset_ms_ += receiver_clock_offset_change_ms; + + // Reduce the capacity and verify the decrease time. + stream_generator_->set_capacity_bps(kReducedCapacityBps); + int64_t overuse_start_time = clock_.TimeInMilliseconds(); + int64_t bitrate_drop_time = -1; + for (int i = 0; i < 100 * number_of_streams; ++i) { + GenerateAndProcessFrame(kDefaultSsrc, bitrate_bps); + if (bitrate_drop_time == -1 && + bitrate_observer_->latest_bitrate() <= kReducedCapacityBps) { + bitrate_drop_time = clock_.TimeInMilliseconds(); + } + if (bitrate_observer_->updated()) + bitrate_bps = bitrate_observer_->latest_bitrate(); + } + + EXPECT_NEAR(expected_bitrate_drop_delta, + bitrate_drop_time - overuse_start_time, 33); + + // Remove stream one by one. + for (int i = 0; i < number_of_streams; i++) { + EXPECT_EQ(bitrate_estimator_->LatestEstimate().bps(), bitrate_bps); + bitrate_estimator_->RemoveStream(kDefaultSsrc + i); + } + EXPECT_EQ(bitrate_estimator_->LatestEstimate(), DataRate::Zero()); +} + +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), -static_cast<int>(50 * kFrameIntervalAbsSendTime)); + // Initial set of frames to increase the bitrate. 6 seconds to have enough + // time for the first estimate to be generated and for Process() to be called. + for (int i = 0; i <= 6 * kFramerate; ++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_GE(bitrate_observer_->latest_bitrate(), 400000u); + + // 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, + -static_cast<int>(kTimestampGroupLengthAbsSendTime))); + bitrate_estimator_->Process(); + } + EXPECT_TRUE(bitrate_observer_->updated()); + // Should have reduced the estimate. + EXPECT_LT(bitrate_observer_->latest_bitrate(), 400000u); +} + +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(); + } + DataRate bitrate_before = bitrate_estimator_->LatestEstimate(); + + 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 < 21; ++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(); + } + DataRate bitrate_after = bitrate_estimator_->LatestEstimate(); + EXPECT_LT(bitrate_after, bitrate_before); +} +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h new file mode 100644 index 0000000000..a3b1cfdb34 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h @@ -0,0 +1,225 @@ +/* + * 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. + */ + +#ifndef MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_UNITTEST_HELPER_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_UNITTEST_HELPER_H_ + +#include <list> +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h" +#include "system_wrappers/include/clock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace testing { + +class TestBitrateObserver : public RemoteBitrateObserver { + public: + TestBitrateObserver() : updated_(false), latest_bitrate_(0) {} + virtual ~TestBitrateObserver() {} + + void OnReceiveBitrateChanged(const std::vector<uint32_t>& ssrcs, + uint32_t bitrate) override; + + void Reset() { updated_ = false; } + + bool updated() const { return updated_; } + + uint32_t latest_bitrate() const { return latest_bitrate_; } + + private: + bool updated_; + uint32_t latest_bitrate_; +}; + +class RtpStream { + public: + struct RtpPacket { + int64_t send_time; + int64_t arrival_time; + uint32_t rtp_timestamp; + size_t size; + uint32_t ssrc; + }; + + struct RtcpPacket { + uint32_t ntp_secs; + uint32_t ntp_frac; + uint32_t timestamp; + uint32_t ssrc; + }; + + typedef std::list<RtpPacket*> PacketList; + + enum { kSendSideOffsetUs = 1000000 }; + + RtpStream(int fps, + int bitrate_bps, + uint32_t ssrc, + uint32_t frequency, + uint32_t timestamp_offset, + int64_t rtcp_receive_time); + + RtpStream(const RtpStream&) = delete; + RtpStream& operator=(const RtpStream&) = delete; + + void set_rtp_timestamp_offset(uint32_t offset); + + // Generates a new frame for this stream. If called too soon after the + // previous frame, no frame will be generated. The frame is split into + // packets. + int64_t GenerateFrame(int64_t time_now_us, PacketList* packets); + + // The send-side time when the next frame can be generated. + int64_t next_rtp_time() const; + + // Generates an RTCP packet. + RtcpPacket* Rtcp(int64_t time_now_us); + + void set_bitrate_bps(int bitrate_bps); + + int bitrate_bps() const; + + uint32_t ssrc() const; + + static bool Compare(const std::pair<uint32_t, RtpStream*>& left, + const std::pair<uint32_t, RtpStream*>& right); + + private: + enum { kRtcpIntervalUs = 1000000 }; + + int fps_; + int bitrate_bps_; + uint32_t ssrc_; + uint32_t frequency_; + int64_t next_rtp_time_; + int64_t next_rtcp_time_; + uint32_t rtp_timestamp_offset_; + const double kNtpFracPerMs; +}; + +class StreamGenerator { + public: + typedef std::list<RtpStream::RtcpPacket*> RtcpList; + + StreamGenerator(int capacity, int64_t time_now); + + ~StreamGenerator(); + + StreamGenerator(const StreamGenerator&) = delete; + StreamGenerator& operator=(const StreamGenerator&) = delete; + + // Add a new stream. + void AddStream(RtpStream* stream); + + // Set the link capacity. + void set_capacity_bps(int capacity_bps); + + // Divides `bitrate_bps` among all streams. The allocated bitrate per stream + // is decided by the initial allocation ratios. + void SetBitrateBps(int bitrate_bps); + + // Set the RTP timestamp offset for the stream identified by `ssrc`. + void set_rtp_timestamp_offset(uint32_t ssrc, uint32_t offset); + + // TODO(holmer): Break out the channel simulation part from this class to make + // it possible to simulate different types of channels. + int64_t GenerateFrame(RtpStream::PacketList* packets, int64_t time_now_us); + + private: + typedef std::map<uint32_t, RtpStream*> StreamMap; + + // Capacity of the simulated channel in bits per second. + int capacity_; + // The time when the last packet arrived. + int64_t prev_arrival_time_us_; + // All streams being transmitted on this simulated channel. + StreamMap streams_; +}; +} // namespace testing + +class RemoteBitrateEstimatorTest : public ::testing::Test { + public: + RemoteBitrateEstimatorTest(); + virtual ~RemoteBitrateEstimatorTest(); + + RemoteBitrateEstimatorTest(const RemoteBitrateEstimatorTest&) = delete; + RemoteBitrateEstimatorTest& operator=(const RemoteBitrateEstimatorTest&) = + delete; + + protected: + virtual void SetUp() = 0; + + void AddDefaultStream(); + + // Helper to convert some time format to resolution used in absolute send time + // header extension, rounded upwards. `t` is the time to convert, in some + // resolution. `denom` is the value to divide `t` by to get whole seconds, + // e.g. `denom` = 1000 if `t` is in milliseconds. + static uint32_t AbsSendTime(int64_t t, int64_t denom); + + // Helper to add two absolute send time values and keep it less than 1<<24. + static uint32_t AddAbsSendTime(uint32_t t1, uint32_t t2); + + // Helper to create an RTPHeader containing the relevant data for the + // estimator (all other fields are cleared) and call IncomingPacket on the + // estimator. + void IncomingPacket(uint32_t ssrc, + size_t payload_size, + int64_t arrival_time, + uint32_t rtp_timestamp, + uint32_t absolute_send_time); + + // Generates a frame of packets belonging to a stream at a given bitrate and + // with a given ssrc. The stream is pushed through a very simple simulated + // network, and is then given to the receive-side bandwidth estimator. + // Returns true if an over-use was seen, false otherwise. + // The StreamGenerator::updated() should be used to check for any changes in + // target bitrate after the call to this function. + bool GenerateAndProcessFrame(uint32_t ssrc, uint32_t bitrate_bps); + + // Run the bandwidth estimator with a stream of `number_of_frames` frames, or + // until it reaches `target_bitrate`. + // Can for instance be used to run the estimator for some time to get it + // into a steady state. + uint32_t SteadyStateRun(uint32_t ssrc, + int number_of_frames, + uint32_t start_bitrate, + uint32_t min_bitrate, + uint32_t max_bitrate, + uint32_t target_bitrate); + + void TestTimestampGroupingTestHelper(); + + void TestWrappingHelper(int silence_time_s); + + void InitialBehaviorTestHelper(uint32_t expected_converge_bitrate); + void RateIncreaseReorderingTestHelper(uint32_t expected_bitrate); + void RateIncreaseRtpTimestampsTestHelper(int expected_iterations); + void CapacityDropTestHelper(int number_of_streams, + bool wrap_time_stamp, + uint32_t expected_bitrate_drop_delta, + int64_t receiver_clock_offset_change_ms); + + static const uint32_t kDefaultSsrc; + + SimulatedClock clock_; // Time at the receiver. + std::unique_ptr<testing::TestBitrateObserver> bitrate_observer_; + std::unique_ptr<RemoteBitrateEstimator> bitrate_estimator_; + std::unique_ptr<testing::StreamGenerator> stream_generator_; + int64_t arrival_time_offset_ms_; +}; +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_BITRATE_ESTIMATOR_UNITTEST_HELPER_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc new file mode 100644 index 0000000000..598279e0af --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2015 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 "modules/remote_bitrate_estimator/remote_estimator_proxy.h" + +#include <algorithm> +#include <limits> +#include <memory> +#include <utility> + +#include "api/units/data_size.h" +#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h" +#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/numerics/safe_minmax.h" +#include "system_wrappers/include/clock.h" + +namespace webrtc { +namespace { +// The maximum allowed value for a timestamp in milliseconds. This is lower +// than the numerical limit since we often convert to microseconds. +constexpr int64_t kMaxTimeMs = std::numeric_limits<int64_t>::max() / 1000; +constexpr TimeDelta kBackWindow = TimeDelta::Millis(500); +constexpr TimeDelta kMinInterval = TimeDelta::Millis(50); +constexpr TimeDelta kMaxInterval = TimeDelta::Millis(250); +constexpr TimeDelta kDefaultInterval = TimeDelta::Millis(100); + +TimeDelta GetAbsoluteSendTimeDelta(uint32_t new_sendtime, + uint32_t previous_sendtime) { + static constexpr uint32_t kWrapAroundPeriod = 0x0100'0000; + RTC_DCHECK_LT(new_sendtime, kWrapAroundPeriod); + RTC_DCHECK_LT(previous_sendtime, kWrapAroundPeriod); + uint32_t delta = (new_sendtime - previous_sendtime) % kWrapAroundPeriod; + if (delta >= kWrapAroundPeriod / 2) { + // absolute send time wraps around, thus treat deltas larger than half of + // the wrap around period as negative. Ignore reordering of packets and + // treat them as they have approximately the same send time. + return TimeDelta::Zero(); + } + return TimeDelta::Micros(int64_t{delta} * 1'000'000 / (1 << 18)); +} +} // namespace + +RemoteEstimatorProxy::RemoteEstimatorProxy( + TransportFeedbackSender feedback_sender, + NetworkStateEstimator* network_state_estimator) + : feedback_sender_(std::move(feedback_sender)), + last_process_time_(Timestamp::MinusInfinity()), + network_state_estimator_(network_state_estimator), + media_ssrc_(0), + feedback_packet_count_(0), + packet_overhead_(DataSize::Zero()), + send_interval_(kDefaultInterval), + send_periodic_feedback_(true), + previous_abs_send_time_(0), + abs_send_timestamp_(Timestamp::Zero()) { + RTC_LOG(LS_INFO) + << "Maximum interval between transport feedback RTCP messages: " + << kMaxInterval; +} + +RemoteEstimatorProxy::~RemoteEstimatorProxy() {} + +void RemoteEstimatorProxy::MaybeCullOldPackets(int64_t sequence_number, + Timestamp arrival_time) { + if (periodic_window_start_seq_ >= + packet_arrival_times_.end_sequence_number() && + arrival_time - Timestamp::Zero() >= kBackWindow) { + // Start new feedback packet, cull old packets. + packet_arrival_times_.RemoveOldPackets(sequence_number, + arrival_time - kBackWindow); + } +} + +void RemoteEstimatorProxy::IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header) { + if (arrival_time_ms < 0 || arrival_time_ms >= kMaxTimeMs) { + RTC_LOG(LS_WARNING) << "Arrival time out of bounds: " << arrival_time_ms; + return; + } + Packet packet = {.arrival_time = Timestamp::Millis(arrival_time_ms), + .size = DataSize::Bytes(header.headerLength + payload_size), + .ssrc = header.ssrc}; + if (header.extension.hasTransportSequenceNumber) { + packet.transport_sequence_number = header.extension.transportSequenceNumber; + } + if (header.extension.hasAbsoluteSendTime) { + packet.absolute_send_time_24bits = header.extension.absoluteSendTime; + } + packet.feedback_request = header.extension.feedback_request; + + IncomingPacket(packet); +} + +void RemoteEstimatorProxy::IncomingPacket(Packet packet) { + MutexLock lock(&lock_); + media_ssrc_ = packet.ssrc; + int64_t seq = 0; + + if (packet.transport_sequence_number.has_value()) { + seq = unwrapper_.Unwrap(*packet.transport_sequence_number); + + if (send_periodic_feedback_) { + MaybeCullOldPackets(seq, packet.arrival_time); + + if (!periodic_window_start_seq_ || seq < *periodic_window_start_seq_) { + periodic_window_start_seq_ = seq; + } + } + + // We are only interested in the first time a packet is received. + if (packet_arrival_times_.has_received(seq)) { + return; + } + + packet_arrival_times_.AddPacket(seq, packet.arrival_time); + + // Limit the range of sequence numbers to send feedback for. + if (!periodic_window_start_seq_.has_value() || + periodic_window_start_seq_.value() < + packet_arrival_times_.begin_sequence_number()) { + periodic_window_start_seq_ = + packet_arrival_times_.begin_sequence_number(); + } + + if (packet.feedback_request) { + // Send feedback packet immediately. + SendFeedbackOnRequest(seq, *packet.feedback_request); + } + } + + if (network_state_estimator_ && packet.absolute_send_time_24bits) { + PacketResult packet_result; + packet_result.receive_time = packet.arrival_time; + abs_send_timestamp_ += GetAbsoluteSendTimeDelta( + *packet.absolute_send_time_24bits, previous_abs_send_time_); + previous_abs_send_time_ = *packet.absolute_send_time_24bits; + packet_result.sent_packet.send_time = abs_send_timestamp_; + packet_result.sent_packet.size = packet.size + packet_overhead_; + packet_result.sent_packet.sequence_number = seq; + network_state_estimator_->OnReceivedPacket(packet_result); + } +} + +TimeDelta RemoteEstimatorProxy::Process(Timestamp now) { + MutexLock lock(&lock_); + if (!send_periodic_feedback_) { + return TimeDelta::PlusInfinity(); + } + Timestamp next_process_time = last_process_time_ + send_interval_; + if (now >= next_process_time) { + last_process_time_ = now; + SendPeriodicFeedbacks(); + return send_interval_; + } + + return next_process_time - now; +} + +void RemoteEstimatorProxy::OnBitrateChanged(int bitrate_bps) { + // TwccReportSize = Ipv4(20B) + UDP(8B) + SRTP(10B) + + // AverageTwccReport(30B) + // TwccReport size at 50ms interval is 24 byte. + // TwccReport size at 250ms interval is 36 byte. + // AverageTwccReport = (TwccReport(50ms) + TwccReport(250ms)) / 2 + constexpr DataSize kTwccReportSize = DataSize::Bytes(20 + 8 + 10 + 30); + constexpr DataRate kMinTwccRate = kTwccReportSize / kMaxInterval; + + // Let TWCC reports occupy 5% of total bandwidth. + DataRate twcc_bitrate = DataRate::BitsPerSec(0.05 * bitrate_bps); + + // Check upper send_interval bound by checking bitrate to avoid overflow when + // dividing by small bitrate, in particular avoid dividing by zero bitrate. + TimeDelta send_interval = + twcc_bitrate <= kMinTwccRate + ? kMaxInterval + : std::max(kTwccReportSize / twcc_bitrate, kMinInterval); + + MutexLock lock(&lock_); + send_interval_ = send_interval; +} + +void RemoteEstimatorProxy::SetSendPeriodicFeedback( + bool send_periodic_feedback) { + MutexLock lock(&lock_); + send_periodic_feedback_ = send_periodic_feedback; +} + +void RemoteEstimatorProxy::SetTransportOverhead(DataSize overhead_per_packet) { + MutexLock lock(&lock_); + packet_overhead_ = overhead_per_packet; +} + +void RemoteEstimatorProxy::SendPeriodicFeedbacks() { + // `periodic_window_start_seq_` is the first sequence number to include in + // the current feedback packet. Some older may still be in the map, in case + // a reordering happens and we need to retransmit them. + if (!periodic_window_start_seq_) + return; + + std::unique_ptr<rtcp::RemoteEstimate> remote_estimate; + if (network_state_estimator_) { + absl::optional<NetworkStateEstimate> state_estimate = + network_state_estimator_->GetCurrentEstimate(); + if (state_estimate) { + remote_estimate = std::make_unique<rtcp::RemoteEstimate>(); + remote_estimate->SetEstimate(state_estimate.value()); + } + } + + int64_t packet_arrival_times_end_seq = + packet_arrival_times_.end_sequence_number(); + while (periodic_window_start_seq_ < packet_arrival_times_end_seq) { + auto feedback_packet = MaybeBuildFeedbackPacket( + /*include_timestamps=*/true, periodic_window_start_seq_.value(), + packet_arrival_times_end_seq, + /*is_periodic_update=*/true); + + if (feedback_packet == nullptr) { + break; + } + + RTC_DCHECK(feedback_sender_ != nullptr); + + std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets; + if (remote_estimate) { + packets.push_back(std::move(remote_estimate)); + } + packets.push_back(std::move(feedback_packet)); + + feedback_sender_(std::move(packets)); + // Note: Don't erase items from packet_arrival_times_ after sending, in + // case they need to be re-sent after a reordering. Removal will be + // handled by OnPacketArrival once packets are too old. + } +} + +void RemoteEstimatorProxy::SendFeedbackOnRequest( + int64_t sequence_number, + const FeedbackRequest& feedback_request) { + if (feedback_request.sequence_count == 0) { + return; + } + + int64_t first_sequence_number = + sequence_number - feedback_request.sequence_count + 1; + + auto feedback_packet = MaybeBuildFeedbackPacket( + feedback_request.include_timestamps, first_sequence_number, + sequence_number + 1, /*is_periodic_update=*/false); + + // This is called when a packet has just been added. + RTC_DCHECK(feedback_packet != nullptr); + + // Clear up to the first packet that is included in this feedback packet. + packet_arrival_times_.EraseTo(first_sequence_number); + + RTC_DCHECK(feedback_sender_ != nullptr); + std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets; + packets.push_back(std::move(feedback_packet)); + feedback_sender_(std::move(packets)); +} + +std::unique_ptr<rtcp::TransportFeedback> +RemoteEstimatorProxy::MaybeBuildFeedbackPacket( + bool include_timestamps, + int64_t begin_sequence_number_inclusive, + int64_t end_sequence_number_exclusive, + bool is_periodic_update) { + RTC_DCHECK_LT(begin_sequence_number_inclusive, end_sequence_number_exclusive); + + int64_t start_seq = + packet_arrival_times_.clamp(begin_sequence_number_inclusive); + + int64_t end_seq = packet_arrival_times_.clamp(end_sequence_number_exclusive); + + // Create the packet on demand, as it's not certain that there are packets + // in the range that have been received. + std::unique_ptr<rtcp::TransportFeedback> feedback_packet = nullptr; + + int64_t next_sequence_number = begin_sequence_number_inclusive; + + for (int64_t seq = start_seq; seq < end_seq; ++seq) { + PacketArrivalTimeMap::PacketArrivalTime packet = + packet_arrival_times_.FindNextAtOrAfter(seq); + seq = packet.sequence_number; + if (seq >= end_seq) { + break; + } + + if (feedback_packet == nullptr) { + feedback_packet = + std::make_unique<rtcp::TransportFeedback>(include_timestamps); + feedback_packet->SetMediaSsrc(media_ssrc_); + // Base sequence number is the expected first sequence number. This is + // known, but we might not have actually received it, so the base time + // shall be the time of the first received packet in the feedback. + feedback_packet->SetBase( + static_cast<uint16_t>(begin_sequence_number_inclusive & 0xFFFF), + packet.arrival_time); + feedback_packet->SetFeedbackSequenceNumber(feedback_packet_count_++); + } + + if (!feedback_packet->AddReceivedPacket(static_cast<uint16_t>(seq & 0xFFFF), + packet.arrival_time)) { + // Could not add timestamp, feedback packet might be full. Return and + // try again with a fresh packet. + break; + } + + next_sequence_number = seq + 1; + } + if (is_periodic_update) { + periodic_window_start_seq_ = next_sequence_number; + } + return feedback_packet; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h new file mode 100644 index 0000000000..54257ea6f0 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015 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 MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_ + +#include <deque> +#include <functional> +#include <memory> +#include <vector> + +#include "absl/types/optional.h" +#include "api/field_trials_view.h" +#include "api/rtp_headers.h" +#include "api/transport/network_control.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/remote_bitrate_estimator/packet_arrival_map.h" +#include "modules/rtp_rtcp/source/rtcp_packet.h" +#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "rtc_base/numerics/sequence_number_unwrapper.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { + +// Class used when send-side BWE is enabled: This proxy is instantiated on the +// receive side. It buffers a number of receive timestamps and then sends +// transport feedback messages back too the send side. +class RemoteEstimatorProxy { + public: + // Used for sending transport feedback messages when send side + // BWE is used. + using TransportFeedbackSender = std::function<void( + std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets)>; + RemoteEstimatorProxy(TransportFeedbackSender feedback_sender, + NetworkStateEstimator* network_state_estimator); + ~RemoteEstimatorProxy(); + + struct Packet { + Timestamp arrival_time; + DataSize size; + uint32_t ssrc; + absl::optional<uint32_t> absolute_send_time_24bits; + absl::optional<uint16_t> transport_sequence_number; + absl::optional<FeedbackRequest> feedback_request; + }; + void IncomingPacket(Packet packet); + + void IncomingPacket(int64_t arrival_time_ms, + size_t payload_size, + const RTPHeader& header); + + // Sends periodic feedback if it is time to send it. + // Returns time until next call to Process should be made. + TimeDelta Process(Timestamp now); + + void OnBitrateChanged(int bitrate); + void SetSendPeriodicFeedback(bool send_periodic_feedback); + void SetTransportOverhead(DataSize overhead_per_packet); + + private: + void MaybeCullOldPackets(int64_t sequence_number, Timestamp arrival_time) + RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); + void SendPeriodicFeedbacks() RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); + void SendFeedbackOnRequest(int64_t sequence_number, + const FeedbackRequest& feedback_request) + RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); + + // Returns a Transport Feedback packet with information about as many packets + // that has been received between [`begin_sequence_number_incl`, + // `end_sequence_number_excl`) that can fit in it. If `is_periodic_update`, + // this represents sending a periodic feedback message, which will make it + // update the `periodic_window_start_seq_` variable with the first packet that + // was not included in the feedback packet, so that the next update can + // continue from that sequence number. + // + // If no incoming packets were added, nullptr is returned. + // + // `include_timestamps` decide if the returned TransportFeedback should + // include timestamps. + std::unique_ptr<rtcp::TransportFeedback> MaybeBuildFeedbackPacket( + bool include_timestamps, + int64_t begin_sequence_number_inclusive, + int64_t end_sequence_number_exclusive, + bool is_periodic_update) RTC_EXCLUSIVE_LOCKS_REQUIRED(&lock_); + + const TransportFeedbackSender feedback_sender_; + Timestamp last_process_time_; + + Mutex lock_; + // `network_state_estimator_` may be null. + NetworkStateEstimator* const network_state_estimator_ + RTC_PT_GUARDED_BY(&lock_); + uint32_t media_ssrc_ RTC_GUARDED_BY(&lock_); + uint8_t feedback_packet_count_ RTC_GUARDED_BY(&lock_); + SeqNumUnwrapper<uint16_t> unwrapper_ RTC_GUARDED_BY(&lock_); + DataSize packet_overhead_ RTC_GUARDED_BY(&lock_); + + // The next sequence number that should be the start sequence number during + // periodic reporting. Will be absl::nullopt before the first seen packet. + absl::optional<int64_t> periodic_window_start_seq_ RTC_GUARDED_BY(&lock_); + + // Packet arrival times, by sequence number. + PacketArrivalTimeMap packet_arrival_times_ RTC_GUARDED_BY(&lock_); + + TimeDelta send_interval_ RTC_GUARDED_BY(&lock_); + bool send_periodic_feedback_ RTC_GUARDED_BY(&lock_); + + // Unwraps absolute send times. + uint32_t previous_abs_send_time_ RTC_GUARDED_BY(&lock_); + Timestamp abs_send_timestamp_ RTC_GUARDED_BY(&lock_); +}; + +} // namespace webrtc + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_REMOTE_ESTIMATOR_PROXY_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc new file mode 100644 index 0000000000..437fd6f1e2 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2015 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 "modules/remote_bitrate_estimator/remote_estimator_proxy.h" + +#include <memory> +#include <utility> + +#include "api/transport/network_types.h" +#include "api/transport/test/mock_network_control.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "system_wrappers/include/clock.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Invoke; +using ::testing::MockFunction; +using ::testing::Return; +using ::testing::SizeIs; + +constexpr DataSize kDefaultPacketSize = DataSize::Bytes(100); +constexpr uint32_t kMediaSsrc = 456; +constexpr uint16_t kBaseSeq = 10; +constexpr Timestamp kBaseTime = Timestamp::Millis(123); +constexpr TimeDelta kBaseTimeWrapAround = + rtcp::TransportFeedback::kDeltaTick * (int64_t{1} << 32); +constexpr TimeDelta kMaxSmallDelta = rtcp::TransportFeedback::kDeltaTick * 0xFF; + +constexpr TimeDelta kBackWindow = TimeDelta::Millis(500); +constexpr TimeDelta kMinSendInterval = TimeDelta::Millis(50); +constexpr TimeDelta kMaxSendInterval = TimeDelta::Millis(250); +constexpr TimeDelta kDefaultSendInterval = TimeDelta::Millis(100); + +std::vector<uint16_t> SequenceNumbers( + const rtcp::TransportFeedback& feedback_packet) { + std::vector<uint16_t> sequence_numbers; + for (const auto& rtp_packet_received : feedback_packet.GetReceivedPackets()) { + sequence_numbers.push_back(rtp_packet_received.sequence_number()); + } + return sequence_numbers; +} + +std::vector<Timestamp> Timestamps( + const rtcp::TransportFeedback& feedback_packet) { + std::vector<Timestamp> timestamps; + Timestamp timestamp = feedback_packet.BaseTime(); + // rtcp::TransportFeedback makes no promises about epoch of the base time, + // It may add several kBaseTimeWrapAround periods to make it large enough and + // thus to support negative deltas. Align it close to the kBaseTime to make + // tests expectations simpler. + if (timestamp > kBaseTime) { + timestamp -= (timestamp - kBaseTime).RoundTo(kBaseTimeWrapAround); + } + for (const auto& rtp_packet_received : feedback_packet.GetReceivedPackets()) { + timestamp += rtp_packet_received.delta(); + timestamps.push_back(timestamp); + } + return timestamps; +} + +class RemoteEstimatorProxyTest : public ::testing::Test { + public: + RemoteEstimatorProxyTest() + : clock_(0), + proxy_(feedback_sender_.AsStdFunction(), &network_state_estimator_) {} + + protected: + void IncomingPacket( + uint16_t seq, + Timestamp arrival_time, + absl::optional<FeedbackRequest> feedback_request = absl::nullopt) { + proxy_.IncomingPacket({.arrival_time = arrival_time, + .size = DataSize::Bytes(100), + .ssrc = kMediaSsrc, + .transport_sequence_number = seq, + .feedback_request = feedback_request}); + } + + void Process() { + clock_.AdvanceTime(kDefaultSendInterval); + proxy_.Process(clock_.CurrentTime()); + } + + SimulatedClock clock_; + MockFunction<void(std::vector<std::unique_ptr<rtcp::RtcpPacket>>)> + feedback_sender_; + ::testing::NiceMock<MockNetworkStateEstimator> network_state_estimator_; + RemoteEstimatorProxy proxy_; +}; + +TEST_F(RemoteEstimatorProxyTest, SendsSinglePacketFeedback) { + IncomingPacket(kBaseSeq, kBaseTime); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq)); + EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime)); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, DuplicatedPackets) { + IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacket(kBaseSeq, kBaseTime + TimeDelta::Seconds(1)); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq)); + EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime)); + return true; + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, FeedbackWithMissingStart) { + // First feedback. + IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacket(kBaseSeq + 1, kBaseTime + TimeDelta::Seconds(1)); + EXPECT_CALL(feedback_sender_, Call); + Process(); + + // Second feedback starts with a missing packet (DROP kBaseSeq + 2). + IncomingPacket(kBaseSeq + 3, kBaseTime + TimeDelta::Seconds(3)); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 2, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 3)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + TimeDelta::Seconds(3))); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, SendsFeedbackWithVaryingDeltas) { + IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacket(kBaseSeq + 1, kBaseTime + kMaxSmallDelta); + IncomingPacket(kBaseSeq + 2, + kBaseTime + (2 * kMaxSmallDelta) + TimeDelta::Millis(1)); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq, kBaseSeq + 1, kBaseSeq + 2)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime, kBaseTime + kMaxSmallDelta, + kBaseTime + (2 * kMaxSmallDelta) + + TimeDelta::Millis(1))); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, SendsFragmentedFeedback) { + static constexpr TimeDelta kTooLargeDelta = + rtcp::TransportFeedback::kDeltaTick * (1 << 16); + + IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacket(kBaseSeq + 1, kBaseTime + kTooLargeDelta); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq)); + EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime)); + })) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 1, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 1)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + kTooLargeDelta)); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, HandlesReorderingAndWrap) { + const TimeDelta kDelta = TimeDelta::Seconds(1); + const uint16_t kLargeSeq = 62762; + IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacket(kLargeSeq, kBaseTime + kDelta); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kLargeSeq, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + kDelta, kBaseTime)); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, HandlesMalformedSequenceNumbers) { + // This test generates incoming packets with large jumps in sequence numbers. + // When unwrapped, the sequeunce numbers of these 30 incoming packets, will + // span a range of roughly 650k packets. Test that we only send feedback for + // the last packets. Test for regression found in chromium:949020. + const TimeDelta kDelta = TimeDelta::Seconds(1); + for (int i = 0; i < 10; ++i) { + IncomingPacket(kBaseSeq + i, kBaseTime + 3 * i * kDelta); + IncomingPacket(kBaseSeq + 20000 + i, kBaseTime + (3 * i + 1) * kDelta); + IncomingPacket(kBaseSeq + 40000 + i, kBaseTime + (3 * i + 2) * kDelta); + } + + // Only expect feedback for the last two packets. + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 20009, kBaseSeq + 40009)); + EXPECT_THAT( + Timestamps(*feedback_packet), + ElementsAre(kBaseTime + 28 * kDelta, kBaseTime + 29 * kDelta)); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, HandlesBackwardsWrappingSequenceNumbers) { + // This test is like HandlesMalformedSequenceNumbers but for negative wrap + // arounds. Test that we only send feedback for the packets with highest + // sequence numbers. Test for regression found in chromium:949020. + const TimeDelta kDelta = TimeDelta::Seconds(1); + for (int i = 0; i < 10; ++i) { + IncomingPacket(kBaseSeq + i, kBaseTime + 3 * i * kDelta); + IncomingPacket(kBaseSeq + 40000 + i, kBaseTime + (3 * i + 1) * kDelta); + IncomingPacket(kBaseSeq + 20000 + i, kBaseTime + (3 * i + 2) * kDelta); + } + + // Only expect feedback for the first two packets. + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 40000, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 40000, kBaseSeq)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + kDelta, kBaseTime)); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, ResendsTimestampsOnReordering) { + IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacket(kBaseSeq + 2, kBaseTime + TimeDelta::Millis(2)); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq, kBaseSeq + 2)); + EXPECT_THAT( + Timestamps(*feedback_packet), + ElementsAre(kBaseTime, kBaseTime + TimeDelta::Millis(2))); + })); + + Process(); + + IncomingPacket(kBaseSeq + 1, kBaseTime + TimeDelta::Millis(1)); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 1, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 1, kBaseSeq + 2)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + TimeDelta::Millis(1), + kBaseTime + TimeDelta::Millis(2))); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, RemovesTimestampsOutOfScope) { + const Timestamp kTimeoutTime = kBaseTime + kBackWindow; + + IncomingPacket(kBaseSeq + 2, kBaseTime); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 2, feedback_packet->GetBaseSequence()); + + EXPECT_THAT(Timestamps(*feedback_packet), ElementsAre(kBaseTime)); + })); + + Process(); + + IncomingPacket(kBaseSeq + 3, kTimeoutTime); // kBaseSeq + 2 times out here. + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 3, feedback_packet->GetBaseSequence()); + + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kTimeoutTime)); + })); + + Process(); + + // New group, with sequence starting below the first so that they may be + // retransmitted. + IncomingPacket(kBaseSeq, kBaseTime - TimeDelta::Millis(1)); + IncomingPacket(kBaseSeq + 1, kTimeoutTime - TimeDelta::Millis(1)); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [&](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq, feedback_packet->GetBaseSequence()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq, kBaseSeq + 1, kBaseSeq + 3)); + EXPECT_THAT( + Timestamps(*feedback_packet), + ElementsAre(kBaseTime - TimeDelta::Millis(1), + kTimeoutTime - TimeDelta::Millis(1), kTimeoutTime)); + })); + + Process(); +} + +TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsDefaultOnUnkownBitrate) { + EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kDefaultSendInterval); +} + +TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsMinIntervalOn300kbps) { + proxy_.OnBitrateChanged(300'000); + EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kMinSendInterval); +} + +TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsMaxIntervalOn0kbps) { + // TimeUntilNextProcess should be limited by `kMaxSendIntervalMs` when + // bitrate is small. We choose 0 bps as a special case, which also tests + // erroneous behaviors like division-by-zero. + proxy_.OnBitrateChanged(0); + EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kMaxSendInterval); +} + +TEST_F(RemoteEstimatorProxyTest, TimeUntilNextProcessIsMaxIntervalOn20kbps) { + proxy_.OnBitrateChanged(20'000); + EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), kMaxSendInterval); +} + +TEST_F(RemoteEstimatorProxyTest, TwccReportsUse5PercentOfAvailableBandwidth) { + proxy_.OnBitrateChanged(80'000); + // 80kbps * 0.05 = TwccReportSize(68B * 8b/B) * 1000ms / SendInterval(136ms) + EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), TimeDelta::Millis(136)); +} + +////////////////////////////////////////////////////////////////////////////// +// Tests for the extended protocol where the feedback is explicitly requested +// by the sender. +////////////////////////////////////////////////////////////////////////////// +typedef RemoteEstimatorProxyTest RemoteEstimatorProxyOnRequestTest; +TEST_F(RemoteEstimatorProxyOnRequestTest, DisablesPeriodicProcess) { + proxy_.SetSendPeriodicFeedback(false); + EXPECT_EQ(proxy_.Process(clock_.CurrentTime()), TimeDelta::PlusInfinity()); +} + +TEST_F(RemoteEstimatorProxyOnRequestTest, ProcessDoesNotSendFeedback) { + proxy_.SetSendPeriodicFeedback(false); + IncomingPacket(kBaseSeq, kBaseTime); + EXPECT_CALL(feedback_sender_, Call).Times(0); + Process(); +} + +TEST_F(RemoteEstimatorProxyOnRequestTest, RequestSinglePacketFeedback) { + proxy_.SetSendPeriodicFeedback(false); + IncomingPacket(kBaseSeq, kBaseTime); + IncomingPacket(kBaseSeq + 1, kBaseTime + kMaxSmallDelta); + IncomingPacket(kBaseSeq + 2, kBaseTime + 2 * kMaxSmallDelta); + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 3, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 3)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + 3 * kMaxSmallDelta)); + })); + + constexpr FeedbackRequest kSinglePacketFeedbackRequest = { + /*include_timestamps=*/true, /*sequence_count=*/1}; + IncomingPacket(kBaseSeq + 3, kBaseTime + 3 * kMaxSmallDelta, + kSinglePacketFeedbackRequest); +} + +TEST_F(RemoteEstimatorProxyOnRequestTest, RequestLastFivePacketFeedback) { + proxy_.SetSendPeriodicFeedback(false); + int i = 0; + for (; i < 10; ++i) { + IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta); + } + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 6, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 6, kBaseSeq + 7, kBaseSeq + 8, + kBaseSeq + 9, kBaseSeq + 10)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + 6 * kMaxSmallDelta, + kBaseTime + 7 * kMaxSmallDelta, + kBaseTime + 8 * kMaxSmallDelta, + kBaseTime + 9 * kMaxSmallDelta, + kBaseTime + 10 * kMaxSmallDelta)); + })); + + constexpr FeedbackRequest kFivePacketsFeedbackRequest = { + /*include_timestamps=*/true, /*sequence_count=*/5}; + IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta, + kFivePacketsFeedbackRequest); +} + +TEST_F(RemoteEstimatorProxyOnRequestTest, + RequestLastFivePacketFeedbackMissingPackets) { + proxy_.SetSendPeriodicFeedback(false); + int i = 0; + for (; i < 10; ++i) { + if (i != 7 && i != 9) + IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta); + } + + EXPECT_CALL(feedback_sender_, Call) + .WillOnce(Invoke( + [](std::vector<std::unique_ptr<rtcp::RtcpPacket>> feedback_packets) { + rtcp::TransportFeedback* feedback_packet = + static_cast<rtcp::TransportFeedback*>( + feedback_packets[0].get()); + EXPECT_EQ(kBaseSeq + 6, feedback_packet->GetBaseSequence()); + EXPECT_EQ(kMediaSsrc, feedback_packet->media_ssrc()); + + EXPECT_THAT(SequenceNumbers(*feedback_packet), + ElementsAre(kBaseSeq + 6, kBaseSeq + 8, kBaseSeq + 10)); + EXPECT_THAT(Timestamps(*feedback_packet), + ElementsAre(kBaseTime + 6 * kMaxSmallDelta, + kBaseTime + 8 * kMaxSmallDelta, + kBaseTime + 10 * kMaxSmallDelta)); + })); + + constexpr FeedbackRequest kFivePacketsFeedbackRequest = { + /*include_timestamps=*/true, /*sequence_count=*/5}; + IncomingPacket(kBaseSeq + i, kBaseTime + i * kMaxSmallDelta, + kFivePacketsFeedbackRequest); +} + +TEST_F(RemoteEstimatorProxyTest, ReportsIncomingPacketToNetworkStateEstimator) { + Timestamp first_send_timestamp = Timestamp::Zero(); + const DataSize kPacketOverhead = DataSize::Bytes(38); + proxy_.SetTransportOverhead(kPacketOverhead); + + EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_)) + .WillOnce(Invoke([&](const PacketResult& packet) { + EXPECT_EQ(packet.receive_time, kBaseTime); + EXPECT_EQ(packet.sent_packet.size, + kDefaultPacketSize + kPacketOverhead); + first_send_timestamp = packet.sent_packet.send_time; + })); + // Incoming packet with abs sendtime but without transport sequence number. + proxy_.IncomingPacket( + {.arrival_time = kBaseTime, + .size = kDefaultPacketSize, + .ssrc = kMediaSsrc, + .absolute_send_time_24bits = AbsoluteSendTime::To24Bits(kBaseTime)}); + + // Expect packet with older abs send time to be treated as sent at the same + // time as the previous packet due to reordering. + EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_)) + .WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) { + EXPECT_EQ(packet.receive_time, kBaseTime); + EXPECT_EQ(packet.sent_packet.send_time, first_send_timestamp); + })); + + proxy_.IncomingPacket( + {.arrival_time = kBaseTime, + .size = kDefaultPacketSize, + .ssrc = kMediaSsrc, + .absolute_send_time_24bits = + AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(12))}); +} + +TEST_F(RemoteEstimatorProxyTest, IncomingPacketHandlesWrapInAbsSendTime) { + // abs send time use 24bit precision. + const uint32_t kFirstAbsSendTime = + AbsoluteSendTime::To24Bits(Timestamp::Millis((1 << 24) - 30)); + // Second abs send time has wrapped. + const uint32_t kSecondAbsSendTime = + AbsoluteSendTime::To24Bits(Timestamp::Millis(1 << 24)); + const TimeDelta kExpectedAbsSendTimeDelta = TimeDelta::Millis(30); + + Timestamp first_send_timestamp = Timestamp::Zero(); + EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_)) + .WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) { + EXPECT_EQ(packet.receive_time, kBaseTime); + first_send_timestamp = packet.sent_packet.send_time; + })); + proxy_.IncomingPacket({.arrival_time = kBaseTime, + .size = kDefaultPacketSize, + .ssrc = kMediaSsrc, + .absolute_send_time_24bits = kFirstAbsSendTime, + .transport_sequence_number = kBaseSeq}); + + EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_)) + .WillOnce(Invoke([first_send_timestamp, + kExpectedAbsSendTimeDelta](const PacketResult& packet) { + EXPECT_EQ(packet.receive_time, kBaseTime + TimeDelta::Millis(123)); + EXPECT_EQ(packet.sent_packet.send_time.ms(), + (first_send_timestamp + kExpectedAbsSendTimeDelta).ms()); + })); + proxy_.IncomingPacket({.arrival_time = kBaseTime + TimeDelta::Millis(123), + .size = kDefaultPacketSize, + .ssrc = kMediaSsrc, + .absolute_send_time_24bits = kSecondAbsSendTime, + .transport_sequence_number = kBaseSeq + 1}); +} + +TEST_F(RemoteEstimatorProxyTest, SendTransportFeedbackAndNetworkStateUpdate) { + proxy_.IncomingPacket( + {.arrival_time = kBaseTime, + .size = kDefaultPacketSize, + .ssrc = kMediaSsrc, + .absolute_send_time_24bits = + AbsoluteSendTime::To24Bits(kBaseTime - TimeDelta::Millis(1)), + .transport_sequence_number = kBaseSeq}); + EXPECT_CALL(network_state_estimator_, GetCurrentEstimate()) + .WillOnce(Return(NetworkStateEstimate())); + EXPECT_CALL(feedback_sender_, Call(SizeIs(2))); + Process(); +} + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc new file mode 100644 index 0000000000..c8f6faa127 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc @@ -0,0 +1,262 @@ +/* + * 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 "modules/remote_bitrate_estimator/test/bwe_test_logging.h" + +#if BWE_TEST_LOGGING_COMPILE_TIME_ENABLE + +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> + +#include <algorithm> + +#include "rtc_base/checks.h" +#include "rtc_base/platform_thread.h" +#include "rtc_base/strings/string_builder.h" + +namespace webrtc { +namespace testing { +namespace bwe { + +static std::string ToString(uint32_t v) { + rtc::StringBuilder ss; + ss << v; + return ss.Release(); +} + +Logging::ThreadState::ThreadState() = default; +Logging::ThreadState::~ThreadState() = default; + +Logging::Context::Context(uint32_t name, int64_t timestamp_ms, bool enabled) { + Logging::GetInstance()->PushState(ToString(name), timestamp_ms, enabled); +} + +Logging::Context::Context(const std::string& name, + int64_t timestamp_ms, + bool enabled) { + Logging::GetInstance()->PushState(name, timestamp_ms, enabled); +} + +Logging::Context::Context(const char* name, + int64_t timestamp_ms, + bool enabled) { + Logging::GetInstance()->PushState(name, timestamp_ms, enabled); +} + +Logging::Context::~Context() { + Logging::GetInstance()->PopState(); +} + +Logging* Logging::GetInstance() { + static Logging* logging = new Logging(); + return logging; +} + +void Logging::SetGlobalContext(uint32_t name) { + MutexLock lock(&mutex_); + thread_map_[rtc::CurrentThreadId()].global_state.tag = ToString(name); +} + +void Logging::SetGlobalContext(const std::string& name) { + MutexLock lock(&mutex_); + thread_map_[rtc::CurrentThreadId()].global_state.tag = name; +} + +void Logging::SetGlobalContext(const char* name) { + MutexLock lock(&mutex_); + thread_map_[rtc::CurrentThreadId()].global_state.tag = name; +} + +void Logging::SetGlobalEnable(bool enabled) { + MutexLock lock(&mutex_); + thread_map_[rtc::CurrentThreadId()].global_state.enabled = enabled; +} + +void Logging::Log(const char format[], ...) { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + const State& state = it->second.stack.top(); + if (state.enabled) { + printf("%s\t", state.tag.c_str()); + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + } +} + +void Logging::Plot(int figure, const std::string& name, double value) { + Plot(figure, name, value, 0, "-"); +} + +void Logging::Plot(int figure, + const std::string& name, + double value, + uint32_t ssrc) { + Plot(figure, name, value, ssrc, "-"); +} + +void Logging::Plot(int figure, + const std::string& name, + double value, + const std::string& alg_name) { + Plot(figure, name, value, 0, alg_name); +} + +void Logging::Plot(int figure, + const std::string& name, + double value, + uint32_t ssrc, + const std::string& alg_name) { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + const State& state = it->second.stack.top(); + if (state.enabled) { + printf("PLOT\t%d\t%s:%" PRIu32 "@%s\t%f\t%f\n", figure, name.c_str(), ssrc, + alg_name.c_str(), state.timestamp_ms * 0.001, value); + } +} + +void Logging::PlotBar(int figure, + const std::string& name, + double value, + int flow_id) { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + const State& state = it->second.stack.top(); + if (state.enabled) { + printf("BAR\t%d\t%s_%d\t%f\n", figure, name.c_str(), flow_id, value); + } +} + +void Logging::PlotBaselineBar(int figure, + const std::string& name, + double value, + int flow_id) { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + const State& state = it->second.stack.top(); + if (state.enabled) { + printf("BASELINE\t%d\t%s_%d\t%f\n", figure, name.c_str(), flow_id, value); + } +} + +void Logging::PlotErrorBar(int figure, + const std::string& name, + double value, + double ylow, + double yhigh, + const std::string& error_title, + int flow_id) { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + const State& state = it->second.stack.top(); + if (state.enabled) { + printf("ERRORBAR\t%d\t%s_%d\t%f\t%f\t%f\t%s\n", figure, name.c_str(), + flow_id, value, ylow, yhigh, error_title.c_str()); + } +} + +void Logging::PlotLimitErrorBar(int figure, + const std::string& name, + double value, + double ylow, + double yhigh, + const std::string& error_title, + double ymax, + const std::string& limit_title, + int flow_id) { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + const State& state = it->second.stack.top(); + if (state.enabled) { + printf("LIMITERRORBAR\t%d\t%s_%d\t%f\t%f\t%f\t%s\t%f\t%s\n", figure, + name.c_str(), flow_id, value, ylow, yhigh, error_title.c_str(), ymax, + limit_title.c_str()); + } +} + +void Logging::PlotLabel(int figure, + const std::string& title, + const std::string& y_label, + int num_flows) { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + const State& state = it->second.stack.top(); + if (state.enabled) { + printf("LABEL\t%d\t%s\t%s\t%d\n", figure, title.c_str(), y_label.c_str(), + num_flows); + } +} + +Logging::Logging() : thread_map_() {} + +Logging::~Logging() = default; + +Logging::State::State() : tag(""), timestamp_ms(0), enabled(true) {} + +Logging::State::State(const std::string& tag, + int64_t timestamp_ms, + bool enabled) + : tag(tag), timestamp_ms(timestamp_ms), enabled(enabled) {} + +void Logging::State::MergePrevious(const State& previous) { + if (tag.empty()) { + tag = previous.tag; + } else if (!previous.tag.empty()) { + tag = previous.tag + "_" + tag; + } + timestamp_ms = std::max(previous.timestamp_ms, timestamp_ms); + enabled = previous.enabled && enabled; +} + +void Logging::PushState(const std::string& append_to_tag, + int64_t timestamp_ms, + bool enabled) { + MutexLock lock(&mutex_); + State new_state(append_to_tag, timestamp_ms, enabled); + ThreadState* thread_state = &thread_map_[rtc::CurrentThreadId()]; + std::stack<State>* stack = &thread_state->stack; + if (stack->empty()) { + new_state.MergePrevious(thread_state->global_state); + } else { + new_state.MergePrevious(stack->top()); + } + stack->push(new_state); +} + +void Logging::PopState() { + MutexLock lock(&mutex_); + ThreadMap::iterator it = thread_map_.find(rtc::CurrentThreadId()); + RTC_DCHECK(it != thread_map_.end()); + std::stack<State>* stack = &it->second.stack; + int64_t newest_timestamp_ms = stack->top().timestamp_ms; + stack->pop(); + if (!stack->empty()) { + State* state = &stack->top(); + // Update time so that next log/plot will use the latest time seen so far + // in this call tree. + state->timestamp_ms = std::max(state->timestamp_ms, newest_timestamp_ms); + } +} +} // namespace bwe +} // namespace testing +} // namespace webrtc + +#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h new file mode 100644 index 0000000000..49e1e716b2 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h @@ -0,0 +1,360 @@ +/* + * 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 MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_LOGGING_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_LOGGING_H_ + +// To enable BWE logging, run this command from trunk/ : +// build/gyp_chromium --depth=. webrtc/modules/modules.gyp +// -Denable_bwe_test_logging=1 +#ifndef BWE_TEST_LOGGING_COMPILE_TIME_ENABLE +#define BWE_TEST_LOGGING_COMPILE_TIME_ENABLE 0 +#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE + +// BWE logging allows you to insert dynamically named log/plot points in the +// call tree. E.g. the function: +// void f1() { +// BWE_TEST_LOGGING_TIME(clock_->TimeInMilliseconds()); +// BWE_TEST_LOGGING_CONTEXT("stream"); +// for (uint32_t i=0; i<4; ++i) { +// BWE_TEST_LOGGING_ENABLE(i & 1); +// BWE_TEST_LOGGING_CONTEXT(i); +// BWE_TEST_LOGGING_LOG1("weight", "%f tonnes", weights_[i]); +// for (float j=0.0f; j<1.0; j+=0.4f) { +// BWE_TEST_LOGGING_PLOT(0, "bps", -1, j); +// } +// } +// } +// +// Might produce the output: +// stream_00000001_weight 13.000000 tonnes +// PLOT stream_00000001_bps 1.000000 0.000000 +// PLOT stream_00000001_bps 1.000000 0.400000 +// PLOT stream_00000001_bps 1.000000 0.800000 +// stream_00000003_weight 39.000000 tonnes +// PLOT stream_00000003_bps 1.000000 0.000000 +// PLOT stream_00000003_bps 1.000000 0.400000 +// PLOT stream_00000003_bps 1.000000 0.800000 +// +// Log *contexts* are names concatenated with '_' between them, with the name +// of the logged/plotted string/value last. Plot *time* is inherited down the +// tree. A branch is enabled by default but can be *disabled* to reduce output. +// The difference between the RTC_LOG and PLOT macros is that PLOT prefixes the +// line so it can be easily filtered, plus it outputs the current time. + +#if !(BWE_TEST_LOGGING_COMPILE_TIME_ENABLE) + +// Set a thread-global base logging context. This name will be prepended to all +// hierarchical contexts. +// `name` is a char*, std::string or uint32_t to name the context. +#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) + +// Thread-globally allow/disallow logging. +// `enable` is expected to be a bool. +#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled) + +// Insert a (hierarchical) logging context. +// `name` is a char*, std::string or uint32_t to name the context. +#define BWE_TEST_LOGGING_CONTEXT(name) + +// Allow/disallow logging down the call tree from this point. Logging must be +// enabled all the way to the root of the call tree to take place. +// `enable` is expected to be a bool. +#define BWE_TEST_LOGGING_ENABLE(enabled) + +// Set current time (only affects PLOT output). Down the call tree, the latest +// time set always takes precedence. +// `time` is an int64_t time in ms, or -1 to inherit time from previous context. +#define BWE_TEST_LOGGING_TIME(time) + +// Print to stdout, e.g.: +// Context1_Context2_Name printf-formated-string +// `name` is a char*, std::string or uint32_t to name the log line. +// `format` is a printf format string. +// |_1...| are arguments for printf. +#define BWE_TEST_LOGGING_LOG1(name, format, _1) +#define BWE_TEST_LOGGING_LOG2(name, format, _1, _2) +#define BWE_TEST_LOGGING_LOG3(name, format, _1, _2, _3) +#define BWE_TEST_LOGGING_LOG4(name, format, _1, _2, _3, _4) +#define BWE_TEST_LOGGING_LOG5(name, format, _1, _2, _3, _4, _5) + +// Print to stdout in tab-separated format suitable for plotting, e.g.: +// PLOT figure Context1_Context2_Name time value +// `figure` is a figure id. Different figures are plotted in different windows. +// `name` is a char*, std::string or uint32_t to name the plotted value. +// `time` is an int64_t time in ms, or -1 to inherit time from previous context. +// `value` is a double precision float to be plotted. +// `ssrc` identifies the source of a stream +// `alg_name` is an optional argument, a string +#define BWE_TEST_LOGGING_PLOT(figure, name, time, value) +#define BWE_TEST_LOGGING_PLOT_WITH_NAME(figure, name, time, value, alg_name) +#define BWE_TEST_LOGGING_PLOT_WITH_SSRC(figure, name, time, value, ssrc) +#define BWE_TEST_LOGGING_PLOT_WITH_NAME_AND_SSRC(figure, name, time, value, \ + ssrc, alg_name) + +// Print to stdout in tab-separated format suitable for plotting, e.g.: +// BAR figure Context1_Context2_Name x_left width value +// `figure` is a figure id. Different figures are plotted in different windows. +// `name` is a char*, std::string or uint32_t to name the plotted value. +// `value` is a double precision float to be plotted. +// `ylow` and `yhigh` are double precision float for the error line. +// `title` is a string and refers to the error label. +// `ymax` is a double precision float for the limit horizontal line. +// `limit_title` is a string and refers to the limit label. +#define BWE_TEST_LOGGING_BAR(figure, name, value, flow_id) +#define BWE_TEST_LOGGING_ERRORBAR(figure, name, value, ylow, yhigh, \ + error_title, flow_id) +#define BWE_TEST_LOGGING_LIMITERRORBAR( \ + figure, name, value, ylow, yhigh, error_title, ymax, limit_title, flow_id) + +#define BWE_TEST_LOGGING_BASELINEBAR(figure, name, value, flow_id) + +// `num_flows` is an integer refering to the number of RMCAT flows in the +// scenario. +// Define `x_label` and `y_label` for plots. +#define BWE_TEST_LOGGING_LABEL(figure, x_label, y_label, num_flows) + +#else // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE + +#include <map> +#include <memory> +#include <stack> +#include <string> + +#include "rtc_base/synchronization/mutex.h" + +#define BWE_TEST_LOGGING_GLOBAL_CONTEXT(name) \ + do { \ + webrtc::testing::bwe::Logging::GetInstance()->SetGlobalContext(name); \ + } while (0) + +#define BWE_TEST_LOGGING_GLOBAL_ENABLE(enabled) \ + do { \ + webrtc::testing::bwe::Logging::GetInstance()->SetGlobalEnable(enabled); \ + } while (0) + +#define __BWE_TEST_LOGGING_CONTEXT_NAME(ctx, line) ctx##line +#define __BWE_TEST_LOGGING_CONTEXT_DECLARE(ctx, line, name, time, enabled) \ + webrtc::testing::bwe::Logging::Context __BWE_TEST_LOGGING_CONTEXT_NAME( \ + ctx, line)(name, time, enabled) + +#define BWE_TEST_LOGGING_CONTEXT(name) \ + __BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __LINE__, name, -1, true) +#define BWE_TEST_LOGGING_ENABLE(enabled) \ + __BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __LINE__, "", -1, \ + static_cast<bool>(enabled)) +#define BWE_TEST_LOGGING_TIME(time) \ + __BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __LINE__, "", \ + static_cast<int64_t>(time), true) + +#define BWE_TEST_LOGGING_LOG1(name, format, _1) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1); \ + } while (0) +#define BWE_TEST_LOGGING_LOG2(name, format, _1, _2) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2); \ + } while (0) +#define BWE_TEST_LOGGING_LOG3(name, format, _1, _2, _3) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2, _3); \ + } while (0) +#define BWE_TEST_LOGGING_LOG4(name, format, _1, _2, _3, _4) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2, _3, _4); \ + } while (0) +#define BWE_TEST_LOGGING_LOG5(name, format, _1, _2, _3, _4, _5) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->Log(format, _1, _2, _3, _4, \ + _5); \ + } while (0) + +#define BWE_TEST_LOGGING_PLOT(figure, name, time, value) \ + do { \ + __BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \ + static_cast<int64_t>(time), true); \ + webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value); \ + } while (0) + +#define BWE_TEST_LOGGING_PLOT_WITH_NAME(figure, name, time, value, alg_name) \ + do { \ + __BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \ + static_cast<int64_t>(time), true); \ + webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value, \ + alg_name); \ + } while (0) + +#define BWE_TEST_LOGGING_PLOT_WITH_SSRC(figure, name, time, value, ssrc) \ + do { \ + __BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \ + static_cast<int64_t>(time), true); \ + webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value, \ + ssrc); \ + } while (0) + +#define BWE_TEST_LOGGING_PLOT_WITH_NAME_AND_SSRC(figure, name, time, value, \ + ssrc, alg_name) \ + do { \ + __BWE_TEST_LOGGING_CONTEXT_DECLARE(__bwe_log_, __PLOT__, name, \ + static_cast<int64_t>(time), true); \ + webrtc::testing::bwe::Logging::GetInstance()->Plot(figure, name, value, \ + ssrc, alg_name); \ + } while (0) + +#define BWE_TEST_LOGGING_BAR(figure, name, value, flow_id) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->PlotBar(figure, name, value, \ + flow_id); \ + } while (0) + +#define BWE_TEST_LOGGING_BASELINEBAR(figure, name, value, flow_id) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->PlotBaselineBar( \ + figure, name, value, flow_id); \ + } while (0) + +#define BWE_TEST_LOGGING_ERRORBAR(figure, name, value, ylow, yhigh, title, \ + flow_id) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->PlotErrorBar( \ + figure, name, value, ylow, yhigh, title, flow_id); \ + } while (0) + +#define BWE_TEST_LOGGING_LIMITERRORBAR( \ + figure, name, value, ylow, yhigh, error_title, ymax, limit_title, flow_id) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(name); \ + webrtc::testing::bwe::Logging::GetInstance()->PlotLimitErrorBar( \ + figure, name, value, ylow, yhigh, error_title, ymax, limit_title, \ + flow_id); \ + } while (0) + +#define BWE_TEST_LOGGING_LABEL(figure, title, y_label, num_flows) \ + do { \ + BWE_TEST_LOGGING_CONTEXT(title); \ + webrtc::testing::bwe::Logging::GetInstance()->PlotLabel( \ + figure, title, y_label, num_flows); \ + } while (0) + +namespace webrtc { +namespace testing { +namespace bwe { + +class Logging { + public: + class Context { + public: + Context(uint32_t name, int64_t timestamp_ms, bool enabled); + Context(const std::string& name, int64_t timestamp_ms, bool enabled); + Context(const char* name, int64_t timestamp_ms, bool enabled); + + Context() = delete; + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + ~Context(); + }; + + static Logging* GetInstance(); + + void SetGlobalContext(uint32_t name); + void SetGlobalContext(const std::string& name); + void SetGlobalContext(const char* name); + void SetGlobalEnable(bool enabled); + +#if defined(__GNUC__) + // Note: Implicit `this` argument counts as the first argument. + __attribute__((__format__(__printf__, 2, 3))) +#endif + void + Log(const char format[], ...); + void Plot(int figure, const std::string& name, double value); + void Plot(int figure, + const std::string& name, + double value, + const std::string& alg_name); + void Plot(int figure, const std::string& name, double value, uint32_t ssrc); + void Plot(int figure, + const std::string& name, + double value, + uint32_t ssrc, + const std::string& alg_name); + void PlotBar(int figure, const std::string& name, double value, int flow_id); + void PlotBaselineBar(int figure, + const std::string& name, + double value, + int flow_id); + void PlotErrorBar(int figure, + const std::string& name, + double value, + double ylow, + double yhigh, + const std::string& error_title, + int flow_id); + + void PlotLimitErrorBar(int figure, + const std::string& name, + double value, + double ylow, + double yhigh, + const std::string& error_title, + double ymax, + const std::string& limit_title, + int flow_id); + void PlotLabel(int figure, + const std::string& title, + const std::string& y_label, + int num_flows); + + private: + struct State { + State(); + State(const std::string& new_tag, int64_t timestamp_ms, bool enabled); + void MergePrevious(const State& previous); + + std::string tag; + int64_t timestamp_ms; + bool enabled; + }; + struct ThreadState { + ThreadState(); + ~ThreadState(); + State global_state; + std::stack<State> stack; + }; + typedef std::map<uint32_t, ThreadState> ThreadMap; + + Logging(); + ~Logging(); + + Logging(const Logging&) = delete; + Logging& operator=(const Logging&) = delete; + + void PushState(const std::string& append_to_tag, + int64_t timestamp_ms, + bool enabled); + void PopState(); + + Mutex mutex_; + ThreadMap thread_map_; +}; +} // namespace bwe +} // namespace testing +} // namespace webrtc + +#endif // BWE_TEST_LOGGING_COMPILE_TIME_ENABLE +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_TEST_BWE_TEST_LOGGING_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc new file mode 100644 index 0000000000..403f81fd03 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc @@ -0,0 +1,109 @@ +/* + * 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 "modules/remote_bitrate_estimator/tools/bwe_rtp.h" + +#include <stdio.h> + +#include <set> +#include <sstream> +#include <string> + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "test/rtp_file_reader.h" + +ABSL_FLAG(std::string, + extension_type, + "abs", + "Extension type, either abs for absolute send time or tsoffset " + "for timestamp offset."); +std::string ExtensionType() { + return absl::GetFlag(FLAGS_extension_type); +} + +ABSL_FLAG(int, extension_id, 3, "Extension id."); +int ExtensionId() { + return absl::GetFlag(FLAGS_extension_id); +} + +ABSL_FLAG(std::string, input_file, "", "Input file."); +std::string InputFile() { + return absl::GetFlag(FLAGS_input_file); +} + +ABSL_FLAG(std::string, + ssrc_filter, + "", + "Comma-separated list of SSRCs in hexadecimal which are to be " + "used as input to the BWE (only applicable to pcap files)."); +std::set<uint32_t> SsrcFilter() { + std::string ssrc_filter_string = absl::GetFlag(FLAGS_ssrc_filter); + if (ssrc_filter_string.empty()) + return std::set<uint32_t>(); + std::stringstream ss; + std::string ssrc_filter = ssrc_filter_string; + std::set<uint32_t> ssrcs; + + // Parse the ssrcs in hexadecimal format. + ss << std::hex << ssrc_filter; + uint32_t ssrc; + while (ss >> ssrc) { + ssrcs.insert(ssrc); + ss.ignore(1, ','); + } + return ssrcs; +} + +bool ParseArgsAndSetupRtpReader( + int argc, + char** argv, + std::unique_ptr<webrtc::test::RtpFileReader>& rtp_reader, + webrtc::RtpHeaderExtensionMap& rtp_header_extensions) { + absl::ParseCommandLine(argc, argv); + std::string filename = InputFile(); + + std::set<uint32_t> ssrc_filter = SsrcFilter(); + fprintf(stderr, "Filter on SSRC: "); + for (auto& s : ssrc_filter) { + fprintf(stderr, "0x%08x, ", s); + } + fprintf(stderr, "\n"); + if (filename.substr(filename.find_last_of('.')) == ".pcap") { + fprintf(stderr, "Opening as pcap\n"); + rtp_reader.reset(webrtc::test::RtpFileReader::Create( + webrtc::test::RtpFileReader::kPcap, filename.c_str(), SsrcFilter())); + } else { + fprintf(stderr, "Opening as rtp\n"); + rtp_reader.reset(webrtc::test::RtpFileReader::Create( + webrtc::test::RtpFileReader::kRtpDump, filename.c_str())); + } + if (!rtp_reader) { + fprintf(stderr, "Cannot open input file %s\n", filename.c_str()); + return false; + } + fprintf(stderr, "Input file: %s\n\n", filename.c_str()); + + webrtc::RTPExtensionType extension = webrtc::kRtpExtensionAbsoluteSendTime; + if (ExtensionType() == "tsoffset") { + extension = webrtc::kRtpExtensionTransmissionTimeOffset; + fprintf(stderr, "Extension: toffset\n"); + } else if (ExtensionType() == "abs") { + fprintf(stderr, "Extension: abs\n"); + } else { + fprintf(stderr, "Unknown extension type\n"); + return false; + } + + rtp_header_extensions.RegisterByType(ExtensionId(), extension); + + return true; +} diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h b/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h new file mode 100644 index 0000000000..3b161db37b --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h @@ -0,0 +1,25 @@ +/* + * 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 MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_ +#define MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_ + +#include <memory> + +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "test/rtp_file_reader.h" + +bool ParseArgsAndSetupRtpReader( + int argc, + char** argv, + std::unique_ptr<webrtc::test::RtpFileReader>& rtp_reader, + webrtc::RtpHeaderExtensionMap& rtp_header_extensions); + +#endif // MODULES_REMOTE_BITRATE_ESTIMATOR_TOOLS_BWE_RTP_H_ diff --git a/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc b/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc new file mode 100644 index 0000000000..e8dc59f740 --- /dev/null +++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc @@ -0,0 +1,67 @@ +/* + * 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 <stdio.h> + +#include <memory> + +#include "modules/remote_bitrate_estimator/tools/bwe_rtp.h" +#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" +#include "modules/rtp_rtcp/source/rtp_header_extensions.h" +#include "modules/rtp_rtcp/source/rtp_packet.h" +#include "rtc_base/strings/string_builder.h" +#include "test/rtp_file_reader.h" + +int main(int argc, char* argv[]) { + std::unique_ptr<webrtc::test::RtpFileReader> reader; + webrtc::RtpHeaderExtensionMap rtp_header_extensions; + if (!ParseArgsAndSetupRtpReader(argc, argv, reader, rtp_header_extensions)) { + return -1; + } + + bool arrival_time_only = (argc >= 5 && strncmp(argv[4], "-t", 2) == 0); + + fprintf(stdout, + "seqnum timestamp ts_offset abs_sendtime recvtime " + "markerbit ssrc size original_size\n"); + int packet_counter = 0; + int non_zero_abs_send_time = 0; + int non_zero_ts_offsets = 0; + webrtc::test::RtpPacket packet; + while (reader->NextPacket(&packet)) { + webrtc::RtpPacket header(&rtp_header_extensions); + header.Parse(packet.data, packet.length); + uint32_t abs_send_time = 0; + if (header.GetExtension<webrtc::AbsoluteSendTime>(&abs_send_time) && + abs_send_time != 0) + ++non_zero_abs_send_time; + int32_t toffset = 0; + if (header.GetExtension<webrtc::TransmissionOffset>(&toffset) && + toffset != 0) + ++non_zero_ts_offsets; + if (arrival_time_only) { + rtc::StringBuilder ss; + ss << static_cast<int64_t>(packet.time_ms) * 1000000; + fprintf(stdout, "%s\n", ss.str().c_str()); + } else { + fprintf(stdout, "%u %u %d %u %u %d %u %zu %zu\n", header.SequenceNumber(), + header.Timestamp(), toffset, abs_send_time, packet.time_ms, + header.Marker(), header.Ssrc(), packet.length, + packet.original_length); + } + ++packet_counter; + } + fprintf(stderr, "Parsed %d packets\n", packet_counter); + fprintf(stderr, "Packets with non-zero absolute send time: %d\n", + non_zero_abs_send_time); + fprintf(stderr, "Packets with non-zero timestamp offset: %d\n", + non_zero_ts_offsets); + return 0; +} |