From 1f2f5dc95131db779455813108ee2f84449f10c5 Mon Sep 17 00:00:00 2001 From: Diep Bui Date: Thu, 26 Oct 2023 07:58:26 +0000 Subject: [PATCH] Compute loss rate based on byte count rather than packet count in loss based BWE. 2 main reasons: 1. Packet sizes are much different thus a lost audio packet should not be treated similar to a lost video packet. In low bandwidth/traffic policing scenario, the number of send packet is few, thus the computed loss can be imprecise. 2. Given a candidate bandwidth estimate, the objective function (how good the candidate is) is computed by recomputing loss rate = send rate/estimate bandwith + inherent loss. It means the objective function is byte based rather than packet based. Potential risk: the current algorithm params are tuned based on packet count, thus it might not work with byte count, which is much higher than packet count. The change is under field trial and disabled by default. Bug: webrtc:12707 Change-Id: I8b832e7920d2b4cadcd4a072b3a4d4f26a213a20 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325065 Reviewed-by: Per Kjellander Commit-Queue: Diep Bui Cr-Commit-Position: refs/heads/main@{#41013} --- .../goog_cc/loss_based_bwe_v2.cc | 96 +++++++++++++++---- .../goog_cc/loss_based_bwe_v2.h | 6 ++ .../goog_cc/loss_based_bwe_v2_test.cc | 90 ++++++++++------- 3 files changed, 141 insertions(+), 51 deletions(-) diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc index f79164ebf0..2ebbe3338e 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc @@ -49,10 +49,13 @@ bool IsValid(Timestamp timestamp) { return timestamp.IsFinite(); } +double ToKiloBytes(DataSize datasize) { return datasize.bytes() / 1000.0; } + struct PacketResultsSummary { int num_packets = 0; int num_lost_packets = 0; DataSize total_size = DataSize::Zero(); + DataSize lost_size = DataSize::Zero(); Timestamp first_send_time = Timestamp::PlusInfinity(); Timestamp last_send_time = Timestamp::MinusInfinity(); }; @@ -67,6 +70,7 @@ PacketResultsSummary GetPacketResultsSummary( for (const PacketResult& packet : packet_results) { if (!packet.IsReceived()) { packet_results_summary.num_lost_packets++; + packet_results_summary.lost_size += packet.sent_packet.size; } packet_results_summary.total_size += packet.sent_packet.size; packet_results_summary.first_send_time = std::min( @@ -444,6 +448,8 @@ absl::optional LossBasedBweV2::CreateConfig( FieldTrialParameter use_padding_for_increase("UsePadding", false); FieldTrialParameter hold_duration_factor("HoldDurationFactor", 0.0); + FieldTrialParameter use_byte_loss_rate("UseByteLossRate", false); + if (key_value_config) { ParseFieldTrial({&enabled, &bandwidth_rampup_upper_bound_factor, @@ -482,7 +488,8 @@ absl::optional LossBasedBweV2::CreateConfig( &min_num_observations, &lower_bound_by_acked_rate_factor, &use_padding_for_increase, - &hold_duration_factor}, + &hold_duration_factor, + &use_byte_loss_rate}, key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2")); } @@ -546,6 +553,7 @@ absl::optional LossBasedBweV2::CreateConfig( lower_bound_by_acked_rate_factor.Get(); config->use_padding_for_increase = use_padding_for_increase.Get(); config->hold_duration_factor = hold_duration_factor.Get(); + config->use_byte_loss_rate = use_byte_loss_rate.Get(); return config; } @@ -742,6 +750,11 @@ bool LossBasedBweV2::IsConfigValid() const { } double LossBasedBweV2::GetAverageReportedLossRatio() const { + return config_->use_byte_loss_rate ? GetAverageReportedByteLossRatio() + : GetAverageReportedPacketLossRatio(); +} + +double LossBasedBweV2::GetAverageReportedPacketLossRatio() const { if (num_observations_ <= 0) { return 0.0; } @@ -763,6 +776,27 @@ double LossBasedBweV2::GetAverageReportedLossRatio() const { return num_lost_packets / num_packets; } +double LossBasedBweV2::GetAverageReportedByteLossRatio() const { + if (num_observations_ <= 0) { + return 0.0; + } + + DataSize total_bytes = DataSize::Zero(); + DataSize lost_bytes = DataSize::Zero(); + for (const Observation& observation : observations_) { + if (!observation.IsInitialized()) { + continue; + } + + double instant_temporal_weight = + instant_upper_bound_temporal_weights_[(num_observations_ - 1) - + observation.id]; + total_bytes += instant_temporal_weight * observation.size; + lost_bytes += instant_temporal_weight * observation.lost_size; + } + return lost_bytes / total_bytes; +} + DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const { DataRate candidate_bandwidth_upper_bound = max_bitrate_; if (IsInLossLimitedState() && IsValid(bandwidth_limit_in_current_window_)) { @@ -846,16 +880,29 @@ LossBasedBweV2::Derivatives LossBasedBweV2::GetDerivatives( double temporal_weight = temporal_weights_[(num_observations_ - 1) - observation.id]; - - derivatives.first += - temporal_weight * - ((observation.num_lost_packets / loss_probability) - - (observation.num_received_packets / (1.0 - loss_probability))); - derivatives.second -= - temporal_weight * - ((observation.num_lost_packets / std::pow(loss_probability, 2)) + - (observation.num_received_packets / - std::pow(1.0 - loss_probability, 2))); + if (config_->use_byte_loss_rate) { + derivatives.first += + temporal_weight * + ((ToKiloBytes(observation.lost_size) / loss_probability) - + (ToKiloBytes(observation.size - observation.lost_size) / + (1.0 - loss_probability))); + derivatives.second -= + temporal_weight * + ((ToKiloBytes(observation.lost_size) / + std::pow(loss_probability, 2)) + + (ToKiloBytes(observation.size - observation.lost_size) / + std::pow(1.0 - loss_probability, 2))); + } else { + derivatives.first += + temporal_weight * + ((observation.num_lost_packets / loss_probability) - + (observation.num_received_packets / (1.0 - loss_probability))); + derivatives.second -= + temporal_weight * + ((observation.num_lost_packets / std::pow(loss_probability, 2)) + + (observation.num_received_packets / + std::pow(1.0 - loss_probability, 2))); + } } if (derivatives.second >= 0.0) { @@ -927,13 +974,23 @@ double LossBasedBweV2::GetObjective( double temporal_weight = temporal_weights_[(num_observations_ - 1) - observation.id]; - - objective += - temporal_weight * - ((observation.num_lost_packets * std::log(loss_probability)) + - (observation.num_received_packets * std::log(1.0 - loss_probability))); - objective += - temporal_weight * high_bandwidth_bias * observation.num_packets; + if (config_->use_byte_loss_rate) { + objective += + temporal_weight * + ((ToKiloBytes(observation.lost_size) * std::log(loss_probability)) + + (ToKiloBytes(observation.size - observation.lost_size) * + std::log(1.0 - loss_probability))); + objective += + temporal_weight * high_bandwidth_bias * ToKiloBytes(observation.size); + } else { + objective += + temporal_weight * + ((observation.num_lost_packets * std::log(loss_probability)) + + (observation.num_received_packets * + std::log(1.0 - loss_probability))); + objective += + temporal_weight * high_bandwidth_bias * observation.num_packets; + } } return objective; @@ -1036,6 +1093,7 @@ bool LossBasedBweV2::PushBackObservation( partial_observation_.num_lost_packets += packet_results_summary.num_lost_packets; partial_observation_.size += packet_results_summary.total_size; + partial_observation_.lost_size += packet_results_summary.lost_size; // This is the first packet report we have received. if (!IsValid(last_send_time_most_recent_observation_)) { @@ -1061,6 +1119,8 @@ bool LossBasedBweV2::PushBackObservation( observation.num_packets - observation.num_lost_packets; observation.sending_rate = GetSendingRate(partial_observation_.size / observation_duration); + observation.lost_size = partial_observation_.lost_size; + observation.size = partial_observation_.size; observation.id = num_observations_++; observations_[observation.id % config_->observation_window_size] = observation; diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h index 0468e16525..b39657c7bb 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h @@ -120,6 +120,7 @@ class LossBasedBweV2 { double lower_bound_by_acked_rate_factor = 0.0; bool use_padding_for_increase = false; double hold_duration_factor = 0.0; + bool use_byte_loss_rate = false; }; struct Derivatives { @@ -134,6 +135,8 @@ class LossBasedBweV2 { int num_lost_packets = 0; int num_received_packets = 0; DataRate sending_rate = DataRate::MinusInfinity(); + DataSize size = DataSize::Zero(); + DataSize lost_size = DataSize::Zero(); int id = -1; }; @@ -141,6 +144,7 @@ class LossBasedBweV2 { int num_packets = 0; int num_lost_packets = 0; DataSize size = DataSize::Zero(); + DataSize lost_size = DataSize::Zero(); }; static absl::optional CreateConfig( @@ -149,6 +153,8 @@ class LossBasedBweV2 { // Returns `0.0` if not enough loss statistics have been received. double GetAverageReportedLossRatio() const; + double GetAverageReportedPacketLossRatio() const; + double GetAverageReportedByteLossRatio() const; std::vector GetCandidates(bool in_alr) const; DataRate GetCandidateBandwidthUpperBound() const; Derivatives GetDerivatives(const ChannelParameters& channel_parameters) const; diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc index d9480d00d5..fc1f410bd6 100644 --- a/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc @@ -31,6 +31,7 @@ using ::webrtc::test::ExplicitKeyValueConfig; constexpr TimeDelta kObservationDurationLowerBound = TimeDelta::Millis(250); constexpr TimeDelta kDelayedIncreaseWindow = TimeDelta::Millis(300); constexpr double kMaxIncreaseFactor = 1.5; +constexpr int kPacketSize = 15'000; class LossBasedBweV2Test : public ::testing::TestWithParam { protected: @@ -90,8 +91,8 @@ class LossBasedBweV2Test : public ::testing::TestWithParam { std::vector CreatePacketResultsWithReceivedPackets( Timestamp first_packet_timestamp) { std::vector enough_feedback(2); - enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[0].sent_packet.size = DataSize::Bytes(kPacketSize); + enough_feedback[1].sent_packet.size = DataSize::Bytes(kPacketSize); enough_feedback[0].sent_packet.send_time = first_packet_timestamp; enough_feedback[1].sent_packet.send_time = first_packet_timestamp + kObservationDurationLowerBound; @@ -102,12 +103,13 @@ class LossBasedBweV2Test : public ::testing::TestWithParam { return enough_feedback; } - std::vector CreatePacketResultsWith10pLossRate( - Timestamp first_packet_timestamp) { + std::vector CreatePacketResultsWith10pPacketLossRate( + Timestamp first_packet_timestamp, + DataSize lost_packet_size = DataSize::Bytes(kPacketSize)) { std::vector enough_feedback(10); - enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[0].sent_packet.size = DataSize::Bytes(kPacketSize); for (unsigned i = 0; i < enough_feedback.size(); ++i) { - enough_feedback[i].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[i].sent_packet.size = DataSize::Bytes(kPacketSize); enough_feedback[i].sent_packet.send_time = first_packet_timestamp + static_cast(i) * kObservationDurationLowerBound; @@ -116,14 +118,15 @@ class LossBasedBweV2Test : public ::testing::TestWithParam { static_cast(i + 1) * kObservationDurationLowerBound; } enough_feedback[9].receive_time = Timestamp::PlusInfinity(); + enough_feedback[9].sent_packet.size = lost_packet_size; return enough_feedback; } - std::vector CreatePacketResultsWith50pLossRate( + std::vector CreatePacketResultsWith50pPacketLossRate( Timestamp first_packet_timestamp) { std::vector enough_feedback(2); - enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[0].sent_packet.size = DataSize::Bytes(kPacketSize); + enough_feedback[1].sent_packet.size = DataSize::Bytes(kPacketSize); enough_feedback[0].sent_packet.send_time = first_packet_timestamp; enough_feedback[1].sent_packet.send_time = first_packet_timestamp + kObservationDurationLowerBound; @@ -136,8 +139,8 @@ class LossBasedBweV2Test : public ::testing::TestWithParam { std::vector CreatePacketResultsWith100pLossRate( Timestamp first_packet_timestamp) { std::vector enough_feedback(2); - enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); - enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback[0].sent_packet.size = DataSize::Bytes(kPacketSize); + enough_feedback[1].sent_packet.size = DataSize::Bytes(kPacketSize); enough_feedback[0].sent_packet.send_time = first_packet_timestamp; enough_feedback[1].sent_packet.send_time = first_packet_timestamp + kObservationDurationLowerBound; @@ -461,7 +464,7 @@ TEST_F(LossBasedBweV2Test, UseAckedBitrateForEmegencyBackOff) { // Create two packet results, first packet has 50% loss rate, second packet // has 100% loss rate. std::vector enough_feedback_1 = - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); std::vector enough_feedback_2 = CreatePacketResultsWith100pLossRate( @@ -814,7 +817,7 @@ TEST_F(LossBasedBweV2Test, CreatePacketResultsWithReceivedPackets( /*first_packet_timestamp=*/Timestamp::Zero()); std::vector enough_feedback_2 = - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kDelayedIncreaseWindow - TimeDelta::Millis(2)); std::vector enough_feedback_3 = @@ -913,7 +916,7 @@ TEST_F(LossBasedBweV2Test, NotIncreaseIfInherentLossLessThanAverageLoss) { DataRate::KilobitsPerSec(600)); std::vector enough_feedback_10p_loss_1 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_10p_loss_1, @@ -921,7 +924,7 @@ TEST_F(LossBasedBweV2Test, NotIncreaseIfInherentLossLessThanAverageLoss) { /*in_alr=*/false); std::vector enough_feedback_10p_loss_2 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( @@ -947,7 +950,7 @@ TEST_F(LossBasedBweV2Test, DataRate::KilobitsPerSec(600)); std::vector enough_feedback_10p_loss_1 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_10p_loss_1, delay_based_estimate, @@ -955,7 +958,7 @@ TEST_F(LossBasedBweV2Test, /*in_alr=*/false); std::vector enough_feedback_10p_loss_2 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( @@ -981,7 +984,7 @@ TEST_F(LossBasedBweV2Test, DataRate::KilobitsPerSec(600)); std::vector enough_feedback_10p_loss_1 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_10p_loss_1, delay_based_estimate, @@ -989,7 +992,7 @@ TEST_F(LossBasedBweV2Test, /*in_alr=*/false); std::vector enough_feedback_10p_loss_2 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( @@ -1017,7 +1020,7 @@ TEST_F(LossBasedBweV2Test, DataRate::KilobitsPerSec(600)); std::vector enough_feedback_10p_loss_1 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_10p_loss_1, delay_based_estimate, @@ -1025,7 +1028,7 @@ TEST_F(LossBasedBweV2Test, /*in_alr=*/false); std::vector enough_feedback_10p_loss_2 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( @@ -1053,7 +1056,7 @@ TEST_F(LossBasedBweV2Test, DataRate::KilobitsPerSec(600)); std::vector enough_feedback_50p_loss_1 = - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_50p_loss_1, delay_based_estimate, @@ -1061,7 +1064,7 @@ TEST_F(LossBasedBweV2Test, /*in_alr=*/false); std::vector enough_feedback_50p_loss_2 = - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( @@ -1327,7 +1330,7 @@ TEST_F(LossBasedBweV2Test, HasDecreaseStateBecauseOfUpperBound) { DataRate::KilobitsPerSec(500)); std::vector enough_feedback_10p_loss_1 = - CreatePacketResultsWith10pLossRate( + CreatePacketResultsWith10pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_10p_loss_1, @@ -1357,7 +1360,7 @@ TEST_F(LossBasedBweV2Test, HasIncreaseStateBecauseOfLowerBound) { // Network has a high loss to create a loss scenario. std::vector enough_feedback_50p_loss_1 = - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( enough_feedback_50p_loss_1, @@ -1371,7 +1374,7 @@ TEST_F(LossBasedBweV2Test, HasIncreaseStateBecauseOfLowerBound) { loss_based_bandwidth_estimator.SetAcknowledgedBitrate( DataRate::KilobitsPerSec(200)); std::vector enough_feedback_50p_loss_2 = - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( @@ -1398,7 +1401,7 @@ TEST_F(LossBasedBweV2Test, loss_based_bandwidth_estimator.SetAcknowledgedBitrate( DataRate::KilobitsPerSec(150)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()), /*delay_based_estimate=*/DataRate::PlusInfinity(), /*in_alr=*/true); @@ -1439,7 +1442,7 @@ TEST_F(LossBasedBweV2Test, HasDelayBasedStateIfLossBasedBweIsMax) { DataRate::KilobitsPerSec(1000)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - /*feedback=*/CreatePacketResultsWith50pLossRate( + /*feedback=*/CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound), /*delay_based_estimate=*/DataRate::KilobitsPerSec(2000), @@ -1474,7 +1477,7 @@ TEST_F(LossBasedBweV2Test, IncreaseUsingPaddingStateIfFieldTrial) { loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(2500)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()), /*delay_based_estimate=*/DataRate::PlusInfinity(), /*in_alr=*/false); @@ -1498,7 +1501,7 @@ TEST_F(LossBasedBweV2Test, IncreaseEstimateIfNotHold) { loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(2500)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()), /*delay_based_estimate=*/DataRate::PlusInfinity(), /*in_alr=*/false); @@ -1527,7 +1530,7 @@ TEST_F(LossBasedBweV2Test, IncreaseEstimateAfterHoldDuration) { loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(2500)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()), /*delay_based_estimate=*/DataRate::PlusInfinity(), /*in_alr=*/false); @@ -1564,7 +1567,7 @@ TEST_F(LossBasedBweV2Test, IncreaseEstimateAfterHoldDuration) { // Get another 50p packet loss. loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero() + kObservationDurationLowerBound * 3), /*delay_based_estimate=*/DataRate::PlusInfinity(), @@ -1610,7 +1613,7 @@ TEST_F(LossBasedBweV2Test, EndHoldDurationIfDelayBasedEstimateWorks) { loss_based_bandwidth_estimator.SetBandwidthEstimate( DataRate::KilobitsPerSec(2500)); loss_based_bandwidth_estimator.UpdateBandwidthEstimate( - CreatePacketResultsWith50pLossRate( + CreatePacketResultsWith50pPacketLossRate( /*first_packet_timestamp=*/Timestamp::Zero()), /*delay_based_estimate=*/DataRate::PlusInfinity(), /*in_alr=*/false); @@ -1632,5 +1635,26 @@ TEST_F(LossBasedBweV2Test, EndHoldDurationIfDelayBasedEstimateWorks) { estimate + DataRate::KilobitsPerSec(10)); } +TEST_F(LossBasedBweV2Test, UseByteLossRate) { + ExplicitKeyValueConfig key_value_config( + ShortObservationConfig("UseByteLossRate:true")); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(500)); + // Create packet feedback having 10% packet loss but more than 50% byte loss. + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + CreatePacketResultsWith10pPacketLossRate( + /*first_packet_timestamp=*/Timestamp::Zero(), + /*lost_packet_size=*/DataSize::Bytes(kPacketSize * 20)), + /*delay_based_estimate=*/DataRate::PlusInfinity(), + /*in_alr=*/false); + EXPECT_EQ(loss_based_bandwidth_estimator.GetLossBasedResult().state, + LossBasedState::kDecreasing); + // The estimate is bounded by the instant upper bound due to high loss. + EXPECT_LT( + loss_based_bandwidth_estimator.GetLossBasedResult().bandwidth_estimate, + DataRate::KilobitsPerSec(150)); +} + } // namespace } // namespace webrtc