tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 2d3b84910161279bc6bf3ddaa27e38dc81d81c2d
parent 4f581cb483a8ea4bf8cd756b683e6f6ba41824ef
Author: Michael Froman <mfroman@mozilla.com>
Date:   Wed,  8 Oct 2025 17:42:29 -0500

Bug 1993083 - Vendor libwebrtc from c432ba14db

Essentially a no-op since we're going to see this change
reverted when we vendor in 5298f72f97.

Upstream commit: https://webrtc.googlesource.com/src/+/c432ba14dbb2cdd2b90a6f18043254cbcfa2fd0e
    Refactor class SendSideBandwidthEstimation

    The goal is to make states more clear and be able to log where a certain decision come from.
    In this cl:
     - loss bases BWE handling moved to separate files
     - remove field trial WebRTC-Bwe-ReceiverLimitCapsOnly

    Bug: webrtc:423841921, webrtc:42222445
    Change-Id: I502bee094e18606f8a188214fafa421a868023ca
    Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/396342
    Reviewed-by: Diep Bui <diepbp@webrtc.org>
    Commit-Queue: Per Kjellander <perkj@webrtc.org>
    Cr-Commit-Position: refs/heads/main@{#45056}

Diffstat:
Mthird_party/libwebrtc/README.mozilla.last-vendor | 4++--
Athird_party/libwebrtc/moz-patch-stack/5298f72f97.no-op-cherry-pick-msg | 1+
Mthird_party/libwebrtc/moz-patch-stack/p0001.patch | 1831+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mthird_party/libwebrtc/moz-patch-stack/p0002.patch | 84++++++++++++++++++++++++++++++++++++++++---------------------------------------
Cthird_party/libwebrtc/moz-patch-stack/p0002.patch -> third_party/libwebrtc/moz-patch-stack/p0003.patch | 0
5 files changed, 1836 insertions(+), 84 deletions(-)

diff --git a/third_party/libwebrtc/README.mozilla.last-vendor b/third_party/libwebrtc/README.mozilla.last-vendor @@ -1,4 +1,4 @@ # ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc -libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-08T22:40:58.452032+00:00. +libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-08T22:42:19.983469+00:00. # base of lastest vendoring -50e4b6ac27 +c432ba14db diff --git a/third_party/libwebrtc/moz-patch-stack/5298f72f97.no-op-cherry-pick-msg b/third_party/libwebrtc/moz-patch-stack/5298f72f97.no-op-cherry-pick-msg @@ -0,0 +1 @@ +We already cherry-picked this when we vendored c432ba14db. diff --git a/third_party/libwebrtc/moz-patch-stack/p0001.patch b/third_party/libwebrtc/moz-patch-stack/p0001.patch @@ -1,48 +1,1797 @@ -From: Gennady Tsitovich <gtsitovich@google.com> -Date: Tue, 15 Jul 2025 08:24:50 +0000 -Subject: (cherry-pick-branch-heads/7258) [M139] Add chrome-cherry-picker - account to bot allowlist -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit +From: Michael Froman <mjfroman@mac.com> +Date: Wed, 8 Oct 2025 17:42:07 -0500 +Subject: (tmp-cherry-pick) Revert "Refactor class SendSideBandwidthEstimation" + (5298f72f97) +This reverts commit c432ba14dbb2cdd2b90a6f18043254cbcfa2fd0e. + +Reason for revert: Investigate downstream test breakage - b/429391976 + +Bug: webrtc:423841921, webrtc:42222445 Original change's description: -> Add chrome-cherry-picker account to bot allowlist +> Refactor class SendSideBandwidthEstimation > -> chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com is -> being by the Chrome Cherry Picker (go/chromecherrypicker) and needs to -> be able to skip the author check for presubmits. +> The goal is to make states more clear and be able to log where a certain decision come from. +> In this cl: +> - loss bases BWE handling moved to separate files +> - remove field trial WebRTC-Bwe-ReceiverLimitCapsOnly > -> Bug: chromium:414375466 -> Change-Id: Ib9f15dd67a4efe5346e6631135e1bcd7196b992c -> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/400480 -> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> -> Reviewed-by: Björn Terelius <terelius@webrtc.org> -> Commit-Queue: Gennady Tsitovich <gtsitovich@google.com> -> Cr-Commit-Position: refs/heads/main@{#45148} +> Bug: webrtc:423841921, webrtc:42222445 +> Change-Id: I502bee094e18606f8a188214fafa421a868023ca +> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/396342 +> Reviewed-by: Diep Bui <diepbp@webrtc.org> +> Commit-Queue: Per Kjellander <perkj@webrtc.org> +> Cr-Commit-Position: refs/heads/main@{#45056} -Bug: chromium:431157710,chromium:414375466 -Change-Id: Ib9f15dd67a4efe5346e6631135e1bcd7196b992c -Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/400700 -Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> -Auto-Submit: Chrome Cherry Picker <chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com> -Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> -Cr-Commit-Position: refs/branch-heads/7258@{#2} -Cr-Branched-From: 74fa937f86ed8432c07676f7a1ce0e5e2812b3d5-refs/heads/main@{#44974} +Bug: webrtc:423841921, webrtc:42222445 +Change-Id: I8dcda24877dd8b1fbab4e1bb5235e2e7b903dabf +Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/399080 +Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com> +Reviewed-by: Diep Bui <diepbp@webrtc.org> +Commit-Queue: Per Kjellander <perkj@webrtc.org> +Cr-Commit-Position: refs/heads/main@{#45088} --- - PRESUBMIT.py | 2 ++ - 1 file changed, 2 insertions(+) + experiments/field_trials.py | 5 +- + .../congestion_controller/goog_cc/BUILD.gn | 24 - + .../goog_cc/goog_cc_network_control.cc | 31 +- + .../goog_cc/loss_based_bwe.cc | 293 ------------ + .../goog_cc/loss_based_bwe.h | 107 ----- + .../goog_cc/loss_based_bwe_unittest.cc | 157 ------- + .../goog_cc/send_side_bandwidth_estimation.cc | 436 ++++++++++++++---- + .../goog_cc/send_side_bandwidth_estimation.h | 79 +++- + ...send_side_bandwidth_estimation_unittest.cc | 155 ++++--- + 9 files changed, 500 insertions(+), 787 deletions(-) + delete mode 100644 modules/congestion_controller/goog_cc/loss_based_bwe.cc + delete mode 100644 modules/congestion_controller/goog_cc/loss_based_bwe.h + delete mode 100644 modules/congestion_controller/goog_cc/loss_based_bwe_unittest.cc -diff --git a/PRESUBMIT.py b/PRESUBMIT.py -index 96fa8abd9d..debc65fb24 100755 ---- a/PRESUBMIT.py -+++ b/PRESUBMIT.py -@@ -991,6 +991,8 @@ def CommonChecks(input_api, output_api): - bot_allowlist=[ - 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com', - 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com', -+ ('chrome-cherry-picker' -+ '@chops-service-accounts.iam.gserviceaccount.com'), - ])) - results.extend( - input_api.canned_checks.CheckChangeTodoHasOwner( +diff --git a/experiments/field_trials.py b/experiments/field_trials.py +index bb507462f1..dc1e12750a 100755 +--- a/experiments/field_trials.py ++++ b/experiments/field_trials.py +@@ -583,6 +583,9 @@ POLICY_EXEMPT_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([ + FieldTrial('WebRTC-Bwe-ReceiveTimeFix', + 42234228, + date(2024, 4, 1)), ++ FieldTrial('WebRTC-Bwe-ReceiverLimitCapsOnly', ++ 42222445, ++ date(2024, 4, 1)), + FieldTrial('WebRTC-Bwe-RobustThroughputEstimatorSettings', + 42220312, + date(2024, 4, 1)), +@@ -902,7 +905,7 @@ POLICY_EXEMPT_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([ + ]) # yapf: disable + + POLICY_EXEMPT_FIELD_TRIALS_DIGEST: str = \ +- 'cf604f3ec3a4fa7cf0857f8e1f9201366abe2e5f' ++ '625f8d689ab8bcfe4118347c6f8c852e3ac372c7' + + REGISTERED_FIELD_TRIALS: FrozenSet[FieldTrial] = ACTIVE_FIELD_TRIALS.union( + POLICY_EXEMPT_FIELD_TRIALS) +diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn +index 9b0660c244..e1cd1c4813 100644 +--- a/modules/congestion_controller/goog_cc/BUILD.gn ++++ b/modules/congestion_controller/goog_cc/BUILD.gn +@@ -142,27 +142,6 @@ rtc_library("loss_based_bwe_v2") { + "//third_party/abseil-cpp/absl/algorithm:container", + ] + } +-rtc_library("loss_based_bwe") { +- sources = [ +- "loss_based_bwe.cc", +- "loss_based_bwe.h", +- ] +- deps = [ +- ":loss_based_bwe_v2", +- "../../../api:array_view", +- "../../../api:field_trials_view", +- "../../../api/transport:network_control", +- "../../../api/units:data_rate", +- "../../../api/units:data_size", +- "../../../api/units:time_delta", +- "../../../api/units:timestamp", +- "../../../rtc_base:checks", +- "../../../rtc_base:logging", +- "../../../rtc_base/experiments:field_trial_parser", +- "../../remote_bitrate_estimator", +- "//third_party/abseil-cpp/absl/algorithm:container", +- ] +-} + + rtc_library("send_side_bwe") { + sources = [ +@@ -170,7 +149,6 @@ rtc_library("send_side_bwe") { + "send_side_bandwidth_estimation.h", + ] + deps = [ +- ":loss_based_bwe", + ":loss_based_bwe_v2", + "../../../api:field_trials_view", + "../../../api/rtc_event_log", +@@ -285,7 +263,6 @@ if (rtc_include_tests) { + "delay_based_bwe_unittest_helper.cc", + "delay_based_bwe_unittest_helper.h", + "goog_cc_network_control_unittest.cc", +- "loss_based_bwe_unittest.cc", + "loss_based_bwe_v2_test.cc", + "probe_bitrate_estimator_unittest.cc", + "probe_controller_unittest.cc", +@@ -298,7 +275,6 @@ if (rtc_include_tests) { + ":delay_based_bwe", + ":estimators", + ":goog_cc", +- ":loss_based_bwe", + ":loss_based_bwe_v2", + ":probe_controller", + ":pushback_controller", +diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +index 2a64b31abf..45dda7b2c9 100644 +--- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc ++++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +@@ -220,7 +220,7 @@ NetworkControlUpdate GoogCcNetworkController::OnProcessInterval( + congestion_window_pushback_controller_->UpdatePacingQueue( + msg.pacer_queue->bytes()); + } +- bandwidth_estimation_->OnPeriodicUpdate(msg.at_time); ++ bandwidth_estimation_->UpdateEstimate(msg.at_time); + std::optional<int64_t> start_time_ms = + alr_detector_->GetApplicationLimitedRegionStartTime(); + probe_controller_->SetAlrStartTimeMs(start_time_ms); +@@ -380,8 +380,10 @@ std::vector<ProbeClusterConfig> GoogCcNetworkController::ResetConstraints( + + NetworkControlUpdate GoogCcNetworkController::OnTransportLossReport( + TransportLossReport msg) { ++ int64_t total_packets_delta = ++ msg.packets_received_delta + msg.packets_lost_delta; + bandwidth_estimation_->UpdatePacketsLost( +- msg.packets_lost_delta, msg.packets_received_delta, msg.receive_time); ++ msg.packets_lost_delta, total_packets_delta, msg.receive_time); + return NetworkControlUpdate(); + } + +@@ -452,7 +454,11 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( + probe_controller_->SetAlrEndedTimeMs(now_ms); + } + previously_in_alr_ = alr_start_time.has_value(); +- ++ acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector( ++ report.SortedByReceiveTime()); ++ auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate(); ++ bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate, ++ report.feedback_time); + for (const auto& feedback : report.SortedByReceiveTime()) { + if (feedback.sent_packet.pacing_info.probe_cluster_id != + PacedPacketInfo::kNotAProbe) { +@@ -471,9 +477,6 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( + *probe_bitrate < estimate_->link_capacity_lower) { + probe_bitrate.reset(); + } +- acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector( +- report.SortedByReceiveTime()); +- auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate(); + if (limit_probes_lower_than_throughput_estimate_ && probe_bitrate && + acknowledged_bitrate) { + // Limit the backoff to something slightly below the acknowledged +@@ -497,9 +500,19 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( + report, acknowledged_bitrate, probe_bitrate, estimate_, + alr_start_time.has_value()); + +- bandwidth_estimation_->OnTransportPacketsFeedback( +- report, delay_based_bwe_->last_estimate(), acknowledged_bitrate, +- /*is_probe_rate=*/result.probe, alr_start_time.has_value()); ++ if (result.updated) { ++ if (result.probe) { ++ bandwidth_estimation_->SetSendBitrate(result.target_bitrate, ++ report.feedback_time); ++ } ++ // Since SetSendBitrate now resets the delay-based estimate, we have to ++ // call UpdateDelayBasedEstimate after SetSendBitrate. ++ bandwidth_estimation_->UpdateDelayBasedEstimate(report.feedback_time, ++ result.target_bitrate); ++ } ++ bandwidth_estimation_->UpdateLossBasedEstimator( ++ report, result.delay_detector_state, probe_bitrate, ++ alr_start_time.has_value()); + if (result.updated) { + // Update the estimate in the ProbeController, in case we want to probe. + MaybeTriggerOnNetworkChanged(&update, report.feedback_time); +diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe.cc b/modules/congestion_controller/goog_cc/loss_based_bwe.cc +deleted file mode 100644 +index ed40fc723b..0000000000 +--- a/modules/congestion_controller/goog_cc/loss_based_bwe.cc ++++ /dev/null +@@ -1,293 +0,0 @@ +-/* +- * Copyright (c) 2025 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/congestion_controller/goog_cc/loss_based_bwe.h" +- +-#include <algorithm> +-#include <cstdint> +-#include <cstdio> +-#include <limits> +-#include <memory> +-#include <optional> +-#include <string> +-#include <utility> +- +-#include "api/field_trials_view.h" +-#include "api/transport/network_types.h" +-#include "api/units/data_rate.h" +-#include "api/units/time_delta.h" +-#include "api/units/timestamp.h" +-#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" +-#include "rtc_base/checks.h" +-#include "rtc_base/logging.h" +- +-namespace webrtc { +- +-namespace { +- +-constexpr float kDefaultLowLossThreshold = 0.02f; +-constexpr float kDefaultHighLossThreshold = 0.1f; +-constexpr DataRate kDefaultBitrateThreshold = DataRate::Zero(); +-constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis(1000); +-constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis(300); +-constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000); +-constexpr int kLimitNumPackets = 20; +- +-const char kBweLosExperiment[] = "WebRTC-BweLossExperiment"; +- +-bool BweLossExperimentIsEnabled(const FieldTrialsView& field_trials) { +- return field_trials.IsEnabled(kBweLosExperiment); +-} +- +-bool ReadBweLossExperimentParameters(const FieldTrialsView& field_trials, +- float* low_loss_threshold, +- float* high_loss_threshold, +- uint32_t* bitrate_threshold_kbps) { +- RTC_DCHECK(low_loss_threshold); +- RTC_DCHECK(high_loss_threshold); +- RTC_DCHECK(bitrate_threshold_kbps); +- std::string experiment_string = field_trials.Lookup(kBweLosExperiment); +- int parsed_values = +- sscanf(experiment_string.c_str(), "Enabled-%f,%f,%u", low_loss_threshold, +- high_loss_threshold, bitrate_threshold_kbps); +- if (parsed_values == 3) { +- RTC_CHECK_GT(*low_loss_threshold, 0.0f) +- << "Loss threshold must be greater than 0."; +- RTC_CHECK_LE(*low_loss_threshold, 1.0f) +- << "Loss threshold must be less than or equal to 1."; +- RTC_CHECK_GT(*high_loss_threshold, 0.0f) +- << "Loss threshold must be greater than 0."; +- RTC_CHECK_LE(*high_loss_threshold, 1.0f) +- << "Loss threshold must be less than or equal to 1."; +- RTC_CHECK_LE(*low_loss_threshold, *high_loss_threshold) +- << "The low loss threshold must be less than or equal to the high loss " +- "threshold."; +- RTC_CHECK_GE(*bitrate_threshold_kbps, 0) +- << "Bitrate threshold can't be negative."; +- RTC_CHECK_LT(*bitrate_threshold_kbps, +- std::numeric_limits<int>::max() / 1000) +- << "Bitrate must be smaller enough to avoid overflows."; +- return true; +- } +- RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweLossExperiment " +- "experiment from field trial string. Using default."; +- *low_loss_threshold = kDefaultLowLossThreshold; +- *high_loss_threshold = kDefaultHighLossThreshold; +- *bitrate_threshold_kbps = kDefaultBitrateThreshold.kbps(); +- return false; +-} +-} // namespace +- +-LossBasedBwe::LossBasedBwe(const FieldTrialsView* field_trials) +- : field_trials_(field_trials), +- loss_based_bwe_v2_(std::make_unique<LossBasedBweV2>(field_trials)), +- low_loss_threshold_(kDefaultLowLossThreshold), +- high_loss_threshold_(kDefaultHighLossThreshold), +- bitrate_threshold_(kDefaultBitrateThreshold) { +- if (BweLossExperimentIsEnabled(*field_trials)) { +- uint32_t bitrate_threshold_kbps; +- if (ReadBweLossExperimentParameters(*field_trials, &low_loss_threshold_, +- &high_loss_threshold_, +- &bitrate_threshold_kbps)) { +- RTC_LOG(LS_INFO) << "Enabled BweLossExperiment with parameters " +- << low_loss_threshold_ << ", " << high_loss_threshold_ +- << ", " << bitrate_threshold_kbps; +- bitrate_threshold_ = DataRate::KilobitsPerSec(bitrate_threshold_kbps); +- } +- } +-} +- +-void LossBasedBwe::OnTransportPacketsFeedback( +- const TransportPacketsFeedback& report, +- DataRate delay_based, +- std::optional<DataRate> acknowledged_bitrate, +- bool is_probe_rate, +- bool in_alr) { +- if (is_probe_rate) { +- // delay_based bitrate overrides loss based BWE unless +- // loss_based_bandwidth_estimator_v2_ is used or until +- // loss_based_bandwidth_estimator_v2_ is ready. +- SetStartRate(delay_based); +- } +- delay_based_bwe_ = delay_based; +- if (!loss_based_bwe_v2_->IsEnabled()) { +- return; +- } +- if (acknowledged_bitrate.has_value()) { +- loss_based_bwe_v2_->SetAcknowledgedBitrate(*acknowledged_bitrate); +- } +- loss_based_bwe_v2_->UpdateBandwidthEstimate(report.packet_feedbacks, +- delay_based, in_alr); +-} +- +-void LossBasedBwe::OnRouteChanged() { +- current_state_ = LossBasedState::kDelayBasedEstimate; +- lost_packets_since_last_loss_update_ = 0; +- expected_packets_since_last_loss_update_ = 0; +- min_bitrate_history_.clear(); +- delay_based_bwe_ = DataRate::PlusInfinity(); +- fallback_estimate_ = DataRate::Zero(); +- has_decreased_since_last_fraction_loss_ = false; +- last_loss_feedback_ = Timestamp::MinusInfinity(); +- last_loss_packet_report_ = Timestamp::MinusInfinity(); +- last_fraction_loss_ = 0; +- last_logged_fraction_loss_ = 0; +- last_round_trip_time_ = TimeDelta::Zero(); +- time_last_decrease_ = Timestamp::MinusInfinity(); +- first_report_time_ = Timestamp::MinusInfinity(); +- loss_based_bwe_v2_ = std::make_unique<LossBasedBweV2>(field_trials_); +-} +- +-void LossBasedBwe::SetConfiguredMinMaxBitrate(DataRate min_rate, +- DataRate max_rate) { +- configured_min_rate_ = min_rate; +- configured_max_rate_ = max_rate; +- loss_based_bwe_v2_->SetMinMaxBitrate(min_rate, max_rate); +-} +- +-void LossBasedBwe::SetStartRate(DataRate fallback_rate) { +- // Clear last sent bitrate history so the new value can be used directly +- // and not capped. +- min_bitrate_history_.clear(); +- fallback_estimate_ = fallback_rate; +-} +- +-void LossBasedBwe::OnPacketLossReport(int64_t packets_lost, +- int64_t packets_received, +- TimeDelta round_trip_time, +- Timestamp at_time) { +- last_loss_feedback_ = at_time; +- last_round_trip_time_ = round_trip_time; +- if (first_report_time_.IsInfinite()) { +- first_report_time_ = at_time; +- } +- int64_t number_of_packets = packets_lost + packets_received; +- // Check sequence number diff and weight loss report +- if (number_of_packets <= 0) { +- return; +- } +- int64_t expected = +- expected_packets_since_last_loss_update_ + number_of_packets; +- +- // Don't generate a loss rate until it can be based on enough packets. +- if (expected < kLimitNumPackets) { +- // Accumulate reports. +- expected_packets_since_last_loss_update_ = expected; +- lost_packets_since_last_loss_update_ += packets_lost; +- return; +- } +- +- has_decreased_since_last_fraction_loss_ = false; +- int64_t lost_q8 = +- std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost, 0) +- << 8; +- last_fraction_loss_ = std::min<int>(lost_q8 / expected, 255); +- +- // Reset accumulators. +- lost_packets_since_last_loss_update_ = 0; +- expected_packets_since_last_loss_update_ = 0; +- last_loss_packet_report_ = at_time; +-} +- +-bool LossBasedBwe::OnPeriodicProcess(Timestamp at_time) { +- UpdateMinHistory(at_time); +- if (loss_based_bwe_v2_->IsReady()) { +- return false; +- } +- +- TimeDelta time_since_loss_packet_report = at_time - last_loss_packet_report_; +- if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) { +- // We only care about loss above a given bitrate threshold. +- float loss = last_fraction_loss_ / 256.0f; +- // We only make decisions based on loss when the bitrate is above a +- // threshold. This is a crude way of handling loss which is uncorrelated +- // to congestion. +- if (fallback_estimate_ < bitrate_threshold_ || +- loss <= low_loss_threshold_) { +- // Loss < 2%: Increase rate by 8% of the min bitrate in the last +- // kBweIncreaseInterval. +- // Note that by remembering the bitrate over the last second one can +- // rampup up one second faster than if only allowed to start ramping +- // at 8% per second rate now. E.g.: +- // If sending a constant 100kbps it can rampup immediately to 108kbps +- // whenever a receiver report is received with lower packet loss. +- // If instead one would do: current_bitrate_ *= 1.08^(delta time), +- // it would take over one second since the lower packet loss to +- // achieve 108kbps. +- // Add 1 kbps extra, just to make sure that we do not get stuck +- // (gives a little extra increase at low rates, negligible at higher +- // rates). +- UpdateFallbackEstimate( +- DataRate::BitsPerSec( +- min_bitrate_history_.front().second.bps() * 1.08 + 0.5) + +- DataRate::BitsPerSec(1000)); +- return true; +- } else if (fallback_estimate_ > bitrate_threshold_) { +- if (loss <= high_loss_threshold_) { +- // Loss between 2% - 10%: Do nothing. +- } else { +- // Loss > 10%: Limit the rate decreases to once a kBweDecreaseInterval +- // + rtt. +- if (!has_decreased_since_last_fraction_loss_ && +- (at_time - time_last_decrease_) >= +- (kBweDecreaseInterval + last_round_trip_time_)) { +- time_last_decrease_ = at_time; +- +- // Reduce rate: +- // newRate = rate * (1 - 0.5*lossRate); +- // where packetLoss = 256*lossRate; +- UpdateFallbackEstimate(DataRate::BitsPerSec( +- (fallback_estimate_.bps() * +- static_cast<double>(512 - last_fraction_loss_)) / +- 512.0)); +- has_decreased_since_last_fraction_loss_ = true; +- return true; +- } +- } +- } +- } +- return false; +-} +- +-void LossBasedBwe::UpdateMinHistory(Timestamp at_time) { +- // Remove old data points from history. +- // Since history precision is in ms, add one so it is able to increase +- // bitrate if it is off by as little as 0.5ms. +- while (!min_bitrate_history_.empty() && +- at_time - min_bitrate_history_.front().first + TimeDelta::Millis(1) > +- kBweIncreaseInterval) { +- min_bitrate_history_.pop_front(); +- } +- +- // Typical minimum sliding-window algorithm: Pop values higher than current +- // bitrate before pushing it. +- while (!min_bitrate_history_.empty() && +- fallback_estimate_ <= min_bitrate_history_.back().second) { +- min_bitrate_history_.pop_back(); +- } +- +- min_bitrate_history_.push_back(std::make_pair(at_time, fallback_estimate_)); +-} +- +-DataRate LossBasedBwe::GetEstimate() { +- if (loss_based_bwe_v2_->IsReady()) { +- LossBasedBweV2::Result result = loss_based_bwe_v2_->GetLossBasedResult(); +- current_state_ = result.state; +- return result.bandwidth_estimate; +- } +- return fallback_estimate_; +-} +- +-void LossBasedBwe::UpdateFallbackEstimate(DataRate new_estimate) { +- fallback_estimate_ = std::min({delay_based_bwe_, new_estimate}); +- fallback_estimate_ = std::max(configured_min_rate_, new_estimate); +-} +- +-} // namespace webrtc +diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe.h b/modules/congestion_controller/goog_cc/loss_based_bwe.h +deleted file mode 100644 +index b2ff8f413c..0000000000 +--- a/modules/congestion_controller/goog_cc/loss_based_bwe.h ++++ /dev/null +@@ -1,107 +0,0 @@ +-/* +- * Copyright (c) 2025 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_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_H_ +-#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_H_ +- +-#include <cstdint> +-#include <deque> +-#include <memory> +-#include <optional> +-#include <utility> +- +-#include "api/field_trials_view.h" +-#include "api/transport/network_types.h" +-#include "api/units/data_rate.h" +-#include "api/units/time_delta.h" +-#include "api/units/timestamp.h" +-#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" +- +-namespace webrtc { +- +-// Estimates bandwidth available to WebRTC if there is packet loss. +-// The estimate will depend on loss calculated from transport feedback if it +-// exist, or (RTCP) receiver report otherwise. +-class LossBasedBwe { +- public: +- explicit LossBasedBwe(const FieldTrialsView* field_trials); +- +- // Called when the network route change. Resets state. +- void OnRouteChanged(); +- +- // Called when new transport feedback is received. +- void OnTransportPacketsFeedback(const TransportPacketsFeedback& report, +- DataRate delay_based, +- std::optional<DataRate> acknowledged_bitrate, +- bool is_probe_rate, +- bool in_alr); +- +- // Called when a new loss report (RTCP receiver report) is received. +- void OnPacketLossReport(int64_t packets_lost, +- int64_t packets_received, +- TimeDelta round_trip_time, +- Timestamp at_time); +- +- // Returns true if estimate changed. +- bool OnPeriodicProcess(Timestamp at_time); +- +- void SetConfiguredMinMaxBitrate(DataRate min_rate, DataRate max_rate); +- // Sets the rate used as reference if there is no transport feedback. It is +- // also used as loss based estimate until enough transport feedback messages +- // has been received. +- void SetStartRate(DataRate fallback_rate); +- +- LossBasedState state() const { return current_state_; } +- DataRate GetEstimate(); +- +- // Returns (number of packets lost << 8) / total number of packets. There has +- // to be at least 20 packets received or lost between each update. +- uint8_t fraction_loss() const { return last_fraction_loss_; } +- +- private: +- // Updates history of min bitrates. +- // After this method returns min_bitrate_history_.front().second contains the +- // min bitrate used during last kBweIncreaseIntervalMs. +- void UpdateMinHistory(Timestamp at_time); +- +- void UpdateFallbackEstimate(DataRate new_estimate); +- +- const FieldTrialsView* field_trials_; +- std::unique_ptr<LossBasedBweV2> loss_based_bwe_v2_; +- +- DataRate configured_min_rate_; +- DataRate configured_max_rate_; +- DataRate delay_based_bwe_ = DataRate::PlusInfinity(); +- +- DataRate fallback_estimate_ = DataRate::Zero(); +- LossBasedState current_state_ = LossBasedState::kDelayBasedEstimate; +- +- TimeDelta last_round_trip_time_; +- int lost_packets_since_last_loss_update_ = 0; +- int expected_packets_since_last_loss_update_ = 0; +- // State variables used before LossBasedBweV2 is ready to be used or if +- // LossBasedBweV2 is disabled. +- std::deque<std::pair<Timestamp, DataRate> > min_bitrate_history_; +- bool has_decreased_since_last_fraction_loss_ = false; +- Timestamp time_last_decrease_ = Timestamp::MinusInfinity(); +- float low_loss_threshold_; +- float high_loss_threshold_; +- DataRate bitrate_threshold_; +- +- Timestamp first_report_time_ = Timestamp::MinusInfinity(); +- Timestamp last_loss_feedback_ = Timestamp::MinusInfinity(); +- +- Timestamp last_loss_packet_report_ = Timestamp::MinusInfinity(); +- uint8_t last_fraction_loss_ = 0; +- uint8_t last_logged_fraction_loss_ = 0; +-}; +- +-} // namespace webrtc +-#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_H_ +diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_unittest.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_unittest.cc +deleted file mode 100644 +index 52561915ea..0000000000 +--- a/modules/congestion_controller/goog_cc/loss_based_bwe_unittest.cc ++++ /dev/null +@@ -1,157 +0,0 @@ +-/* +- * Copyright (c) 2025 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/congestion_controller/goog_cc/loss_based_bwe.h" +- +-#include <optional> +- +-#include "api/field_trials.h" +-#include "api/transport/network_types.h" +-#include "api/units/data_rate.h" +-#include "api/units/time_delta.h" +-#include "api/units/timestamp.h" +-#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" +-#include "test/create_test_field_trials.h" +-#include "test/gtest.h" +- +-namespace webrtc { +-namespace { +- +-TEST(LossBasedBweTest, ReturnFractionLoss) { +- FieldTrials field_trials = CreateTestFieldTrials(); +- LossBasedBwe loss_based_bwe(&field_trials); +- loss_based_bwe.SetConfiguredMinMaxBitrate(DataRate::KilobitsPerSec(50), +- DataRate::KilobitsPerSec(500)); +- loss_based_bwe.SetStartRate(DataRate::KilobitsPerSec(100)); +- EXPECT_EQ(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- +- // 25% packet loss. +- loss_based_bwe.OnPacketLossReport(/*packets_lost=*/5, +- /*packets_received=*/20, +- /*round_trip_time=*/TimeDelta::Millis(10), +- Timestamp::Seconds(123)); +- EXPECT_EQ(loss_based_bwe.fraction_loss(), (5 << 8) / (20 + 5)); +- +- loss_based_bwe.OnPacketLossReport( +- /*packets_lost=*/0, +- /*packets_received=*/2, +- /*round_trip_time=*/TimeDelta::Millis(10), +- Timestamp::Seconds(123) + TimeDelta::Millis(50)); +- // Not enough packets for a new update. Expect old value. +- EXPECT_EQ(loss_based_bwe.fraction_loss(), (5 << 8) / (20 + 5)); +- +- loss_based_bwe.OnPacketLossReport( +- /*packets_lost=*/0, +- /*packets_received=*/20, +- /*round_trip_time=*/TimeDelta::Millis(10), +- Timestamp::Seconds(123) + TimeDelta::Millis(70)); +- EXPECT_EQ(loss_based_bwe.fraction_loss(), 0); +-} +- +-// Test that BWE react to loss even if no transport feedback is received. +-TEST(LossBasedBweTest, EstimateReactToLossReport) { +- FieldTrials field_trials = CreateTestFieldTrials(); +- LossBasedBwe loss_based_bwe(&field_trials); +- loss_based_bwe.SetConfiguredMinMaxBitrate(DataRate::KilobitsPerSec(50), +- DataRate::KilobitsPerSec(500)); +- loss_based_bwe.SetStartRate(DataRate::KilobitsPerSec(100)); +- EXPECT_EQ(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- +- Timestamp now = Timestamp::Seconds(123); +- for (int i = 0; i < 3; ++i) { +- now += TimeDelta::Millis(30); +- // 25% packet loss. +- loss_based_bwe.OnPacketLossReport( +- /*packets_lost=*/5, +- /*packets_received=*/20, +- /*round_trip_time=*/TimeDelta::Millis(10), now); +- loss_based_bwe.OnPeriodicProcess(now); +- } +- EXPECT_LT(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- // V0 loss based estimator does not change state. Probing is still allowed? +- EXPECT_EQ(loss_based_bwe.state(), LossBasedState::kDelayBasedEstimate); +- +- // If there is no loss, BWE eventually increase to current delay based +- // estimate. +- while (now < Timestamp::Seconds(123) + TimeDelta::Seconds(20)) { +- now += TimeDelta::Millis(30); +- loss_based_bwe.OnPacketLossReport( +- /*packets_lost=*/0, +- /*packets_received=*/20, +- /*round_trip_time=*/TimeDelta::Millis(10), now); +- loss_based_bwe.OnPeriodicProcess(now); +- } +- EXPECT_GT(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- EXPECT_LE(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(500)); +- EXPECT_EQ(loss_based_bwe.state(), LossBasedState::kDelayBasedEstimate); +-} +- +-TEST(LossBasedBweTest, IsProbeRateResetBweEvenIfLossLimitedInStartPhase) { +- FieldTrials field_trials = CreateTestFieldTrials(); +- LossBasedBwe loss_based_bwe(&field_trials); +- loss_based_bwe.SetConfiguredMinMaxBitrate(DataRate::KilobitsPerSec(50), +- DataRate::KilobitsPerSec(500)); +- loss_based_bwe.SetStartRate(DataRate::KilobitsPerSec(100)); +- ASSERT_EQ(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- +- Timestamp now = Timestamp::Seconds(123); +- for (int i = 0; i < 3; ++i) { +- now += TimeDelta::Millis(30); +- // 25% packet loss. +- loss_based_bwe.OnPacketLossReport( +- /*packets_lost=*/5, +- /*packets_received=*/20, +- /*round_trip_time=*/TimeDelta::Millis(10), now); +- loss_based_bwe.OnPeriodicProcess(now); +- } +- ASSERT_LT(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- +- TransportPacketsFeedback feedback; +- feedback.feedback_time = now; +- loss_based_bwe.OnTransportPacketsFeedback( +- feedback, DataRate::KilobitsPerSec(200), +- /*acknowledged_bitrate=*/std::nullopt, +- /*is_probe_rate=*/true, +- /*in_alr=*/false); +- EXPECT_EQ(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(200)); +-} +- +-TEST(LossBasedBweTest, DelayBasedBweDoesNotResetBweIfLossLimitedInStartPhase) { +- FieldTrials field_trials = CreateTestFieldTrials(); +- LossBasedBwe loss_based_bwe(&field_trials); +- loss_based_bwe.SetConfiguredMinMaxBitrate(DataRate::KilobitsPerSec(50), +- DataRate::KilobitsPerSec(500)); +- loss_based_bwe.SetStartRate(DataRate::KilobitsPerSec(100)); +- ASSERT_EQ(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- +- Timestamp now = Timestamp::Seconds(123); +- for (int i = 0; i < 3; ++i) { +- now += TimeDelta::Millis(30); +- // 25% packet loss. +- loss_based_bwe.OnPacketLossReport( +- /*packets_lost=*/5, +- /*packets_received=*/20, +- /*round_trip_time=*/TimeDelta::Millis(10), now); +- loss_based_bwe.OnPeriodicProcess(now); +- } +- ASSERT_LT(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +- +- TransportPacketsFeedback feedback; +- feedback.feedback_time = now; +- loss_based_bwe.OnTransportPacketsFeedback( +- feedback, DataRate::KilobitsPerSec(200), +- /*acknowledged_bitrate=*/std::nullopt, +- /*is_probe_rate=*/false, +- /*in_alr=*/false); +- EXPECT_LT(loss_based_bwe.GetEstimate(), DataRate::KilobitsPerSec(100)); +-} +- +-} // anonymous namespace +-} // namespace webrtc +diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc +index b4c6e1cb6a..03c3f61de3 100644 +--- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc ++++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc +@@ -13,17 +13,20 @@ + #include <algorithm> + #include <cstdint> + #include <cstdio> ++#include <limits> + #include <memory> + #include <optional> ++#include <string> ++#include <utility> + + #include "api/field_trials_view.h" + #include "api/rtc_event_log/rtc_event_log.h" ++#include "api/transport/bandwidth_usage.h" + #include "api/transport/network_types.h" + #include "api/units/data_rate.h" + #include "api/units/time_delta.h" + #include "api/units/timestamp.h" + #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" +-#include "modules/congestion_controller/goog_cc/loss_based_bwe.h" + #include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" + #include "modules/remote_bitrate_estimator/include/bwe_defines.h" + #include "rtc_base/checks.h" +@@ -33,12 +36,20 @@ + + namespace webrtc { + namespace { +- ++constexpr TimeDelta kBweIncreaseInterval = TimeDelta::Millis(1000); ++constexpr TimeDelta kBweDecreaseInterval = TimeDelta::Millis(300); + constexpr TimeDelta kStartPhase = TimeDelta::Millis(2000); + constexpr TimeDelta kBweConverganceTime = TimeDelta::Millis(20000); ++constexpr int kLimitNumPackets = 20; + constexpr DataRate kDefaultMaxBitrate = DataRate::BitsPerSec(1000000000); + constexpr TimeDelta kLowBitrateLogPeriod = TimeDelta::Millis(10000); + constexpr TimeDelta kRtcEventLogPeriod = TimeDelta::Millis(5000); ++// Expecting that RTCP feedback is sent uniformly within [0.5, 1.5]s intervals. ++constexpr TimeDelta kMaxRtcpFeedbackInterval = TimeDelta::Millis(5000); ++ ++constexpr float kDefaultLowLossThreshold = 0.02f; ++constexpr float kDefaultHighLossThreshold = 0.1f; ++constexpr DataRate kDefaultBitrateThreshold = DataRate::Zero(); + + struct UmaRampUpMetric { + const char* metric_name; +@@ -52,6 +63,49 @@ const UmaRampUpMetric kUmaRampupMetrics[] = { + const size_t kNumUmaRampupMetrics = + sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]); + ++const char kBweLosExperiment[] = "WebRTC-BweLossExperiment"; ++ ++bool BweLossExperimentIsEnabled(const FieldTrialsView& field_trials) { ++ return field_trials.IsEnabled(kBweLosExperiment); ++} ++ ++bool ReadBweLossExperimentParameters(const FieldTrialsView& field_trials, ++ float* low_loss_threshold, ++ float* high_loss_threshold, ++ uint32_t* bitrate_threshold_kbps) { ++ RTC_DCHECK(low_loss_threshold); ++ RTC_DCHECK(high_loss_threshold); ++ RTC_DCHECK(bitrate_threshold_kbps); ++ std::string experiment_string = field_trials.Lookup(kBweLosExperiment); ++ int parsed_values = ++ sscanf(experiment_string.c_str(), "Enabled-%f,%f,%u", low_loss_threshold, ++ high_loss_threshold, bitrate_threshold_kbps); ++ if (parsed_values == 3) { ++ RTC_CHECK_GT(*low_loss_threshold, 0.0f) ++ << "Loss threshold must be greater than 0."; ++ RTC_CHECK_LE(*low_loss_threshold, 1.0f) ++ << "Loss threshold must be less than or equal to 1."; ++ RTC_CHECK_GT(*high_loss_threshold, 0.0f) ++ << "Loss threshold must be greater than 0."; ++ RTC_CHECK_LE(*high_loss_threshold, 1.0f) ++ << "Loss threshold must be less than or equal to 1."; ++ RTC_CHECK_LE(*low_loss_threshold, *high_loss_threshold) ++ << "The low loss threshold must be less than or equal to the high loss " ++ "threshold."; ++ RTC_CHECK_GE(*bitrate_threshold_kbps, 0) ++ << "Bitrate threshold can't be negative."; ++ RTC_CHECK_LT(*bitrate_threshold_kbps, ++ std::numeric_limits<int>::max() / 1000) ++ << "Bitrate must be smaller enough to avoid overflows."; ++ return true; ++ } ++ RTC_LOG(LS_WARNING) << "Failed to parse parameters for BweLossExperiment " ++ "experiment from field trial string. Using default."; ++ *low_loss_threshold = kDefaultLowLossThreshold; ++ *high_loss_threshold = kDefaultHighLossThreshold; ++ *bitrate_threshold_kbps = kDefaultBitrateThreshold.kbps(); ++ return false; ++} + } // namespace + + RttBasedBackoff::RttBasedBackoff(const FieldTrialsView& key_value_config) +@@ -96,54 +150,87 @@ RttBasedBackoff::~RttBasedBackoff() = default; + SendSideBandwidthEstimation::SendSideBandwidthEstimation( + const FieldTrialsView* key_value_config, + RtcEventLog* event_log) +- : rtt_backoff_(*key_value_config), +- loss_based_bwe_(key_value_config), +- last_logged_fraction_loss_(0), +- last_round_trip_time_(TimeDelta::Zero()), +- receiver_limit_(DataRate::PlusInfinity()), +- delay_based_limit_(DataRate::PlusInfinity()), +- loss_based_limit_(DataRate::PlusInfinity()), +- current_target_(kCongestionControllerMinBitrate), ++ : key_value_config_(key_value_config), ++ rtt_backoff_(*key_value_config), ++ lost_packets_since_last_loss_update_(0), ++ expected_packets_since_last_loss_update_(0), ++ current_target_(DataRate::Zero()), + last_logged_target_(DataRate::Zero()), + min_bitrate_configured_(kCongestionControllerMinBitrate), + max_bitrate_configured_(kDefaultMaxBitrate), +- + last_low_bitrate_log_(Timestamp::MinusInfinity()), +- time_last_decrease_due_to_rtt_(Timestamp::MinusInfinity()), +- first_loss_report_time_(Timestamp::MinusInfinity()), ++ has_decreased_since_last_fraction_loss_(false), ++ last_loss_feedback_(Timestamp::MinusInfinity()), ++ last_loss_packet_report_(Timestamp::MinusInfinity()), ++ last_fraction_loss_(0), ++ last_logged_fraction_loss_(0), ++ last_round_trip_time_(TimeDelta::Zero()), ++ receiver_limit_(DataRate::PlusInfinity()), ++ delay_based_limit_(DataRate::PlusInfinity()), ++ time_last_decrease_(Timestamp::MinusInfinity()), ++ first_report_time_(Timestamp::MinusInfinity()), + initially_lost_packets_(0), + bitrate_at_2_seconds_(DataRate::Zero()), + uma_update_state_(kNoUpdate), + uma_rtt_state_(kNoUpdate), + rampup_uma_stats_updated_(kNumUmaRampupMetrics, false), + event_log_(event_log), +- last_rtc_event_log_(Timestamp::MinusInfinity()) { ++ last_rtc_event_log_(Timestamp::MinusInfinity()), ++ low_loss_threshold_(kDefaultLowLossThreshold), ++ high_loss_threshold_(kDefaultHighLossThreshold), ++ bitrate_threshold_(kDefaultBitrateThreshold), ++ loss_based_bandwidth_estimator_v2_(new LossBasedBweV2(key_value_config)), ++ loss_based_state_(LossBasedState::kDelayBasedEstimate), ++ disable_receiver_limit_caps_only_("Disabled") { + RTC_DCHECK(event_log); +- loss_based_bwe_.SetConfiguredMinMaxBitrate(min_bitrate_configured_, +- max_bitrate_configured_); ++ if (BweLossExperimentIsEnabled(*key_value_config_)) { ++ uint32_t bitrate_threshold_kbps; ++ if (ReadBweLossExperimentParameters( ++ *key_value_config_, &low_loss_threshold_, &high_loss_threshold_, ++ &bitrate_threshold_kbps)) { ++ RTC_LOG(LS_INFO) << "Enabled BweLossExperiment with parameters " ++ << low_loss_threshold_ << ", " << high_loss_threshold_ ++ << ", " << bitrate_threshold_kbps; ++ bitrate_threshold_ = DataRate::KilobitsPerSec(bitrate_threshold_kbps); ++ } ++ } ++ ParseFieldTrial({&disable_receiver_limit_caps_only_}, ++ key_value_config->Lookup("WebRTC-Bwe-ReceiverLimitCapsOnly")); ++ if (LossBasedBandwidthEstimatorV2Enabled()) { ++ loss_based_bandwidth_estimator_v2_->SetMinMaxBitrate( ++ min_bitrate_configured_, max_bitrate_configured_); ++ } + } + + SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {} + + void SendSideBandwidthEstimation::OnRouteChange() { +- current_target_ = kCongestionControllerMinBitrate; ++ lost_packets_since_last_loss_update_ = 0; ++ expected_packets_since_last_loss_update_ = 0; ++ current_target_ = DataRate::Zero(); + min_bitrate_configured_ = kCongestionControllerMinBitrate; + max_bitrate_configured_ = kDefaultMaxBitrate; + last_low_bitrate_log_ = Timestamp::MinusInfinity(); ++ has_decreased_since_last_fraction_loss_ = false; ++ last_loss_feedback_ = Timestamp::MinusInfinity(); ++ last_loss_packet_report_ = Timestamp::MinusInfinity(); ++ last_fraction_loss_ = 0; + last_logged_fraction_loss_ = 0; + last_round_trip_time_ = TimeDelta::Zero(); + receiver_limit_ = DataRate::PlusInfinity(); + delay_based_limit_ = DataRate::PlusInfinity(); +- loss_based_limit_ = DataRate::PlusInfinity(); +- time_last_decrease_due_to_rtt_ = Timestamp::MinusInfinity(); +- first_loss_report_time_ = Timestamp::MinusInfinity(); ++ time_last_decrease_ = Timestamp::MinusInfinity(); ++ first_report_time_ = Timestamp::MinusInfinity(); + initially_lost_packets_ = 0; + bitrate_at_2_seconds_ = DataRate::Zero(); + uma_update_state_ = kNoUpdate; + uma_rtt_state_ = kNoUpdate; + last_rtc_event_log_ = Timestamp::MinusInfinity(); +- rtt_back_off_rate_ = std::nullopt; +- loss_based_bwe_.OnRouteChanged(); ++ if (LossBasedBandwidthEstimatorV2Enabled() && ++ loss_based_bandwidth_estimator_v2_->UseInStartPhase()) { ++ loss_based_bandwidth_estimator_v2_.reset( ++ new LossBasedBweV2(key_value_config_)); ++ } + } + + void SendSideBandwidthEstimation::SetBitrates( +@@ -153,12 +240,21 @@ void SendSideBandwidthEstimation::SetBitrates( + Timestamp at_time) { + SetMinMaxBitrate(min_bitrate, max_bitrate); + if (send_bitrate) { +- delay_based_limit_ = DataRate::PlusInfinity(); +- current_target_ = *send_bitrate; +- loss_based_bwe_.SetStartRate(*send_bitrate); ++ SetSendBitrate(*send_bitrate, at_time); + } + } + ++void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate, ++ Timestamp at_time) { ++ RTC_DCHECK_GT(bitrate, DataRate::Zero()); ++ // Reset to avoid being capped by the estimate. ++ delay_based_limit_ = DataRate::PlusInfinity(); ++ UpdateTargetBitrate(bitrate, at_time); ++ // Clear last sent bitrate history so the new value can be used directly ++ // and not capped. ++ min_bitrate_history_.clear(); ++} ++ + void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate, + DataRate max_bitrate) { + min_bitrate_configured_ = +@@ -168,8 +264,8 @@ void SendSideBandwidthEstimation::SetMinMaxBitrate(DataRate min_bitrate, + } else { + max_bitrate_configured_ = kDefaultMaxBitrate; + } +- loss_based_bwe_.SetConfiguredMinMaxBitrate(min_bitrate_configured_, +- max_bitrate_configured_); ++ loss_based_bandwidth_estimator_v2_->SetMinMaxBitrate(min_bitrate_configured_, ++ max_bitrate_configured_); + } + + int SendSideBandwidthEstimation::GetMinBitrate() const { +@@ -177,11 +273,14 @@ int SendSideBandwidthEstimation::GetMinBitrate() const { + } + + DataRate SendSideBandwidthEstimation::target_rate() const { +- return current_target_; ++ DataRate target = current_target_; ++ if (!disable_receiver_limit_caps_only_) ++ target = std::min(target, receiver_limit_); ++ return std::max(min_bitrate_configured_, target); + } + + LossBasedState SendSideBandwidthEstimation::loss_based_state() const { +- return loss_based_bwe_.state(); ++ return loss_based_state_; + } + + bool SendSideBandwidthEstimation::IsRttAboveLimit() const { +@@ -192,59 +291,78 @@ void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time, + DataRate bandwidth) { + // TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no + // limitation. +- DataRate estimate = bandwidth.IsZero() ? DataRate::PlusInfinity() : bandwidth; +- if (estimate != receiver_limit_) { +- receiver_limit_ = estimate; +- +- if (IsInStartPhase(at_time) && loss_based_bwe_.fraction_loss() == 0 && +- receiver_limit_ > current_target_ && +- delay_based_limit_ > receiver_limit_) { +- // Reset the (fallback) loss based estimator and trust the remote estimate +- // is a good starting rate. +- loss_based_bwe_.SetStartRate(receiver_limit_); +- loss_based_limit_ = loss_based_bwe_.GetEstimate(); +- } +- ApplyTargetLimits(at_time); +- } ++ receiver_limit_ = bandwidth.IsZero() ? DataRate::PlusInfinity() : bandwidth; ++ ApplyTargetLimits(at_time); + } + +-void SendSideBandwidthEstimation::OnTransportPacketsFeedback( +- const TransportPacketsFeedback& report, +- DataRate delay_based_estimate, ++void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time, ++ DataRate bitrate) { ++ // TODO(srte): Ensure caller passes PlusInfinity, not zero, to represent no ++ // limitation. ++ delay_based_limit_ = bitrate.IsZero() ? DataRate::PlusInfinity() : bitrate; ++ ApplyTargetLimits(at_time); ++} ++ ++void SendSideBandwidthEstimation::SetAcknowledgedRate( + std::optional<DataRate> acknowledged_rate, +- bool is_probe_rate, +- bool in_alr) { +- delay_based_estimate = delay_based_estimate.IsZero() +- ? DataRate::PlusInfinity() +- : delay_based_estimate; ++ Timestamp at_time) { + acknowledged_rate_ = acknowledged_rate; ++ if (!acknowledged_rate.has_value()) { ++ return; ++ } ++ if (LossBasedBandwidthEstimatorV2Enabled()) { ++ loss_based_bandwidth_estimator_v2_->SetAcknowledgedBitrate( ++ *acknowledged_rate); ++ } ++} + +- loss_based_bwe_.OnTransportPacketsFeedback( +- report, delay_based_estimate, acknowledged_rate_, is_probe_rate, in_alr); +- +- DataRate loss_based_estimate = loss_based_bwe_.GetEstimate(); +- if (loss_based_estimate != loss_based_limit_ || +- delay_based_limit_ != delay_based_estimate) { +- delay_based_limit_ = delay_based_estimate; +- loss_based_limit_ = loss_based_estimate; +- ApplyTargetLimits(report.feedback_time); ++void SendSideBandwidthEstimation::UpdateLossBasedEstimator( ++ const TransportPacketsFeedback& report, ++ BandwidthUsage /* delay_detector_state */, ++ std::optional<DataRate> /* probe_bitrate */, ++ bool in_alr) { ++ if (LossBasedBandwidthEstimatorV2Enabled()) { ++ loss_based_bandwidth_estimator_v2_->UpdateBandwidthEstimate( ++ report.packet_feedbacks, delay_based_limit_, in_alr); ++ UpdateEstimate(report.feedback_time); + } + } + + void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost, +- int64_t packets_received, ++ int64_t number_of_packets, + Timestamp at_time) { +- if (first_loss_report_time_.IsInfinite()) { +- first_loss_report_time_ = at_time; ++ last_loss_feedback_ = at_time; ++ if (first_report_time_.IsInfinite()) ++ first_report_time_ = at_time; ++ ++ // Check sequence number diff and weight loss report ++ if (number_of_packets > 0) { ++ int64_t expected = ++ expected_packets_since_last_loss_update_ + number_of_packets; ++ ++ // Don't generate a loss rate until it can be based on enough packets. ++ if (expected < kLimitNumPackets) { ++ // Accumulate reports. ++ expected_packets_since_last_loss_update_ = expected; ++ lost_packets_since_last_loss_update_ += packets_lost; ++ return; ++ } ++ ++ has_decreased_since_last_fraction_loss_ = false; ++ int64_t lost_q8 = ++ std::max<int64_t>(lost_packets_since_last_loss_update_ + packets_lost, ++ 0) ++ << 8; ++ last_fraction_loss_ = std::min<int>(lost_q8 / expected, 255); ++ ++ // Reset accumulators. ++ lost_packets_since_last_loss_update_ = 0; ++ expected_packets_since_last_loss_update_ = 0; ++ last_loss_packet_report_ = at_time; ++ UpdateEstimate(at_time); + } +- loss_based_bwe_.OnPacketLossReport(packets_lost, packets_received, +- last_round_trip_time_, at_time); ++ + UpdateUmaStatsPacketsLost(at_time, packets_lost); +- DataRate estimate = loss_based_bwe_.GetEstimate(); +- if (estimate != loss_based_limit_) { +- loss_based_limit_ = loss_based_bwe_.GetEstimate(); +- ApplyTargetLimits(at_time); +- } + } + + void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time, +@@ -255,7 +373,7 @@ void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time, + if (!rampup_uma_stats_updated_[i] && + bitrate_kbps.kbps() >= kUmaRampupMetrics[i].bitrate_kbps) { + RTC_HISTOGRAMS_COUNTS_100000(i, kUmaRampupMetrics[i].metric_name, +- (at_time - first_loss_report_time_).ms()); ++ (at_time - first_report_time_).ms()); + rampup_uma_stats_updated_[i] = true; + } + } +@@ -269,7 +387,7 @@ void SendSideBandwidthEstimation::UpdateUmaStatsPacketsLost(Timestamp at_time, + RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialBandwidthEstimate", + bitrate_at_2_seconds_.kbps(), 0, 2000, 50); + } else if (uma_update_state_ == kFirstDone && +- at_time - first_loss_report_time_ >= kBweConverganceTime) { ++ at_time - first_report_time_ >= kBweConverganceTime) { + uma_update_state_ = kDone; + int bitrate_diff_kbps = std::max( + bitrate_at_2_seconds_.kbps<int>() - bitrate_kbps.kbps<int>(), 0); +@@ -290,25 +408,111 @@ void SendSideBandwidthEstimation::UpdateRtt(TimeDelta rtt, Timestamp at_time) { + } + } + +-void SendSideBandwidthEstimation::OnPeriodicUpdate(Timestamp at_time) { ++void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { + if (rtt_backoff_.IsRttAboveLimit()) { +- if (at_time - time_last_decrease_due_to_rtt_ >= +- rtt_backoff_.drop_interval_ && ++ if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_ && + current_target_ > rtt_backoff_.bandwidth_floor_) { +- time_last_decrease_due_to_rtt_ = at_time; +- rtt_back_off_rate_ = ++ time_last_decrease_ = at_time; ++ DataRate new_bitrate = + std::max(current_target_ * rtt_backoff_.drop_fraction_, + rtt_backoff_.bandwidth_floor_.Get()); +- ApplyTargetLimits(at_time); ++ UpdateTargetBitrate(new_bitrate, at_time); ++ return; + } +- } else if (rtt_back_off_rate_.has_value()) { +- rtt_back_off_rate_ = std::nullopt; ++ // TODO(srte): This is likely redundant in most cases. + ApplyTargetLimits(at_time); ++ return; + } +- if (loss_based_bwe_.OnPeriodicProcess(at_time)) { +- loss_based_limit_ = loss_based_bwe_.GetEstimate(); ++ ++ // We trust the REMB and/or delay-based estimate during the first 2 seconds if ++ // we haven't had any packet loss reported, to allow startup bitrate probing. ++ if (last_fraction_loss_ == 0 && IsInStartPhase(at_time) && ++ !loss_based_bandwidth_estimator_v2_->ReadyToUseInStartPhase()) { ++ DataRate new_bitrate = current_target_; ++ // TODO(srte): We should not allow the new_bitrate to be larger than the ++ // receiver limit here. ++ if (receiver_limit_.IsFinite()) ++ new_bitrate = std::max(receiver_limit_, new_bitrate); ++ if (delay_based_limit_.IsFinite()) { ++ new_bitrate = std::max(delay_based_limit_, new_bitrate); ++ } ++ if (new_bitrate != current_target_) { ++ min_bitrate_history_.clear(); ++ min_bitrate_history_.push_back(std::make_pair(at_time, current_target_)); ++ UpdateTargetBitrate(new_bitrate, at_time); ++ return; ++ } ++ } ++ UpdateMinHistory(at_time); ++ if (last_loss_packet_report_.IsInfinite()) { ++ // No feedback received. ++ // TODO(srte): This is likely redundant in most cases. + ApplyTargetLimits(at_time); ++ return; ++ } ++ ++ if (LossBasedBandwidthEstimatorV2ReadyForUse()) { ++ LossBasedBweV2::Result result = ++ loss_based_bandwidth_estimator_v2_->GetLossBasedResult(); ++ loss_based_state_ = result.state; ++ UpdateTargetBitrate(result.bandwidth_estimate, at_time); ++ return; ++ } ++ ++ TimeDelta time_since_loss_packet_report = at_time - last_loss_packet_report_; ++ if (time_since_loss_packet_report < 1.2 * kMaxRtcpFeedbackInterval) { ++ // We only care about loss above a given bitrate threshold. ++ float loss = last_fraction_loss_ / 256.0f; ++ // We only make decisions based on loss when the bitrate is above a ++ // threshold. This is a crude way of handling loss which is uncorrelated ++ // to congestion. ++ if (current_target_ < bitrate_threshold_ || loss <= low_loss_threshold_) { ++ // Loss < 2%: Increase rate by 8% of the min bitrate in the last ++ // kBweIncreaseInterval. ++ // Note that by remembering the bitrate over the last second one can ++ // rampup up one second faster than if only allowed to start ramping ++ // at 8% per second rate now. E.g.: ++ // If sending a constant 100kbps it can rampup immediately to 108kbps ++ // whenever a receiver report is received with lower packet loss. ++ // If instead one would do: current_bitrate_ *= 1.08^(delta time), ++ // it would take over one second since the lower packet loss to achieve ++ // 108kbps. ++ DataRate new_bitrate = DataRate::BitsPerSec( ++ min_bitrate_history_.front().second.bps() * 1.08 + 0.5); ++ ++ // Add 1 kbps extra, just to make sure that we do not get stuck ++ // (gives a little extra increase at low rates, negligible at higher ++ // rates). ++ new_bitrate += DataRate::BitsPerSec(1000); ++ UpdateTargetBitrate(new_bitrate, at_time); ++ return; ++ } else if (current_target_ > bitrate_threshold_) { ++ if (loss <= high_loss_threshold_) { ++ // Loss between 2% - 10%: Do nothing. ++ } else { ++ // Loss > 10%: Limit the rate decreases to once a kBweDecreaseInterval ++ // + rtt. ++ if (!has_decreased_since_last_fraction_loss_ && ++ (at_time - time_last_decrease_) >= ++ (kBweDecreaseInterval + last_round_trip_time_)) { ++ time_last_decrease_ = at_time; ++ ++ // Reduce rate: ++ // newRate = rate * (1 - 0.5*lossRate); ++ // where packetLoss = 256*lossRate; ++ DataRate new_bitrate = DataRate::BitsPerSec( ++ (current_target_.bps() * ++ static_cast<double>(512 - last_fraction_loss_)) / ++ 512.0); ++ has_decreased_since_last_fraction_loss_ = true; ++ UpdateTargetBitrate(new_bitrate, at_time); ++ return; ++ } ++ } ++ } + } ++ // TODO(srte): This is likely redundant in most cases. ++ ApplyTargetLimits(at_time); + } + + void SendSideBandwidthEstimation::UpdatePropagationRtt( +@@ -323,8 +527,35 @@ void SendSideBandwidthEstimation::OnSentPacket(const SentPacket& sent_packet) { + } + + bool SendSideBandwidthEstimation::IsInStartPhase(Timestamp at_time) const { +- return first_loss_report_time_.IsInfinite() || +- at_time - first_loss_report_time_ < kStartPhase; ++ return first_report_time_.IsInfinite() || ++ at_time - first_report_time_ < kStartPhase; ++} ++ ++void SendSideBandwidthEstimation::UpdateMinHistory(Timestamp at_time) { ++ // Remove old data points from history. ++ // Since history precision is in ms, add one so it is able to increase ++ // bitrate if it is off by as little as 0.5ms. ++ while (!min_bitrate_history_.empty() && ++ at_time - min_bitrate_history_.front().first + TimeDelta::Millis(1) > ++ kBweIncreaseInterval) { ++ min_bitrate_history_.pop_front(); ++ } ++ ++ // Typical minimum sliding-window algorithm: Pop values higher than current ++ // bitrate before pushing it. ++ while (!min_bitrate_history_.empty() && ++ current_target_ <= min_bitrate_history_.back().second) { ++ min_bitrate_history_.pop_back(); ++ } ++ ++ min_bitrate_history_.push_back(std::make_pair(at_time, current_target_)); ++} ++ ++DataRate SendSideBandwidthEstimation::GetUpperLimit() const { ++ DataRate upper_limit = delay_based_limit_; ++ if (disable_receiver_limit_caps_only_) ++ upper_limit = std::min(upper_limit, receiver_limit_); ++ return std::min(upper_limit, max_bitrate_configured_); + } + + void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(DataRate bitrate, +@@ -339,28 +570,39 @@ void SendSideBandwidthEstimation::MaybeLogLowBitrateWarning(DataRate bitrate, + + void SendSideBandwidthEstimation::MaybeLogLossBasedEvent(Timestamp at_time) { + if (current_target_ != last_logged_target_ || +- loss_based_bwe_.fraction_loss() != last_logged_fraction_loss_ || ++ last_fraction_loss_ != last_logged_fraction_loss_ || + at_time - last_rtc_event_log_ > kRtcEventLogPeriod) { + event_log_->Log(std::make_unique<RtcEventBweUpdateLossBased>( +- current_target_.bps(), loss_based_bwe_.fraction_loss(), +- /*total_packets_ =*/0)); +- last_logged_fraction_loss_ = loss_based_bwe_.fraction_loss(); ++ current_target_.bps(), last_fraction_loss_, ++ expected_packets_since_last_loss_update_)); ++ last_logged_fraction_loss_ = last_fraction_loss_; + last_logged_target_ = current_target_; + last_rtc_event_log_ = at_time; + } + } + +-void SendSideBandwidthEstimation::ApplyTargetLimits(Timestamp at_time) { +- current_target_ = +- std::min({delay_based_limit_, receiver_limit_, +- rtt_back_off_rate_.value_or(DataRate::PlusInfinity()), +- loss_based_limit_, max_bitrate_configured_}); +- +- if (current_target_ < min_bitrate_configured_) { +- MaybeLogLowBitrateWarning(current_target_, at_time); +- current_target_ = min_bitrate_configured_; ++void SendSideBandwidthEstimation::UpdateTargetBitrate(DataRate new_bitrate, ++ Timestamp at_time) { ++ new_bitrate = std::min(new_bitrate, GetUpperLimit()); ++ if (new_bitrate < min_bitrate_configured_) { ++ MaybeLogLowBitrateWarning(new_bitrate, at_time); ++ new_bitrate = min_bitrate_configured_; + } ++ current_target_ = new_bitrate; + MaybeLogLossBasedEvent(at_time); + } + ++void SendSideBandwidthEstimation::ApplyTargetLimits(Timestamp at_time) { ++ UpdateTargetBitrate(current_target_, at_time); ++} ++ ++bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2Enabled() const { ++ return loss_based_bandwidth_estimator_v2_->IsEnabled(); ++} ++ ++bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2ReadyForUse() ++ const { ++ return loss_based_bandwidth_estimator_v2_->IsReady(); ++} ++ + } // namespace webrtc +diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h +index b68d9844fb..2795ba2386 100644 +--- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h ++++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h +@@ -15,15 +15,18 @@ + + #include <stdint.h> + ++#include <deque> ++#include <memory> + #include <optional> ++#include <utility> + #include <vector> + + #include "api/field_trials_view.h" ++#include "api/transport/bandwidth_usage.h" + #include "api/transport/network_types.h" + #include "api/units/data_rate.h" + #include "api/units/time_delta.h" + #include "api/units/timestamp.h" +-#include "modules/congestion_controller/goog_cc/loss_based_bwe.h" + #include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" + #include "rtc_base/experiments/field_trial_parser.h" + +@@ -68,20 +71,23 @@ class SendSideBandwidthEstimation { + // Return whether the current rtt is higher than the rtt limited configured in + // RttBasedBackoff. + bool IsRttAboveLimit() const; +- uint8_t fraction_loss() const { return loss_based_bwe_.fraction_loss(); } ++ uint8_t fraction_loss() const { return last_fraction_loss_; } + TimeDelta round_trip_time() const { return last_round_trip_time_; } + + // Call periodically to update estimate. +- void OnPeriodicUpdate(Timestamp at_time); ++ void UpdateEstimate(Timestamp at_time); + void OnSentPacket(const SentPacket& sent_packet); + void UpdatePropagationRtt(Timestamp at_time, TimeDelta propagation_rtt); + + // Call when we receive a RTCP message with TMMBR or REMB. + void UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth); + ++ // Call when a new delay-based estimate is available. ++ void UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate); ++ + // Call when we receive a RTCP message with a ReceiveBlock. + void UpdatePacketsLost(int64_t packets_lost, +- int64_t packets_received, ++ int64_t number_of_packets, + Timestamp at_time); + + // Call when we receive a RTCP message with a ReceiveBlock. +@@ -91,14 +97,15 @@ class SendSideBandwidthEstimation { + DataRate min_bitrate, + DataRate max_bitrate, + Timestamp at_time); ++ void SetSendBitrate(DataRate bitrate, Timestamp at_time); + void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate); + int GetMinBitrate() const; +- +- void OnTransportPacketsFeedback(const TransportPacketsFeedback& report, +- DataRate delay_based_estimate, +- std::optional<DataRate> acknowledged_rate, +- bool is_probe_rate, +- bool in_alr); ++ void SetAcknowledgedRate(std::optional<DataRate> acknowledged_rate, ++ Timestamp at_time); ++ void UpdateLossBasedEstimator(const TransportPacketsFeedback& report, ++ BandwidthUsage delay_detector_state, ++ std::optional<DataRate> probe_bitrate, ++ bool in_alr); + + private: + friend class GoogCcStatePrinter; +@@ -114,6 +121,9 @@ class SendSideBandwidthEstimation { + // min bitrate used during last kBweIncreaseIntervalMs. + void UpdateMinHistory(Timestamp at_time); + ++ // Gets the upper limit for the target bitrate. This is the minimum of the ++ // delay based limit, the receiver limit and the loss based controller limit. ++ DataRate GetUpperLimit() const; + // Prints a warning if `bitrate` if sufficiently long time has past since last + // warning. + void MaybeLogLowBitrateWarning(DataRate bitrate, Timestamp at_time); +@@ -121,34 +131,47 @@ class SendSideBandwidthEstimation { + // has changed, or sufficient time has passed since last stored event. + void MaybeLogLossBasedEvent(Timestamp at_time); + ++ // Cap `bitrate` to [min_bitrate_configured_, max_bitrate_configured_] and ++ // set `current_bitrate_` to the capped value and updates the event log. ++ void UpdateTargetBitrate(DataRate bitrate, Timestamp at_time); ++ // Applies lower and upper bounds to the current target rate. ++ // TODO(srte): This seems to be called even when limits haven't changed, that ++ // should be cleaned up. + void ApplyTargetLimits(Timestamp at_time); + ++ bool LossBasedBandwidthEstimatorV2Enabled() const; ++ bool LossBasedBandwidthEstimatorV2ReadyForUse() const; ++ + const FieldTrialsView* key_value_config_; + RttBasedBackoff rtt_backoff_; +- LossBasedBwe loss_based_bwe_; ++ ++ std::deque<std::pair<Timestamp, DataRate> > min_bitrate_history_; ++ ++ // incoming filters ++ int lost_packets_since_last_loss_update_; ++ int expected_packets_since_last_loss_update_; + + std::optional<DataRate> acknowledged_rate_; ++ DataRate current_target_; ++ DataRate last_logged_target_; ++ DataRate min_bitrate_configured_; ++ DataRate max_bitrate_configured_; ++ Timestamp last_low_bitrate_log_; ++ ++ bool has_decreased_since_last_fraction_loss_; ++ Timestamp last_loss_feedback_; ++ Timestamp last_loss_packet_report_; ++ uint8_t last_fraction_loss_; + uint8_t last_logged_fraction_loss_; + TimeDelta last_round_trip_time_; ++ + // The max bitrate as set by the receiver in the call. This is typically + // signalled using the REMB RTCP message and is used when we don't have any + // send side delay based estimate. + DataRate receiver_limit_; + DataRate delay_based_limit_; +- DataRate loss_based_limit_; +- +- // `rtt_back_off_rate_` is calculated in relation to a limit and can only be +- // lower than the limit. If not, it is nullopt. +- std::optional<DataRate> rtt_back_off_rate_; +- +- DataRate current_target_; // Current combined target rate. +- DataRate last_logged_target_; +- DataRate min_bitrate_configured_; +- DataRate max_bitrate_configured_; +- Timestamp last_low_bitrate_log_; +- +- Timestamp time_last_decrease_due_to_rtt_; +- Timestamp first_loss_report_time_; ++ Timestamp time_last_decrease_; ++ Timestamp first_report_time_; + int initially_lost_packets_; + DataRate bitrate_at_2_seconds_; + UmaState uma_update_state_; +@@ -156,6 +179,12 @@ class SendSideBandwidthEstimation { + std::vector<bool> rampup_uma_stats_updated_; + RtcEventLog* const event_log_; + Timestamp last_rtc_event_log_; ++ float low_loss_threshold_; ++ float high_loss_threshold_; ++ DataRate bitrate_threshold_; ++ std::unique_ptr<LossBasedBweV2> loss_based_bandwidth_estimator_v2_; ++ LossBasedState loss_based_state_; ++ FieldTrialFlag disable_receiver_limit_caps_only_; + }; + } // namespace webrtc + #endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_SEND_SIDE_BANDWIDTH_ESTIMATION_H_ +diff --git a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc +index 93b76fc366..5efd337710 100644 +--- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc ++++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation_unittest.cc +@@ -11,13 +11,10 @@ + #include "modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h" + + #include <cstdint> +-#include <optional> + + #include "api/field_trials.h" + #include "api/rtc_event_log/rtc_event.h" +-#include "api/transport/network_types.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 "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h" +@@ -44,87 +41,70 @@ MATCHER(LossBasedBweUpdateWithBitrateAndLossFraction, "") { + return bwe_event->bitrate_bps() > 0 && bwe_event->fraction_loss() > 0; + } + +-TEST(SendSideBweTest, InitialRembAppliesImmediately) { ++void TestProbing(bool use_delay_based) { + ::testing::NiceMock<MockRtcEventLog> event_log; + FieldTrials key_value_config = CreateTestFieldTrials(); + SendSideBandwidthEstimation bwe(&key_value_config, &event_log); + int64_t now_ms = 0; +- bwe.SetBitrates(DataRate::BitsPerSec(200000), DataRate::BitsPerSec(100000), +- DataRate::BitsPerSec(1500000), Timestamp::Millis(now_ms)); ++ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(100000), ++ DataRate::BitsPerSec(1500000)); ++ bwe.SetSendBitrate(DataRate::BitsPerSec(200000), Timestamp::Millis(now_ms)); + + const int kRembBps = 1000000; + const int kSecondRembBps = kRembBps + 500000; + +- bwe.UpdatePacketsLost(/*packets_lost=*/0, /*packets_received=*/1, ++ bwe.UpdatePacketsLost(/*packets_lost=*/0, /*number_of_packets=*/1, + Timestamp::Millis(now_ms)); + bwe.UpdateRtt(TimeDelta::Millis(50), Timestamp::Millis(now_ms)); + + // Initial REMB applies immediately. +- bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), +- DataRate::BitsPerSec(kRembBps)); +- bwe.OnPeriodicUpdate(Timestamp::Millis(now_ms)); +- EXPECT_EQ(bwe.target_rate().bps(), kRembBps); ++ if (use_delay_based) { ++ bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), ++ DataRate::BitsPerSec(kRembBps)); ++ } else { ++ bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), ++ DataRate::BitsPerSec(kRembBps)); ++ } ++ bwe.UpdateEstimate(Timestamp::Millis(now_ms)); ++ EXPECT_EQ(kRembBps, bwe.target_rate().bps()); + +- // Second REMB, after startphase doesn't apply immediately. ++ // Second REMB doesn't apply immediately. + now_ms += 2001; +- bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), +- DataRate::BitsPerSec(kSecondRembBps)); +- bwe.OnPeriodicUpdate(Timestamp::Millis(now_ms)); ++ if (use_delay_based) { ++ bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), ++ DataRate::BitsPerSec(kSecondRembBps)); ++ } else { ++ bwe.UpdateReceiverEstimate(Timestamp::Millis(now_ms), ++ DataRate::BitsPerSec(kSecondRembBps)); ++ } ++ bwe.UpdateEstimate(Timestamp::Millis(now_ms)); ++ EXPECT_EQ(kRembBps, bwe.target_rate().bps()); ++} + +- EXPECT_EQ(bwe.target_rate().bps(), kRembBps); ++TEST(SendSideBweTest, InitialRembWithProbing) { ++ TestProbing(false); + } + +-TEST(SendSideBweTest, TargetFollowProbeRateIfNoLoss) { +- ::testing::NiceMock<MockRtcEventLog> event_log; +- FieldTrials key_value_config = CreateTestFieldTrials(); +- SendSideBandwidthEstimation bwe(&key_value_config, &event_log); +- constexpr Timestamp kStartTime = Timestamp::Seconds(123); +- constexpr DataRate kInitialBwe = DataRate::KilobitsPerSec(200); +- bwe.SetBitrates(kInitialBwe, DataRate::KilobitsPerSec(100), +- DataRate::KilobitsPerSec(15000), kStartTime); +- bwe.UpdatePacketsLost(/*packets_lost=*/0, /*packets_received=*/1, kStartTime); +- +- ASSERT_EQ(bwe.target_rate(), kInitialBwe); +- +- DataRate delay_based_estimate = kInitialBwe; +- +- int sequence_number = 0; +- for (Timestamp now = kStartTime; now < kStartTime + TimeDelta::Seconds(5); +- now = now + TimeDelta::Seconds(1)) { +- TransportPacketsFeedback feedback; +- feedback.feedback_time = now; +- for (int i = 0; i < 100; ++i) { +- PacketResult packet; +- packet.sent_packet.sequence_number = ++sequence_number; +- packet.sent_packet.send_time = now; +- packet.sent_packet.size = DataSize::Bytes(1000); +- packet.receive_time = now; +- feedback.packet_feedbacks.push_back(packet); +- } +- +- bwe.OnTransportPacketsFeedback( +- feedback, delay_based_estimate, +- /*acknowledged_rate=*/delay_based_estimate / 2, +- /*is_probe_rate=*/true, +- /*in_alr=*/false); +- EXPECT_EQ(bwe.target_rate(), delay_based_estimate); +- delay_based_estimate = 2 * delay_based_estimate; +- } ++TEST(SendSideBweTest, InitialDelayBasedBweWithProbing) { ++ TestProbing(true); + } + + TEST(SendSideBweTest, DoesntReapplyBitrateDecreaseWithoutFollowingRemb) { + MockRtcEventLog event_log; ++ EXPECT_CALL(event_log, LogProxy(LossBasedBweUpdateWithBitrateOnly())) ++ .Times(1); + EXPECT_CALL(event_log, + LogProxy(LossBasedBweUpdateWithBitrateAndLossFraction())) +- .Times(2); ++ .Times(1); + FieldTrials key_value_config = CreateTestFieldTrials(); + SendSideBandwidthEstimation bwe(&key_value_config, &event_log); + static const int kMinBitrateBps = 100000; + static const int kInitialBitrateBps = 1000000; + int64_t now_ms = 1000; +- bwe.SetBitrates(DataRate::BitsPerSec(kInitialBitrateBps), +- DataRate::BitsPerSec(kMinBitrateBps), +- DataRate::BitsPerSec(1500000), Timestamp::Millis(now_ms)); ++ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps), ++ DataRate::BitsPerSec(1500000)); ++ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps), ++ Timestamp::Millis(now_ms)); + + static const uint8_t kFractionLoss = 128; + static const int64_t kRttMs = 50; +@@ -135,15 +115,13 @@ TEST(SendSideBweTest, DoesntReapplyBitrateDecreaseWithoutFollowingRemb) { + EXPECT_EQ(0, bwe.round_trip_time().ms()); + + // Signal heavy loss to go down in bitrate. +- bwe.UpdatePacketsLost(/*packets_lost=*/50, /*packets_received=*/50, ++ bwe.UpdatePacketsLost(/*packets_lost=*/50, /*number_of_packets=*/100, + Timestamp::Millis(now_ms)); + bwe.UpdateRtt(TimeDelta::Millis(kRttMs), Timestamp::Millis(now_ms)); + + // Trigger an update 2 seconds later to not be rate limited. + now_ms += 1000; +- bwe.UpdatePacketsLost(/*packets_lost=*/50, /*packets_received=*/50, +- Timestamp::Millis(now_ms)); +- bwe.OnPeriodicUpdate(Timestamp::Millis(now_ms)); ++ bwe.UpdateEstimate(Timestamp::Millis(now_ms)); + EXPECT_LT(bwe.target_rate().bps(), kInitialBitrateBps); + // Verify that the obtained bitrate isn't hitting the min bitrate, or this + // test doesn't make sense. If this ever happens, update the thresholds or +@@ -153,13 +131,13 @@ TEST(SendSideBweTest, DoesntReapplyBitrateDecreaseWithoutFollowingRemb) { + EXPECT_EQ(kRttMs, bwe.round_trip_time().ms()); + + // Triggering an update shouldn't apply further downgrade nor upgrade since +- // there's no intermediate receiver block received indicating whether this +- // is currently good or not. ++ // there's no intermediate receiver block received indicating whether this is ++ // currently good or not. + int last_bitrate_bps = bwe.target_rate().bps(); + // Trigger an update 2 seconds later to not be rate limited (but it still + // shouldn't update). + now_ms += 1000; +- bwe.OnPeriodicUpdate(Timestamp::Millis(now_ms)); ++ bwe.UpdateEstimate(Timestamp::Millis(now_ms)); + + EXPECT_EQ(last_bitrate_bps, bwe.target_rate().bps()); + // The old loss rate should still be applied though. +@@ -167,6 +145,34 @@ TEST(SendSideBweTest, DoesntReapplyBitrateDecreaseWithoutFollowingRemb) { + EXPECT_EQ(kRttMs, bwe.round_trip_time().ms()); + } + ++TEST(SendSideBweTest, SettingSendBitrateOverridesDelayBasedEstimate) { ++ ::testing::NiceMock<MockRtcEventLog> event_log; ++ FieldTrials key_value_config = CreateTestFieldTrials(); ++ SendSideBandwidthEstimation bwe(&key_value_config, &event_log); ++ static const int kMinBitrateBps = 10000; ++ static const int kMaxBitrateBps = 10000000; ++ static const int kInitialBitrateBps = 300000; ++ static const int kDelayBasedBitrateBps = 350000; ++ static const int kForcedHighBitrate = 2500000; ++ ++ int64_t now_ms = 0; ++ ++ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps), ++ DataRate::BitsPerSec(kMaxBitrateBps)); ++ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps), ++ Timestamp::Millis(now_ms)); ++ ++ bwe.UpdateDelayBasedEstimate(Timestamp::Millis(now_ms), ++ DataRate::BitsPerSec(kDelayBasedBitrateBps)); ++ bwe.UpdateEstimate(Timestamp::Millis(now_ms)); ++ EXPECT_GE(bwe.target_rate().bps(), kInitialBitrateBps); ++ EXPECT_LE(bwe.target_rate().bps(), kDelayBasedBitrateBps); ++ ++ bwe.SetSendBitrate(DataRate::BitsPerSec(kForcedHighBitrate), ++ Timestamp::Millis(now_ms)); ++ EXPECT_EQ(bwe.target_rate().bps(), kForcedHighBitrate); ++} ++ + TEST(RttBasedBackoff, DefaultEnabled) { + RttBasedBackoff rtt_backoff(CreateTestFieldTrials()); + EXPECT_TRUE(rtt_backoff.rtt_limit_.IsFinite()); +@@ -186,9 +192,10 @@ TEST(SendSideBweTest, FractionLossIsNotOverflowed) { + static const int kMinBitrateBps = 100000; + static const int kInitialBitrateBps = 1000000; + int64_t now_ms = 1000; +- bwe.SetBitrates(DataRate::BitsPerSec(kInitialBitrateBps), +- DataRate::BitsPerSec(kMinBitrateBps), +- DataRate::BitsPerSec(1500000), Timestamp::Millis(now_ms)); ++ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps), ++ DataRate::BitsPerSec(1500000)); ++ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps), ++ Timestamp::Millis(now_ms)); + + now_ms += 10000; + +@@ -209,10 +216,10 @@ TEST(SendSideBweTest, RttIsAboveLimitIfRttGreaterThanLimit) { + static const int kMaxBitrateBps = 10000000; + static const int kInitialBitrateBps = 300000; + int64_t now_ms = 0; +- bwe.SetBitrates(DataRate::BitsPerSec(kInitialBitrateBps), +- DataRate::BitsPerSec(kMinBitrateBps), +- DataRate::BitsPerSec(kMaxBitrateBps), +- Timestamp::Millis(now_ms)); ++ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps), ++ DataRate::BitsPerSec(kMaxBitrateBps)); ++ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps), ++ Timestamp::Millis(now_ms)); + bwe.UpdatePropagationRtt(/*at_time=*/Timestamp::Millis(now_ms), + /*propagation_rtt=*/TimeDelta::Millis(5000)); + EXPECT_TRUE(bwe.IsRttAboveLimit()); +@@ -226,10 +233,10 @@ TEST(SendSideBweTest, RttIsBelowLimitIfRttLessThanLimit) { + static const int kMaxBitrateBps = 10000000; + static const int kInitialBitrateBps = 300000; + int64_t now_ms = 0; +- bwe.SetBitrates(DataRate::BitsPerSec(kInitialBitrateBps), +- DataRate::BitsPerSec(kMinBitrateBps), +- DataRate::BitsPerSec(kMaxBitrateBps), +- Timestamp::Millis(now_ms)); ++ bwe.SetMinMaxBitrate(DataRate::BitsPerSec(kMinBitrateBps), ++ DataRate::BitsPerSec(kMaxBitrateBps)); ++ bwe.SetSendBitrate(DataRate::BitsPerSec(kInitialBitrateBps), ++ Timestamp::Millis(now_ms)); + bwe.UpdatePropagationRtt(/*at_time=*/Timestamp::Millis(now_ms), + /*propagation_rtt=*/TimeDelta::Millis(1000)); + EXPECT_FALSE(bwe.IsRttAboveLimit()); diff --git a/third_party/libwebrtc/moz-patch-stack/p0002.patch b/third_party/libwebrtc/moz-patch-stack/p0002.patch @@ -1,46 +1,48 @@ -From: Guido Urdaneta <guidou@webrtc.org> -Date: Thu, 24 Jul 2025 11:01:29 +0200 -Subject: (cherry-pick-branch-heads/7258) Use FieldTrialsView::IsEnabled for - DTLS 1.3 +From: Gennady Tsitovich <gtsitovich@google.com> +Date: Tue, 15 Jul 2025 08:24:50 +0000 +Subject: (cherry-pick-branch-heads/7258) [M139] Add chrome-cherry-picker + account to bot allowlist +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit -No behavior changes. +Original change's description: +> Add chrome-cherry-picker account to bot allowlist +> +> chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com is +> being by the Chrome Cherry Picker (go/chromecherrypicker) and needs to +> be able to skip the author check for presubmits. +> +> Bug: chromium:414375466 +> Change-Id: Ib9f15dd67a4efe5346e6631135e1bcd7196b992c +> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/400480 +> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> +> Reviewed-by: Björn Terelius <terelius@webrtc.org> +> Commit-Queue: Gennady Tsitovich <gtsitovich@google.com> +> Cr-Commit-Position: refs/heads/main@{#45148} -(cherry picked from commit 5ff715d5666106e01d27205c1775d1e2d07ea254) - -Bug: webrtc:383141571, chromium:433885045, chromium:434133034 -Change-Id: Ice5f3e5cbd245ddea407248a6f29c61c646e6a72 -Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/401740 -Reviewed-by: Harald Alvestrand <hta@webrtc.org> -Commit-Queue: Guido Urdaneta <guidou@webrtc.org> -Cr-Original-Commit-Position: refs/heads/main@{#45206} -Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/402200 -Cr-Commit-Position: refs/branch-heads/7258@{#3} +Bug: chromium:431157710,chromium:414375466 +Change-Id: Ib9f15dd67a4efe5346e6631135e1bcd7196b992c +Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/400700 +Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> +Auto-Submit: Chrome Cherry Picker <chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com> +Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> +Cr-Commit-Position: refs/branch-heads/7258@{#2} Cr-Branched-From: 74fa937f86ed8432c07676f7a1ce0e5e2812b3d5-refs/heads/main@{#44974} --- - rtc_base/openssl_stream_adapter.cc | 10 ++++++---- - 1 file changed, 6 insertions(+), 4 deletions(-) + PRESUBMIT.py | 2 ++ + 1 file changed, 2 insertions(+) -diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc -index 7d7466b1cc..604a9465c7 100644 ---- a/rtc_base/openssl_stream_adapter.cc -+++ b/rtc_base/openssl_stream_adapter.cc -@@ -144,13 +144,15 @@ int GetForceDtls13(const FieldTrialsView* field_trials) { - return kForceDtls13Off; - } - #ifdef DTLS1_3_VERSION -- auto mode = field_trials->Lookup("WebRTC-ForceDtls13"); -- RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13: " << mode; -- if (mode == "Enabled") { -+ if (field_trials->IsEnabled("WebRTC-ForceDtls13")) { -+ RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Enabled"; - return kForceDtls13Enabled; -- } else if (mode == "Only") { -+ } -+ if (field_trials->Lookup("WebRTC-ForceDtls13") == "Only") { -+ RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Only"; - return kForceDtls13Only; - } -+ RTC_LOG(LS_WARNING) << "WebRTC-ForceDtls13 Disabled"; - #endif - return kForceDtls13Off; - } +diff --git a/PRESUBMIT.py b/PRESUBMIT.py +index 96fa8abd9d..debc65fb24 100755 +--- a/PRESUBMIT.py ++++ b/PRESUBMIT.py +@@ -991,6 +991,8 @@ def CommonChecks(input_api, output_api): + bot_allowlist=[ + 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com', + 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com', ++ ('chrome-cherry-picker' ++ '@chops-service-accounts.iam.gserviceaccount.com'), + ])) + results.extend( + input_api.canned_checks.CheckChangeTodoHasOwner( diff --git a/third_party/libwebrtc/moz-patch-stack/p0002.patch b/third_party/libwebrtc/moz-patch-stack/p0003.patch