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:
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