summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/remote_bitrate_estimator
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/libwebrtc/modules/remote_bitrate_estimator
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/remote_bitrate_estimator')
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/BUILD.gn144
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/DEPS6
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/OWNERS6
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc440
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h126
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control_unittest.cc484
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/bwe_defines.cc24
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/include/bwe_defines.h46
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h70
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.cc163
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival.h95
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/inter_arrival_unittest.cc531
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.cc160
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.h62
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc809
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.cc164
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_estimator.h83
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.cc193
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.h127
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map_test.cc251
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.cc399
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time.h135
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_abs_send_time_unittest.cc296
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build226
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc252
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h87
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream_unittest.cc78
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.cc594
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_unittest_helper.h225
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.cc328
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h143
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc649
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.cc262
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h360
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.cc109
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/tools/bwe_rtp.h25
-rw-r--r--third_party/libwebrtc/modules/remote_bitrate_estimator/tools/rtp_to_text.cc67
37 files changed, 8219 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..9e2352fd61
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/BUILD.gn
@@ -0,0 +1,144 @@
+# 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",
+ "../../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..97e5d51dac
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/OWNERS
@@ -0,0 +1,6 @@
+stefan@webrtc.org
+terelius@webrtc.org
+asapersson@webrtc.org
+mflodman@webrtc.org
+philipel@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..b625a745df
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.cc
@@ -0,0 +1,440 @@
+/*
+ * 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");
+}
+
+bool IsNotDisabled(const FieldTrialsView& field_trials, absl::string_view key) {
+ return !absl::StartsWith(field_trials.Lookup(key), "Disabled");
+}
+
+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),
+ in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
+ no_bitrate_increase_in_alr_(
+ IsEnabled(*key_value_config,
+ "WebRTC-DontIncreaseDelayBasedBweInAlr")),
+ estimate_bounded_backoff_(
+ IsNotDisabled(*key_value_config,
+ "WebRTC-Bwe-EstimateBoundedBackoff")),
+ 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);
+ if (in_experiment_)
+ 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 (estimate_bounded_backoff_ && 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..6c770cdc45
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/aimd_rate_control.h
@@ -0,0 +1,126 @@
+/*
+ * 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_;
+ const bool in_experiment_;
+ // 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_;
+ // Use estimated link capacity lower bound if it is higher than the
+ // acknowledged rate when backing off due to overuse.
+ const bool estimate_bounded_backoff_;
+ // 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..672822bbcd
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.cc
@@ -0,0 +1,160 @@
+/*
+ * 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 char kAdaptiveThresholdExperiment[] = "WebRTC-AdaptiveBweThreshold";
+const char kEnabledPrefix[] = "Enabled";
+const size_t kEnabledPrefixLength = sizeof(kEnabledPrefix) - 1;
+const char kDisabledPrefix[] = "Disabled";
+const size_t kDisabledPrefixLength = sizeof(kDisabledPrefix) - 1;
+
+const double kMaxAdaptOffsetMs = 15.0;
+const double kOverUsingTimeThreshold = 10;
+const int kMaxNumDeltas = 60;
+
+bool AdaptiveThresholdExperimentIsDisabled(
+ const FieldTrialsView& key_value_config) {
+ std::string experiment_string =
+ key_value_config.Lookup(kAdaptiveThresholdExperiment);
+ const size_t kMinExperimentLength = kDisabledPrefixLength;
+ if (experiment_string.length() < kMinExperimentLength)
+ return false;
+ return experiment_string.substr(0, kDisabledPrefixLength) == kDisabledPrefix;
+}
+
+// Gets thresholds from the experiment name following the format
+// "WebRTC-AdaptiveBweThreshold/Enabled-0.5,0.002/".
+bool ReadExperimentConstants(const FieldTrialsView& key_value_config,
+ double* k_up,
+ double* k_down) {
+ std::string experiment_string =
+ key_value_config.Lookup(kAdaptiveThresholdExperiment);
+ const size_t kMinExperimentLength = kEnabledPrefixLength + 3;
+ if (experiment_string.length() < kMinExperimentLength ||
+ experiment_string.substr(0, kEnabledPrefixLength) != kEnabledPrefix)
+ return false;
+ return sscanf(experiment_string.substr(kEnabledPrefixLength + 1).c_str(),
+ "%lf,%lf", k_up, k_down) == 2;
+}
+
+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/".
+ : in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
+ k_up_(0.0087),
+ k_down_(0.039),
+ overusing_time_threshold_(100),
+ threshold_(12.5),
+ last_update_ms_(-1),
+ prev_offset_(0.0),
+ time_over_using_(-1),
+ overuse_counter_(0),
+ hypothesis_(BandwidthUsage::kBwNormal) {
+ if (!AdaptiveThresholdExperimentIsDisabled(*key_value_config))
+ InitializeExperiment(*key_value_config);
+}
+
+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 (!in_experiment_)
+ return;
+
+ 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;
+}
+
+void OveruseDetector::InitializeExperiment(
+ const FieldTrialsView& key_value_config) {
+ RTC_DCHECK(in_experiment_);
+ double k_up = 0.0;
+ double k_down = 0.0;
+ overusing_time_threshold_ = kOverUsingTimeThreshold;
+ if (ReadExperimentConstants(key_value_config, &k_up, &k_down)) {
+ k_up_ = k_up;
+ k_down_ = k_down;
+ }
+}
+} // 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..dfaea9187a
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector.h
@@ -0,0 +1,62 @@
+/*
+ * 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 {
+
+bool AdaptiveThresholdExperimentIsDisabled(
+ const FieldTrialsView& key_value_config);
+
+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);
+
+ bool in_experiment_;
+ double k_up_;
+ double k_down_;
+ 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..8420af96a1
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/overuse_detector_unittest.cc
@@ -0,0 +1,809 @@
+/*
+ * 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/field_trial.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,
+ &timestamp_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, DISABLED_OveruseWithHighVariance100Kbit10fps) {
+ uint32_t frame_duration_ms = 100;
+ uint32_t drift_per_frame_ms = 10;
+ uint32_t rtp_timestamp = frame_duration_ms * 90;
+ size_t packet_size = 1200;
+ int offset = 10;
+
+ // Run 1000 samples to reach steady state.
+ for (int i = 0; i < 1000; ++i) {
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ rtp_timestamp += frame_duration_ms * 90;
+ if (i % 2) {
+ offset = random_.Rand(0, 49);
+ 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.
+ // Above noise generate a standard deviation of approximately 28 ms.
+ // Total build up of 150 ms.
+ for (int j = 0; j < 15; ++j) {
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ now_ms_ += frame_duration_ms + drift_per_frame_ms;
+ rtp_timestamp += frame_duration_ms * 90;
+ EXPECT_EQ(BandwidthUsage::kBwNormal, overuse_detector_->State());
+ }
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ EXPECT_EQ(BandwidthUsage::kBwOverusing, overuse_detector_->State());
+}
+
+TEST_F(OveruseDetectorTest, DISABLED_OveruseWithLowVariance100Kbit10fps) {
+ uint32_t frame_duration_ms = 100;
+ uint32_t drift_per_frame_ms = 1;
+ uint32_t rtp_timestamp = frame_duration_ms * 90;
+ size_t packet_size = 1200;
+ int offset = 10;
+
+ // Run 1000 samples to reach steady state.
+ for (int i = 0; i < 1000; ++i) {
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ rtp_timestamp += frame_duration_ms * 90;
+ if (i % 2) {
+ offset = 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 6 ms.
+ for (int j = 0; j < 6; ++j) {
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ now_ms_ += frame_duration_ms + drift_per_frame_ms;
+ rtp_timestamp += frame_duration_ms * 90;
+ EXPECT_EQ(BandwidthUsage::kBwNormal, overuse_detector_->State());
+ }
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ EXPECT_EQ(BandwidthUsage::kBwOverusing, overuse_detector_->State());
+}
+
+TEST_F(OveruseDetectorTest, OveruseWithLowVariance2000Kbit30fps) {
+ uint32_t frame_duration_ms = 33;
+ uint32_t drift_per_frame_ms = 1;
+ uint32_t rtp_timestamp = frame_duration_ms * 90;
+ size_t packet_size = 1200;
+ int offset = 0;
+
+ // Run 1000 samples to reach steady state.
+ for (int i = 0; i < 1000; ++i) {
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ UpdateDetector(rtp_timestamp, now_ms_, packet_size);
+ rtp_timestamp += frame_duration_ms * 90;
+ if (i % 2) {
+ offset = 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());
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_LowGaussianVariance30Kbit3fps \
+ DISABLED_LowGaussianVariance30Kbit3fps
+#else
+#define MAYBE_LowGaussianVariance30Kbit3fps LowGaussianVariance30Kbit3fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_LowGaussianVariance100Kbit5fps \
+ DISABLED_LowGaussianVariance100Kbit5fps
+#else
+#define MAYBE_LowGaussianVariance100Kbit5fps LowGaussianVariance100Kbit5fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_HighGaussianVariance100Kbit5fps \
+ DISABLED_HighGaussianVariance100Kbit5fps
+#else
+#define MAYBE_HighGaussianVariance100Kbit5fps HighGaussianVariance100Kbit5fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_LowGaussianVariance100Kbit10fps \
+ DISABLED_LowGaussianVariance100Kbit10fps
+#else
+#define MAYBE_LowGaussianVariance100Kbit10fps LowGaussianVariance100Kbit10fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_HighGaussianVariance100Kbit10fps \
+ DISABLED_HighGaussianVariance100Kbit10fps
+#else
+#define MAYBE_HighGaussianVariance100Kbit10fps HighGaussianVariance100Kbit10fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_LowGaussianVariance300Kbit30fps \
+ DISABLED_LowGaussianVariance300Kbit30fps
+#else
+#define MAYBE_LowGaussianVariance300Kbit30fps LowGaussianVariance300Kbit30fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_LowGaussianVariance1000Kbit30fps \
+ DISABLED_LowGaussianVariance1000Kbit30fps
+#else
+#define MAYBE_LowGaussianVariance1000Kbit30fps LowGaussianVariance1000Kbit30fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+#if defined(WEBRTC_ANDROID)
+#define MAYBE_LowGaussianVariance2000Kbit30fps \
+ DISABLED_LowGaussianVariance2000Kbit30fps
+#else
+#define MAYBE_LowGaussianVariance2000Kbit30fps LowGaussianVariance2000Kbit30fps
+#endif
+TEST_F(OveruseDetectorTest, MAYBE_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);
+}
+
+class OveruseDetectorExperimentTest : public OveruseDetectorTest {
+ public:
+ OveruseDetectorExperimentTest()
+ : override_field_trials_(
+ "WebRTC-AdaptiveBweThreshold/Enabled-0.01,0.00018/") {}
+
+ protected:
+ void SetUp() override {
+ overuse_detector_.reset(new OveruseDetector(&field_trials_));
+ }
+
+ test::ScopedFieldTrials override_field_trials_;
+ const FieldTrialBasedConfig field_trials_;
+};
+
+TEST_F(OveruseDetectorExperimentTest, 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(OveruseDetectorExperimentTest, 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..16d400e227
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.cc
@@ -0,0 +1,193 @@
+/*
+ * 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_);
+ // Also trim the buffer to remove leading non-received packets, to
+ // ensure that the buffer only spans received packets.
+ TrimLeadingNotReceivedEntries();
+ }
+
+ 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::TrimLeadingNotReceivedEntries() {
+ const int begin_index = Index(begin_sequence_number_);
+ const Timestamp* const begin_it = arrival_times_.get() + begin_index;
+ const Timestamp* const end_it = arrival_times_.get() + capacity();
+
+ for (const Timestamp* it = begin_it; it != end_it; ++it) {
+ if (*it >= Timestamp::Zero()) {
+ begin_sequence_number_ += (it - begin_it);
+ return;
+ }
+ }
+ // Reached end of the arrival_times_ and all entries represent not received
+ // packets. Remove them.
+ begin_sequence_number_ += (capacity() - begin_index);
+ // Continue removing entries at the beginning of the circular buffer.
+ for (const Timestamp* it = arrival_times_.get(); it != begin_it; ++it) {
+ if (*it >= Timestamp::Zero()) {
+ begin_sequence_number_ += (it - arrival_times_.get());
+ return;
+ }
+ }
+
+ RTC_DCHECK_NOTREACHED() << "There should be at least one non-empty entry";
+}
+
+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;
+ RTC_DCHECK(has_received(begin_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..d489a0c53d
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map.h
@@ -0,0 +1,127 @@
+/*
+ * 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:
+ // 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)];
+ }
+
+ // 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);
+
+ void TrimLeadingNotReceivedEntries();
+
+ // 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..00c927ffd7
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/packet_arrival_map_test.cc
@@ -0,0 +1,251 @@
+/*
+ * 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, 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, GrowsBufferAndRemoveOldTrimsBeginning) {
+ PacketArrivalTimeMap map;
+
+ constexpr int64_t kLargeSeq = 42 + PacketArrivalTimeMap::kMaxNumberOfPackets;
+ map.AddPacket(42, Timestamp::Millis(10));
+ // Missing: 43, 44
+ map.AddPacket(45, Timestamp::Millis(13));
+ map.AddPacket(kLargeSeq, Timestamp::Millis(12));
+
+ EXPECT_EQ(map.begin_sequence_number(), 45);
+ EXPECT_EQ(map.end_sequence_number(), kLargeSeq + 1);
+
+ EXPECT_FALSE(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));
+}
+
+} // 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..7af9c5fc00
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_gn/moz.build
@@ -0,0 +1,226 @@
+# 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"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/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_ENABLE_AVX2"] = 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 += [
+ "GLESv2",
+ "log"
+ ]
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ 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 += [
+ "dl",
+ "rt"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_ENABLE_AVX2"] = 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_ENABLE_AVX2"] = True
+ 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["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":
+
+ OS_LIBS += [
+ "android_support"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ 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..6f442e5e2c
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.cc
@@ -0,0 +1,252 @@
+/*
+ * 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_(new AimdRateControl(&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,
+ &timestamp_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 ||
+ GetRemoteRate()->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;
+ }
+ AimdRateControl* remote_rate = GetRemoteRate();
+
+ 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_);
+ GetRemoteRate()->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;
+ }
+}
+
+AimdRateControl* RemoteBitrateEstimatorSingleStream::GetRemoteRate() {
+ if (!remote_rate_)
+ remote_rate_.reset(new AimdRateControl(&field_trials_));
+ return remote_rate_.get();
+}
+
+} // 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..d62f922e02
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator_single_stream.h
@@ -0,0 +1,87 @@
+/*
+ * 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 <memory>
+#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_);
+
+ // Returns `remote_rate_` if the pointed to object exists,
+ // otherwise creates it.
+ AimdRateControl* GetRemoteRate() RTC_EXCLUSIVE_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_);
+ std::unique_ptr<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..b83720d1a8
--- /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.
+static constexpr int64_t kMaxTimeMs =
+ std::numeric_limits<int64_t>::max() / 1000;
+
+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,
+ const FieldTrialsView* key_value_config,
+ NetworkStateEstimator* network_state_estimator)
+ : feedback_sender_(std::move(feedback_sender)),
+ send_config_(key_value_config),
+ last_process_time_(Timestamp::MinusInfinity()),
+ network_state_estimator_(network_state_estimator),
+ media_ssrc_(0),
+ feedback_packet_count_(0),
+ packet_overhead_(DataSize::Zero()),
+ send_interval_(send_config_.default_interval.Get()),
+ 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 (ms): "
+ << send_config_.max_interval->ms();
+}
+
+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() >= send_config_.back_window.Get()) {
+ // Start new feedback packet, cull old packets.
+ packet_arrival_times_.RemoveOldPackets(
+ sequence_number, arrival_time - send_config_.back_window.Get());
+ }
+}
+
+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);
+ const DataRate kMinTwccRate =
+ kTwccReportSize / send_config_.max_interval.Get();
+
+ // Let TWCC reports occupy 5% of total bandwidth.
+ DataRate twcc_bitrate =
+ DataRate::BitsPerSec(send_config_.bandwidth_fraction * 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
+ ? send_config_.max_interval.Get()
+ : std::max(kTwccReportSize / twcc_bitrate,
+ send_config_.min_interval.Get());
+
+ 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) {
+ Timestamp arrival_time = packet_arrival_times_.get(seq);
+ if (arrival_time < Timestamp::Zero()) {
+ // Packet not received.
+ continue;
+ }
+
+ 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),
+ arrival_time);
+ feedback_packet->SetFeedbackSequenceNumber(feedback_packet_count_++);
+ }
+
+ if (!feedback_packet->AddReceivedPacket(static_cast<uint16_t>(seq & 0xFFFF),
+ 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..509ad0ba02
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy.h
@@ -0,0 +1,143 @@
+/*
+ * 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/experiments/field_trial_parser.h"
+#include "rtc_base/numerics/sequence_number_util.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,
+ const FieldTrialsView* key_value_config,
+ 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:
+ struct TransportWideFeedbackConfig {
+ FieldTrialParameter<TimeDelta> back_window{"wind", TimeDelta::Millis(500)};
+ FieldTrialParameter<TimeDelta> min_interval{"min", TimeDelta::Millis(50)};
+ FieldTrialParameter<TimeDelta> max_interval{"max", TimeDelta::Millis(250)};
+ FieldTrialParameter<TimeDelta> default_interval{"def",
+ TimeDelta::Millis(100)};
+ FieldTrialParameter<double> bandwidth_fraction{"frac", 0.05};
+ explicit TransportWideFeedbackConfig(
+ const FieldTrialsView* key_value_config) {
+ ParseFieldTrial({&back_window, &min_interval, &max_interval,
+ &default_interval, &bandwidth_fraction},
+ key_value_config->Lookup(
+ "WebRTC-Bwe-TransportWideFeedbackIntervals"));
+ }
+ };
+
+ 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_;
+ const TransportWideFeedbackConfig send_config_;
+ 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..10bc1e80a0
--- /dev/null
+++ b/third_party/libwebrtc/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc
@@ -0,0 +1,649 @@
+/*
+ * 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/field_trial_based_config.h"
+#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(),
+ &field_trial_config_,
+ &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());
+ }
+
+ FieldTrialBasedConfig field_trial_config_;
+ 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(kBaseSeq + 20000 + 9, feedback_packet->GetBaseSequence());
+ 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;
+}