diff --git a/modules/congestion_controller/goog_cc/BUILD.gn b/modules/congestion_controller/goog_cc/BUILD.gn index ea20da87a3..9aafedbcb4 100644 --- a/modules/congestion_controller/goog_cc/BUILD.gn +++ b/modules/congestion_controller/goog_cc/BUILD.gn @@ -27,9 +27,9 @@ rtc_library("goog_cc") { ":alr_detector", ":delay_based_bwe", ":estimators", - ":loss_based_controller", ":probe_controller", ":pushback_controller", + ":send_side_bwe", "../..:module_api", "../../../api:network_state_predictor_api", "../../../api/rtc_event_log", @@ -146,15 +146,55 @@ rtc_library("estimators") { ] } -rtc_library("loss_based_controller") { +rtc_library("loss_based_bwe_v2") { + sources = [ + "loss_based_bwe_v2.cc", + "loss_based_bwe_v2.h", + ] + deps = [ + "../../../api:array_view", + "../../../api/transport:network_control", + "../../../api/transport:webrtc_key_value_config", + "../../../api/units:data_rate", + "../../../api/units:data_size", + "../../../api/units:time_delta", + "../../../api/units:timestamp", + "../../../rtc_base:logging", + "../../../rtc_base/experiments:field_trial_parser", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/algorithm:container", + "//third_party/abseil-cpp/absl/types:optional", + ] +} + +rtc_library("loss_based_bwe_v1") { configs += [ ":bwe_test_logging" ] sources = [ "loss_based_bandwidth_estimation.cc", "loss_based_bandwidth_estimation.h", + ] + deps = [ + "../../../api/transport:network_control", + "../../../api/transport:webrtc_key_value_config", + "../../../api/units:data_rate", + "../../../api/units:time_delta", + "../../../api/units:timestamp", + "../../../rtc_base:checks", + "../../../rtc_base/experiments:field_trial_parser", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/strings" ] +} + +rtc_library("send_side_bwe") { + configs += [ ":bwe_test_logging" ] + sources = [ "send_side_bandwidth_estimation.cc", "send_side_bandwidth_estimation.h", ] deps = [ + ":loss_based_bwe_v1", + ":loss_based_bwe_v2", "../../../api/rtc_event_log", "../../../api/transport:network_control", "../../../api/transport:webrtc_key_value_config", @@ -269,6 +309,7 @@ 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_v2_test.cc", "probe_bitrate_estimator_unittest.cc", "probe_controller_unittest.cc", "robust_throughput_estimator_unittest.cc", @@ -280,9 +321,10 @@ if (rtc_include_tests) { ":delay_based_bwe", ":estimators", ":goog_cc", - ":loss_based_controller", + ":loss_based_bwe_v2", ":probe_controller", ":pushback_controller", + ":send_side_bwe", "../../../api/rtc_event_log", "../../../api/test/network_emulation", "../../../api/test/network_emulation:create_cross_traffic", @@ -291,12 +333,15 @@ if (rtc_include_tests) { "../../../api/transport:network_control", "../../../api/transport:webrtc_key_value_config", "../../../api/units:data_rate", + "../../../api/units:data_size", + "../../../api/units:time_delta", "../../../api/units:timestamp", "../../../logging:mocks", "../../../logging:rtc_event_bwe", "../../../rtc_base:checks", "../../../rtc_base:rtc_base_approved", "../../../rtc_base:rtc_base_tests_utils", + "../../../rtc_base:stringutils", "../../../rtc_base/experiments:alr_experiment", "../../../system_wrappers", "../../../test:explicit_key_value_config", diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc new file mode 100644 index 0000000000..ae52b2d852 --- /dev/null +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.cc @@ -0,0 +1,652 @@ +/* + * Copyright 2021 The WebRTC project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/algorithm/container.h" +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/transport/network_types.h" +#include "api/transport/webrtc_key_value_config.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" +#include "rtc_base/experiments/field_trial_list.h" +#include "rtc_base/experiments/field_trial_parser.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +namespace { + +bool IsValid(DataRate datarate) { + return datarate.IsFinite(); +} + +bool IsValid(Timestamp timestamp) { + return timestamp.IsFinite(); +} + +struct PacketResultsSummary { + int num_packets = 0; + int num_lost_packets = 0; + DataSize total_size = DataSize::Zero(); + Timestamp first_send_time = Timestamp::PlusInfinity(); + Timestamp last_send_time = Timestamp::MinusInfinity(); +}; + +// Returns a `PacketResultsSummary` where `first_send_time` is `PlusInfinity, +// and `last_send_time` is `MinusInfinity`, if `packet_results` is empty. +PacketResultsSummary GetPacketResultsSummary( + rtc::ArrayView packet_results) { + PacketResultsSummary packet_results_summary; + + packet_results_summary.num_packets = packet_results.size(); + for (const PacketResult& packet : packet_results) { + if (!packet.IsReceived()) { + packet_results_summary.num_lost_packets++; + } + packet_results_summary.total_size += packet.sent_packet.size; + packet_results_summary.first_send_time = std::min( + packet_results_summary.first_send_time, packet.sent_packet.send_time); + packet_results_summary.last_send_time = std::max( + packet_results_summary.last_send_time, packet.sent_packet.send_time); + } + + return packet_results_summary; +} + +double GetLossProbability(double inherent_loss, + DataRate loss_limited_bandwidth, + DataRate sending_rate) { + if (inherent_loss < 0.0 || inherent_loss > 1.0) { + RTC_LOG(LS_WARNING) << "The inherent loss must be in [0,1]: " + << inherent_loss; + inherent_loss = std::min(std::max(inherent_loss, 0.0), 1.0); + } + if (!sending_rate.IsFinite()) { + RTC_LOG(LS_WARNING) << "The sending rate must be finite: " + << ToString(sending_rate); + } + if (!loss_limited_bandwidth.IsFinite()) { + RTC_LOG(LS_WARNING) << "The loss limited bandwidth must be finite: " + << ToString(loss_limited_bandwidth); + } + + // We approximate the loss model + // loss_probability = inherent_loss + (1 - inherent_loss) * + // max(0, sending_rate - bandwidth) / sending_rate + // by + // loss_probability = inherent_loss + + // max(0, sending_rate - bandwidth) / sending_rate + // as it allows for simpler calculations and makes little difference in + // practice. + double loss_probability = inherent_loss; + if (IsValid(sending_rate) && IsValid(loss_limited_bandwidth) && + (sending_rate > loss_limited_bandwidth)) { + loss_probability += (sending_rate - loss_limited_bandwidth) / sending_rate; + } + return std::min(std::max(loss_probability, 1.0e-6), 1.0 - 1.0e-6); +} + +} // namespace + +LossBasedBweV2::LossBasedBweV2(const WebRtcKeyValueConfig* key_value_config) + : config_(CreateConfig(key_value_config)) { + if (!config_.has_value()) { + RTC_LOG(LS_VERBOSE) << "The configuration does not specify that the " + "estimator should be enabled, disabling it."; + return; + } + if (!IsConfigValid()) { + RTC_LOG(LS_WARNING) + << "The configuration is not valid, disabling the estimator."; + config_.reset(); + return; + } + + current_estimate_.inherent_loss = config_->initial_inherent_loss_estimate; + observations_.resize(config_->observation_window_size); + temporal_weights_.resize(config_->observation_window_size); + tcp_fairness_temporal_weights_.resize(config_->observation_window_size); + CalculateTemporalWeights(); +} + +bool LossBasedBweV2::IsEnabled() const { + return config_.has_value(); +} + +bool LossBasedBweV2::IsReady() const { + return IsEnabled() && IsValid(current_estimate_.loss_limited_bandwidth) && + num_observations_ > 0; +} + +DataRate LossBasedBweV2::GetBandwidthEstimate() const { + if (!IsReady()) { + if (!IsEnabled()) { + RTC_LOG(LS_WARNING) + << "The estimator must be enabled before it can be used."; + } else { + if (!IsValid(current_estimate_.loss_limited_bandwidth)) { + RTC_LOG(LS_WARNING) + << "The estimator must be initialized before it can be used."; + } + if (num_observations_ <= 0) { + RTC_LOG(LS_WARNING) << "The estimator must receive enough loss " + "statistics before it can be used."; + } + } + return DataRate::PlusInfinity(); + } + + return std::min(current_estimate_.loss_limited_bandwidth, + GetTcpFairnessBandwidthUpperBound()); +} + +void LossBasedBweV2::SetAcknowledgedBitrate(DataRate acknowledged_bitrate) { + if (IsValid(acknowledged_bitrate)) { + acknowledged_bitrate_ = acknowledged_bitrate; + } else { + RTC_LOG(LS_WARNING) << "The acknowledged bitrate must be finite: " + << ToString(acknowledged_bitrate); + } +} + +void LossBasedBweV2::SetBandwidthEstimate(DataRate bandwidth_estimate) { + if (IsValid(bandwidth_estimate)) { + current_estimate_.loss_limited_bandwidth = bandwidth_estimate; + } else { + RTC_LOG(LS_WARNING) << "The bandwidth estimate must be finite: " + << ToString(bandwidth_estimate); + } +} + +void LossBasedBweV2::UpdateBandwidthEstimate( + rtc::ArrayView packet_results) { + if (!IsEnabled()) { + RTC_LOG(LS_WARNING) + << "The estimator must be enabled before it can be used."; + return; + } + if (packet_results.empty()) { + RTC_LOG(LS_VERBOSE) + << "The estimate cannot be updated without any loss statistics."; + return; + } + + if (!PushBackObservation(packet_results)) { + return; + } + + if (!IsValid(current_estimate_.loss_limited_bandwidth)) { + RTC_LOG(LS_VERBOSE) + << "The estimator must be initialized before it can be used."; + return; + } + + ChannelParameters best_candidate = current_estimate_; + double objective_max = std::numeric_limits::lowest(); + for (ChannelParameters candidate : GetCandidates()) { + NewtonsMethodUpdate(candidate); + + const double candidate_objective = GetObjective(candidate); + if (candidate_objective > objective_max) { + objective_max = candidate_objective; + best_candidate = candidate; + } + } + + current_estimate_ = best_candidate; +} + +// Returns a `LossBasedBweV2::Config` iff the `key_value_config` specifies a +// configuration for the `LossBasedBweV2` which is explicitly enabled. +absl::optional LossBasedBweV2::CreateConfig( + const WebRtcKeyValueConfig* key_value_config) { + FieldTrialParameter enabled("Enabled", false); + FieldTrialParameter bandwidth_rampup_upper_bound_factor( + "BwRampupUpperBoundFactor", 1.1); + FieldTrialList candidate_factors("CandidateFactors", + {1.05, 1.0, 0.95}); + FieldTrialParameter higher_bandwidth_bias_factor("HigherBwBiasFactor", + 0.00001); + FieldTrialParameter inherent_loss_lower_bound( + "InherentLossLowerBound", 1.0e-3); + FieldTrialParameter inherent_loss_upper_bound_bandwidth_balance( + "InherentLossUpperBoundBwBalance", DataRate::KilobitsPerSec(15.0)); + FieldTrialParameter inherent_loss_upper_bound_offset( + "InherentLossUpperBoundOffset", 0.05); + FieldTrialParameter initial_inherent_loss_estimate( + "InitialInherentLossEstimate", 0.01); + FieldTrialParameter newton_iterations("NewtonIterations", 1); + FieldTrialParameter newton_step_size("NewtonStepSize", 0.5); + FieldTrialParameter observation_duration_lower_bound( + "ObservationDurationLowerBound", TimeDelta::Seconds(1)); + FieldTrialParameter observation_window_size("ObservationWindowSize", 20); + FieldTrialParameter sending_rate_smoothing_factor( + "SendingRateSmoothingFactor", 0.0); + FieldTrialParameter tcp_fairness_temporal_weight_factor( + "TcpFairnessTemporalWeightFactor", 0.99); + FieldTrialParameter tcp_fairness_upper_bound_bandwidth_balance( + "TcpFairnessUpperBoundBwBalance", DataRate::KilobitsPerSec(15.0)); + FieldTrialParameter tcp_fairness_upper_bound_loss_offset( + "TcpFairnessUpperBoundLossOffset", 0.05); + FieldTrialParameter temporal_weight_factor("TemporalWeightFactor", + 0.99); + + if (key_value_config) { + ParseFieldTrial( + {&enabled, &bandwidth_rampup_upper_bound_factor, &candidate_factors, + &higher_bandwidth_bias_factor, &inherent_loss_lower_bound, + &inherent_loss_upper_bound_bandwidth_balance, + &inherent_loss_upper_bound_offset, &initial_inherent_loss_estimate, + &newton_iterations, &newton_step_size, + &observation_duration_lower_bound, &observation_window_size, + &sending_rate_smoothing_factor, &tcp_fairness_temporal_weight_factor, + &tcp_fairness_upper_bound_bandwidth_balance, + &tcp_fairness_upper_bound_loss_offset, &temporal_weight_factor}, + key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2")); + } + + absl::optional config; + if (!enabled.Get()) { + return config; + } + config.emplace(); + config->bandwidth_rampup_upper_bound_factor = + bandwidth_rampup_upper_bound_factor.Get(); + config->candidate_factors = candidate_factors.Get(); + config->higher_bandwidth_bias_factor = higher_bandwidth_bias_factor.Get(); + config->inherent_loss_lower_bound = inherent_loss_lower_bound.Get(); + config->inherent_loss_upper_bound_bandwidth_balance = + inherent_loss_upper_bound_bandwidth_balance.Get(); + config->inherent_loss_upper_bound_offset = + inherent_loss_upper_bound_offset.Get(); + config->initial_inherent_loss_estimate = initial_inherent_loss_estimate.Get(); + config->newton_iterations = newton_iterations.Get(); + config->newton_step_size = newton_step_size.Get(); + config->observation_duration_lower_bound = + observation_duration_lower_bound.Get(); + config->observation_window_size = observation_window_size.Get(); + config->sending_rate_smoothing_factor = sending_rate_smoothing_factor.Get(); + config->tcp_fairness_temporal_weight_factor = + tcp_fairness_temporal_weight_factor.Get(); + config->tcp_fairness_upper_bound_bandwidth_balance = + tcp_fairness_upper_bound_bandwidth_balance.Get(); + config->tcp_fairness_upper_bound_loss_offset = + tcp_fairness_upper_bound_loss_offset.Get(); + config->temporal_weight_factor = temporal_weight_factor.Get(); + return config; +} + +bool LossBasedBweV2::IsConfigValid() const { + if (!config_.has_value()) { + return false; + } + + bool valid = true; + + if (config_->bandwidth_rampup_upper_bound_factor <= 1.0) { + RTC_LOG(LS_WARNING) + << "The bandwidth rampup upper bound factor must be greater than 1: " + << config_->bandwidth_rampup_upper_bound_factor; + valid = false; + } + if (config_->higher_bandwidth_bias_factor < 0.0) { + RTC_LOG(LS_WARNING) + << "The higher bandwidth bias factor must be non-negative: " + << config_->higher_bandwidth_bias_factor; + valid = false; + } + if (config_->inherent_loss_lower_bound < 0.0 || + config_->inherent_loss_lower_bound >= 1.0) { + RTC_LOG(LS_WARNING) << "The inherent loss lower bound must be in [0, 1): " + << config_->inherent_loss_lower_bound; + valid = false; + } + if (config_->inherent_loss_upper_bound_bandwidth_balance <= + DataRate::Zero()) { + RTC_LOG(LS_WARNING) + << "The inherent loss upper bound bandwidth balance " + "must be positive: " + << ToString(config_->inherent_loss_upper_bound_bandwidth_balance); + valid = false; + } + if (config_->inherent_loss_upper_bound_offset < + config_->inherent_loss_lower_bound || + config_->inherent_loss_upper_bound_offset >= 1.0) { + RTC_LOG(LS_WARNING) << "The inherent loss upper bound must be greater " + "than or equal to the inherent " + "loss lower bound, which is " + << config_->inherent_loss_lower_bound + << ", and less than 1: " + << config_->inherent_loss_upper_bound_offset; + valid = false; + } + if (config_->initial_inherent_loss_estimate < 0.0 || + config_->initial_inherent_loss_estimate >= 1.0) { + RTC_LOG(LS_WARNING) + << "The initial inherent loss estimate must be in [0, 1): " + << config_->initial_inherent_loss_estimate; + valid = false; + } + if (config_->newton_iterations <= 0) { + RTC_LOG(LS_WARNING) << "The number of Newton iterations must be positive: " + << config_->newton_iterations; + valid = false; + } + if (config_->newton_step_size <= 0.0) { + RTC_LOG(LS_WARNING) << "The Newton step size must be positive: " + << config_->newton_step_size; + valid = false; + } + if (config_->observation_duration_lower_bound <= TimeDelta::Zero()) { + RTC_LOG(LS_WARNING) + << "The observation duration lower bound must be positive: " + << ToString(config_->observation_duration_lower_bound); + valid = false; + } + if (config_->observation_window_size < 2) { + RTC_LOG(LS_WARNING) << "The observation window size must be at least 2: " + << config_->observation_window_size; + valid = false; + } + if (config_->sending_rate_smoothing_factor < 0.0 || + config_->sending_rate_smoothing_factor >= 1.0) { + RTC_LOG(LS_WARNING) + << "The sending rate smoothing factor must be in [0, 1): " + << config_->sending_rate_smoothing_factor; + valid = false; + } + if (config_->tcp_fairness_temporal_weight_factor <= 0.0 || + config_->tcp_fairness_temporal_weight_factor > 1.0) { + RTC_LOG(LS_WARNING) + << "The TCP fairness temporal weight factor must be in (0, 1]" + << config_->tcp_fairness_temporal_weight_factor; + valid = false; + } + if (config_->tcp_fairness_upper_bound_bandwidth_balance <= DataRate::Zero()) { + RTC_LOG(LS_WARNING) + << "The TCP fairness upper bound bandwidth balance must be positive: " + << ToString(config_->tcp_fairness_upper_bound_bandwidth_balance); + valid = false; + } + if (config_->tcp_fairness_upper_bound_loss_offset < 0.0 || + config_->tcp_fairness_upper_bound_loss_offset >= 1.0) { + RTC_LOG(LS_WARNING) + << "The TCP fairness upper bound loss offset must be in [0, 1): " + << config_->tcp_fairness_upper_bound_loss_offset; + valid = false; + } + if (config_->temporal_weight_factor <= 0.0 || + config_->temporal_weight_factor > 1.0) { + RTC_LOG(LS_WARNING) << "The temporal weight factor must be in (0, 1]: " + << config_->temporal_weight_factor; + valid = false; + } + + return valid; +} + +double LossBasedBweV2::GetAverageReportedLossRatio() const { + if (num_observations_ <= 0) { + return 0.0; + } + + int num_packets = 0; + int num_lost_packets = 0; + for (const Observation& observation : observations_) { + if (!observation.IsInitialized()) { + continue; + } + + double tcp_fairness_temporal_weight = + tcp_fairness_temporal_weights_[(num_observations_ - 1) - + observation.id]; + num_packets += tcp_fairness_temporal_weight * observation.num_packets; + num_lost_packets += + tcp_fairness_temporal_weight * observation.num_lost_packets; + } + + return static_cast(num_lost_packets) / num_packets; +} + +std::vector LossBasedBweV2::GetCandidates() + const { + std::vector bandwidths; + for (double candidate_factor : config_->candidate_factors) { + bandwidths.emplace_back(candidate_factor * + current_estimate_.loss_limited_bandwidth); + } + + if (acknowledged_bitrate_.has_value()) { + bandwidths.emplace_back(*acknowledged_bitrate_); + } + + // TODO(crodbro): Consider adding the delay based estimate as a candidate. + + const DataRate candidate_bandwidth_upper_bound = + acknowledged_bitrate_.has_value() + ? config_->bandwidth_rampup_upper_bound_factor * + (*acknowledged_bitrate_) + : DataRate::PlusInfinity(); + + std::vector candidates; + candidates.resize(bandwidths.size()); + for (size_t i = 0; i < bandwidths.size(); ++i) { + ChannelParameters candidate = current_estimate_; + candidate.loss_limited_bandwidth = std::min( + bandwidths[i], std::max(current_estimate_.loss_limited_bandwidth, + candidate_bandwidth_upper_bound)); + candidate.inherent_loss = GetFeasibleInherentLoss(candidate); + candidates[i] = candidate; + } + return candidates; +} + +LossBasedBweV2::Derivatives LossBasedBweV2::GetDerivatives( + const ChannelParameters& channel_parameters) const { + Derivatives derivatives; + + for (const Observation& observation : observations_) { + if (!observation.IsInitialized()) { + continue; + } + + double loss_probability = GetLossProbability( + channel_parameters.inherent_loss, + channel_parameters.loss_limited_bandwidth, observation.sending_rate); + + 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 (derivatives.second >= 0.0) { + RTC_LOG(LS_ERROR) << "The second derivative is mathematically guaranteed " + "to be negative but is " + << derivatives.second << "."; + derivatives.second = -1.0e-6; + } + + return derivatives; +} + +double LossBasedBweV2::GetFeasibleInherentLoss( + const ChannelParameters& channel_parameters) const { + return std::min( + std::max(channel_parameters.inherent_loss, + config_->inherent_loss_lower_bound), + GetInherentLossUpperBound(channel_parameters.loss_limited_bandwidth)); +} + +double LossBasedBweV2::GetInherentLossUpperBound(DataRate bandwidth) const { + if (bandwidth.IsZero()) { + return 1.0; + } + + double inherent_loss_upper_bound = + config_->inherent_loss_upper_bound_offset + + config_->inherent_loss_upper_bound_bandwidth_balance / bandwidth; + return std::min(inherent_loss_upper_bound, 1.0); +} + +double LossBasedBweV2::GetObjective( + const ChannelParameters& channel_parameters) const { + double objective = 0.0; + + for (const Observation& observation : observations_) { + if (!observation.IsInitialized()) { + continue; + } + + double loss_probability = GetLossProbability( + channel_parameters.inherent_loss, + channel_parameters.loss_limited_bandwidth, observation.sending_rate); + + 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 * (config_->higher_bandwidth_bias_factor * + channel_parameters.loss_limited_bandwidth.kbps() * + observation.num_packets); + } + + return objective; +} + +DataRate LossBasedBweV2::GetSendingRate( + DataRate instantaneous_sending_rate) const { + if (num_observations_ <= 0) { + return instantaneous_sending_rate; + } + + const int most_recent_observation_idx = + (num_observations_ - 1) % config_->observation_window_size; + const Observation& most_recent_observation = + observations_[most_recent_observation_idx]; + DataRate sending_rate_previous_observation = + most_recent_observation.sending_rate; + + return config_->sending_rate_smoothing_factor * + sending_rate_previous_observation + + (1.0 - config_->sending_rate_smoothing_factor) * + instantaneous_sending_rate; +} + +DataRate LossBasedBweV2::GetTcpFairnessBandwidthUpperBound() const { + if (num_observations_ <= 0) { + return DataRate::PlusInfinity(); + } + + const double average_reported_loss_ratio = GetAverageReportedLossRatio(); + if (average_reported_loss_ratio <= + config_->tcp_fairness_upper_bound_loss_offset) { + return DataRate::PlusInfinity(); + } + return config_->tcp_fairness_upper_bound_bandwidth_balance / + (average_reported_loss_ratio - + config_->tcp_fairness_upper_bound_loss_offset); +} + +void LossBasedBweV2::CalculateTemporalWeights() { + for (int i = 0; i < config_->observation_window_size; ++i) { + temporal_weights_[i] = std::pow(config_->temporal_weight_factor, i); + tcp_fairness_temporal_weights_[i] = + std::pow(config_->tcp_fairness_temporal_weight_factor, i); + } +} + +void LossBasedBweV2::NewtonsMethodUpdate( + ChannelParameters& channel_parameters) const { + if (num_observations_ <= 0) { + return; + } + + for (int i = 0; i < config_->newton_iterations; ++i) { + const Derivatives derivatives = GetDerivatives(channel_parameters); + channel_parameters.inherent_loss -= + config_->newton_step_size * derivatives.first / derivatives.second; + channel_parameters.inherent_loss = + GetFeasibleInherentLoss(channel_parameters); + } +} + +bool LossBasedBweV2::PushBackObservation( + rtc::ArrayView packet_results) { + if (packet_results.empty()) { + return false; + } + + PacketResultsSummary packet_results_summary = + GetPacketResultsSummary(packet_results); + + partial_observation_.num_packets += packet_results_summary.num_packets; + partial_observation_.num_lost_packets += + packet_results_summary.num_lost_packets; + partial_observation_.size += packet_results_summary.total_size; + + // This is the first packet report we have received. + if (!IsValid(last_send_time_most_recent_observation_)) { + last_send_time_most_recent_observation_ = + packet_results_summary.first_send_time; + } + + const Timestamp last_send_time = packet_results_summary.last_send_time; + const TimeDelta observation_duration = + last_send_time - last_send_time_most_recent_observation_; + + // Too small to be meaningful. + if (observation_duration < config_->observation_duration_lower_bound) { + return false; + } + + last_send_time_most_recent_observation_ = last_send_time; + + Observation observation; + observation.num_packets = partial_observation_.num_packets; + observation.num_lost_packets = partial_observation_.num_lost_packets; + observation.num_received_packets = + observation.num_packets - observation.num_lost_packets; + observation.sending_rate = + GetSendingRate(partial_observation_.size / observation_duration); + observation.id = num_observations_++; + observations_[observation.id % config_->observation_window_size] = + observation; + + partial_observation_ = PartialObservation(); + + return true; +} + +} // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h new file mode 100644 index 0000000000..83d6f33598 --- /dev/null +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2.h @@ -0,0 +1,135 @@ +/* + * Copyright 2021 The WebRTC project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_ +#define MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_ + +#include +#include + +#include "absl/types/optional.h" +#include "api/array_view.h" +#include "api/transport/network_types.h" +#include "api/transport/webrtc_key_value_config.h" +#include "api/units/data_rate.h" +#include "api/units/data_size.h" +#include "api/units/time_delta.h" +#include "api/units/timestamp.h" + +namespace webrtc { + +class LossBasedBweV2 { + public: + // Creates a disabled `LossBasedBweV2` if the + // `key_value_config` is not valid. + explicit LossBasedBweV2(const WebRtcKeyValueConfig* key_value_config); + + LossBasedBweV2(const LossBasedBweV2&) = delete; + LossBasedBweV2& operator=(const LossBasedBweV2&) = delete; + + ~LossBasedBweV2() = default; + + bool IsEnabled() const; + // Returns true iff a BWE can be calculated, i.e., the estimator has been + // initialized with a BWE and then has received enough `PacketResult`s. + bool IsReady() const; + + // Returns `DataRate::PlusInfinity` if no BWE can be calculated. + DataRate GetBandwidthEstimate() const; + + void SetAcknowledgedBitrate(DataRate acknowledged_bitrate); + void SetBandwidthEstimate(DataRate bandwidth_estimate); + + void UpdateBandwidthEstimate( + rtc::ArrayView packet_results); + + private: + struct ChannelParameters { + double inherent_loss = 0.0; + DataRate loss_limited_bandwidth = DataRate::MinusInfinity(); + }; + + struct Config { + double bandwidth_rampup_upper_bound_factor = 0.0; + std::vector candidate_factors; + double higher_bandwidth_bias_factor = 0.0; + double inherent_loss_lower_bound = 0.0; + DataRate inherent_loss_upper_bound_bandwidth_balance = + DataRate::MinusInfinity(); + double inherent_loss_upper_bound_offset = 0.0; + double initial_inherent_loss_estimate = 0.0; + int newton_iterations = 0; + double newton_step_size = 0.0; + TimeDelta observation_duration_lower_bound = TimeDelta::Zero(); + int observation_window_size = 0; + double sending_rate_smoothing_factor = 0.0; + double tcp_fairness_temporal_weight_factor = 0.0; + DataRate tcp_fairness_upper_bound_bandwidth_balance = + DataRate::MinusInfinity(); + double tcp_fairness_upper_bound_loss_offset = 0.0; + double temporal_weight_factor = 0.0; + }; + + struct Derivatives { + double first = 0.0; + double second = 0.0; + }; + + struct Observation { + bool IsInitialized() const { return id != -1; } + + int num_packets = 0; + int num_lost_packets = 0; + int num_received_packets = 0; + DataRate sending_rate = DataRate::MinusInfinity(); + int id = -1; + }; + + struct PartialObservation { + int num_packets = 0; + int num_lost_packets = 0; + DataSize size = DataSize::Zero(); + }; + + static absl::optional CreateConfig( + const WebRtcKeyValueConfig* key_value_config); + bool IsConfigValid() const; + + // Returns `0.0` if not enough loss statistics have been received. + double GetAverageReportedLossRatio() const; + std::vector GetCandidates() const; + Derivatives GetDerivatives(const ChannelParameters& channel_parameters) const; + double GetFeasibleInherentLoss( + const ChannelParameters& channel_parameters) const; + double GetInherentLossUpperBound(DataRate bandwidth) const; + double GetObjective(const ChannelParameters& channel_parameters) const; + DataRate GetSendingRate(DataRate instantaneous_sending_rate) const; + DataRate GetTcpFairnessBandwidthUpperBound() const; + + void CalculateTemporalWeights(); + void NewtonsMethodUpdate(ChannelParameters& channel_parameters) const; + + // Returns false if no observation was created. + bool PushBackObservation(rtc::ArrayView packet_results); + + absl::optional acknowledged_bitrate_; + absl::optional config_; + ChannelParameters current_estimate_; + int num_observations_ = 0; + std::vector observations_; + PartialObservation partial_observation_; + Timestamp last_send_time_most_recent_observation_ = Timestamp::PlusInfinity(); + std::vector tcp_fairness_temporal_weights_; + std::vector temporal_weights_; +}; + +} // namespace webrtc + +#endif // MODULES_CONGESTION_CONTROLLER_GOOG_CC_LOSS_BASED_BWE_V2_H_ 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 new file mode 100644 index 0000000000..87ac31dfdc --- /dev/null +++ b/modules/congestion_controller/goog_cc/loss_based_bwe_v2_test.cc @@ -0,0 +1,299 @@ +/* + * Copyright 2021 The WebRTC project authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" + +#include + +#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 "rtc_base/strings/string_builder.h" +#include "test/explicit_key_value_config.h" +#include "test/gtest.h" + +namespace webrtc { + +namespace { + +constexpr TimeDelta kObservationDurationLowerBound = TimeDelta::Millis(200); + +std::string Config(bool enabled, bool valid) { + char buffer[1024]; + rtc::SimpleStringBuilder config_string(buffer); + + config_string << "WebRTC-Bwe-LossBasedBweV2/"; + + if (enabled) { + config_string << "Enabled:true"; + } else { + config_string << "Enabled:false"; + } + + if (valid) { + config_string << ",BwRampupUpperBoundFactor:1.2"; + } else { + config_string << ",BwRampupUpperBoundFactor:0.0"; + } + + config_string + << ",CandidateFactors:0.9|1.1,HigherBwBiasFactor:0.01," + "InherentLossLowerBound:0.001,InherentLossUpperBoundBwBalance:14kbps," + "InherentLossUpperBoundOffset:0.9,InitialInherentLossEstimate:0.01," + "NewtonIterations:2,NewtonStepSize:0.4,ObservationWindowSize:15," + "SendingRateSmoothingFactor:0.01,TcpFairnessTemporalWeightFactor:0.97," + "TcpFairnessUpperBoundBwBalance:90kbps," + "TcpFairnessUpperBoundLossOffset:0.1,TemporalWeightFactor:0.98"; + + config_string.AppendFormat( + ",ObservationDurationLowerBound:%dms", + static_cast(kObservationDurationLowerBound.ms())); + + config_string << "/"; + + return config_string.str(); +} + +TEST(LossBasedBweV2Test, EnabledWhenGivenValidConfigurationValues) { + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + EXPECT_TRUE(loss_based_bandwidth_estimator.IsEnabled()); +} + +TEST(LossBasedBweV2Test, DisabledWhenGivenDisabledConfiguration) { + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/false, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled()); +} + +TEST(LossBasedBweV2Test, DisabledWhenGivenNonValidConfigurationValues) { + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/false)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + EXPECT_FALSE(loss_based_bandwidth_estimator.IsEnabled()); +} + +TEST(LossBasedBweV2Test, BandwidthEstimateGivenInitializationAndThenFeedback) { + PacketResult 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.send_time = Timestamp::Zero(); + enough_feedback[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate(enough_feedback); + + EXPECT_TRUE(loss_based_bandwidth_estimator.IsReady()); + EXPECT_TRUE(loss_based_bandwidth_estimator.GetBandwidthEstimate().IsFinite()); +} + +TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNoInitialization) { + PacketResult 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.send_time = Timestamp::Zero(); + enough_feedback[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate(enough_feedback); + + EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); + EXPECT_TRUE( + loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); +} + +TEST(LossBasedBweV2Test, NoBandwidthEstimateGivenNotEnoughFeedback) { + // Create packet results where the observation duration is less than the lower + // bound. + PacketResult not_enough_feedback[2]; + not_enough_feedback[0].sent_packet.size = DataSize::Bytes(15'000); + not_enough_feedback[1].sent_packet.size = DataSize::Bytes(15'000); + not_enough_feedback[0].sent_packet.send_time = Timestamp::Zero(); + not_enough_feedback[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound / 2; + not_enough_feedback[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound / 2; + not_enough_feedback[1].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + + EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); + EXPECT_TRUE( + loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate(not_enough_feedback); + + EXPECT_FALSE(loss_based_bandwidth_estimator.IsReady()); + EXPECT_TRUE( + loss_based_bandwidth_estimator.GetBandwidthEstimate().IsPlusInfinity()); +} + +TEST(LossBasedBweV2Test, + SetValueIsTheEstimateUntilAdditionalFeedbackHasBeenReceived) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate(enough_feedback_1); + + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + DataRate::KilobitsPerSec(600)); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + DataRate::KilobitsPerSec(600)); + + loss_based_bandwidth_estimator.UpdateBandwidthEstimate(enough_feedback_2); + + EXPECT_NE(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + DataRate::KilobitsPerSec(600)); +} + +TEST(LossBasedBweV2Test, + SetAcknowledgedBitrateOnlyAffectsTheBweWhenAdditionalFeedbackIsGiven) { + PacketResult enough_feedback_1[2]; + PacketResult enough_feedback_2[2]; + enough_feedback_1[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[0].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_2[1].sent_packet.size = DataSize::Bytes(15'000); + enough_feedback_1[0].sent_packet.send_time = Timestamp::Zero(); + enough_feedback_1[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_2[0].sent_packet.send_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[1].sent_packet.send_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_1[0].receive_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_1[1].receive_time = + Timestamp::Zero() + 2 * kObservationDurationLowerBound; + enough_feedback_2[0].receive_time = + Timestamp::Zero() + 3 * kObservationDurationLowerBound; + enough_feedback_2[1].receive_time = + Timestamp::Zero() + 4 * kObservationDurationLowerBound; + + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator_1(&key_value_config); + LossBasedBweV2 loss_based_bandwidth_estimator_2(&key_value_config); + + loss_based_bandwidth_estimator_1.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator_2.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate(enough_feedback_1); + loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate(enough_feedback_1); + + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + DataRate::KilobitsPerSec(660)); + + loss_based_bandwidth_estimator_1.SetAcknowledgedBitrate( + DataRate::KilobitsPerSec(600)); + + EXPECT_EQ(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + DataRate::KilobitsPerSec(660)); + + loss_based_bandwidth_estimator_1.UpdateBandwidthEstimate(enough_feedback_2); + loss_based_bandwidth_estimator_2.UpdateBandwidthEstimate(enough_feedback_2); + + EXPECT_NE(loss_based_bandwidth_estimator_1.GetBandwidthEstimate(), + loss_based_bandwidth_estimator_2.GetBandwidthEstimate()); +} + +TEST(LossBasedBweV2Test, + BandwidthEstimateIsCappedToBeTcpFairGivenTooHighLossRate) { + PacketResult enough_feedback_no_received_packets[2]; + enough_feedback_no_received_packets[0].sent_packet.size = + DataSize::Bytes(15'000); + enough_feedback_no_received_packets[1].sent_packet.size = + DataSize::Bytes(15'000); + enough_feedback_no_received_packets[0].sent_packet.send_time = + Timestamp::Zero(); + enough_feedback_no_received_packets[1].sent_packet.send_time = + Timestamp::Zero() + kObservationDurationLowerBound; + enough_feedback_no_received_packets[0].receive_time = + Timestamp::PlusInfinity(); + enough_feedback_no_received_packets[1].receive_time = + Timestamp::PlusInfinity(); + + test::ExplicitKeyValueConfig key_value_config( + Config(/*enabled=*/true, /*valid=*/true)); + LossBasedBweV2 loss_based_bandwidth_estimator(&key_value_config); + + loss_based_bandwidth_estimator.SetBandwidthEstimate( + DataRate::KilobitsPerSec(600)); + loss_based_bandwidth_estimator.UpdateBandwidthEstimate( + enough_feedback_no_received_packets); + + EXPECT_EQ(loss_based_bandwidth_estimator.GetBandwidthEstimate(), + DataRate::KilobitsPerSec(100)); +} + +} // 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 c5f51df99b..749b073cd5 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.cc @@ -23,6 +23,7 @@ #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.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" #include "rtc_base/logging.h" @@ -226,7 +227,8 @@ SendSideBandwidthEstimation::SendSideBandwidthEstimation( low_loss_threshold_(kDefaultLowLossThreshold), high_loss_threshold_(kDefaultHighLossThreshold), bitrate_threshold_(kDefaultBitrateThreshold), - loss_based_bandwidth_estimation_(key_value_config), + loss_based_bandwidth_estimator_v1_(key_value_config), + loss_based_bandwidth_estimator_v2_(key_value_config), receiver_limit_caps_only_("Enabled") { RTC_DCHECK(event_log); if (BweLossExperimentIsEnabled()) { @@ -341,18 +343,29 @@ void SendSideBandwidthEstimation::SetAcknowledgedRate( absl::optional acknowledged_rate, Timestamp at_time) { acknowledged_rate_ = acknowledged_rate; - if (acknowledged_rate && loss_based_bandwidth_estimation_.Enabled()) { - loss_based_bandwidth_estimation_.UpdateAcknowledgedBitrate( + if (!acknowledged_rate.has_value()) { + return; + } + if (LossBasedBandwidthEstimatorV1Enabled()) { + loss_based_bandwidth_estimator_v1_.UpdateAcknowledgedBitrate( *acknowledged_rate, at_time); } + if (LossBasedBandwidthEstimatorV2Enabled()) { + loss_based_bandwidth_estimator_v2_.SetAcknowledgedBitrate( + *acknowledged_rate); + } } void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( const TransportPacketsFeedback& report) { - if (loss_based_bandwidth_estimation_.Enabled()) { - loss_based_bandwidth_estimation_.UpdateLossStatistics( + if (LossBasedBandwidthEstimatorV1Enabled()) { + loss_based_bandwidth_estimator_v1_.UpdateLossStatistics( report.packet_feedbacks, report.feedback_time); } + if (LossBasedBandwidthEstimatorV2Enabled()) { + loss_based_bandwidth_estimator_v2_.UpdateBandwidthEstimate( + report.packet_feedbacks); + } } void SendSideBandwidthEstimation::UpdatePacketsLost(int64_t packets_lost, @@ -459,13 +472,16 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { new_bitrate = std::max(receiver_limit_, new_bitrate); if (delay_based_limit_.IsFinite()) new_bitrate = std::max(delay_based_limit_, new_bitrate); - if (loss_based_bandwidth_estimation_.Enabled()) { - loss_based_bandwidth_estimation_.Initialize(new_bitrate); + if (LossBasedBandwidthEstimatorV1Enabled()) { + loss_based_bandwidth_estimator_v1_.Initialize(new_bitrate); + } + if (LossBasedBandwidthEstimatorV2Enabled()) { + loss_based_bandwidth_estimator_v2_.SetBandwidthEstimate(new_bitrate); } if (new_bitrate != current_target_) { min_bitrate_history_.clear(); - if (loss_based_bandwidth_estimation_.Enabled()) { + if (LossBasedBandwidthEstimatorV1Enabled()) { min_bitrate_history_.push_back(std::make_pair(at_time, new_bitrate)); } else { min_bitrate_history_.push_back( @@ -483,14 +499,22 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { return; } - if (loss_based_bandwidth_estimation_.InUse()) { - DataRate new_bitrate = loss_based_bandwidth_estimation_.Update( + if (LossBasedBandwidthEstimatorV1ReadyForUse()) { + DataRate new_bitrate = loss_based_bandwidth_estimator_v1_.Update( at_time, min_bitrate_history_.front().second, delay_based_limit_, last_round_trip_time_); UpdateTargetBitrate(new_bitrate, at_time); return; } + if (LossBasedBandwidthEstimatorV2ReadyForUse()) { + DataRate new_bitrate = + loss_based_bandwidth_estimator_v2_.GetBandwidthEstimate(); + new_bitrate = std::min(new_bitrate, delay_based_limit_); + UpdateTargetBitrate(new_bitrate, 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. @@ -628,4 +652,26 @@ void SendSideBandwidthEstimation::UpdateTargetBitrate(DataRate new_bitrate, void SendSideBandwidthEstimation::ApplyTargetLimits(Timestamp at_time) { UpdateTargetBitrate(current_target_, at_time); } + +bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV1Enabled() const { + return loss_based_bandwidth_estimator_v1_.Enabled() && + !LossBasedBandwidthEstimatorV2Enabled(); +} + +bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV1ReadyForUse() + const { + return LossBasedBandwidthEstimatorV1Enabled() && + loss_based_bandwidth_estimator_v1_.InUse(); +} + +bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2Enabled() const { + return loss_based_bandwidth_estimator_v2_.IsEnabled(); +} + +bool SendSideBandwidthEstimation::LossBasedBandwidthEstimatorV2ReadyForUse() + const { + return LossBasedBandwidthEstimatorV2Enabled() && + 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 5917d9b271..63be1f03ef 100644 --- a/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h +++ b/modules/congestion_controller/goog_cc/send_side_bandwidth_estimation.h @@ -26,6 +26,7 @@ #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/congestion_controller/goog_cc/loss_based_bandwidth_estimation.h" +#include "modules/congestion_controller/goog_cc/loss_based_bwe_v2.h" #include "rtc_base/experiments/field_trial_parser.h" namespace webrtc { @@ -149,6 +150,12 @@ class SendSideBandwidthEstimation { // should be cleaned up. void ApplyTargetLimits(Timestamp at_time); + bool LossBasedBandwidthEstimatorV1Enabled() const; + bool LossBasedBandwidthEstimatorV2Enabled() const; + + bool LossBasedBandwidthEstimatorV1ReadyForUse() const; + bool LossBasedBandwidthEstimatorV2ReadyForUse() const; + RttBasedBackoff rtt_backoff_; LinkCapacityTracker link_capacity_; @@ -189,7 +196,8 @@ class SendSideBandwidthEstimation { float low_loss_threshold_; float high_loss_threshold_; DataRate bitrate_threshold_; - LossBasedBandwidthEstimation loss_based_bandwidth_estimation_; + LossBasedBandwidthEstimation loss_based_bandwidth_estimator_v1_; + LossBasedBweV2 loss_based_bandwidth_estimator_v2_; FieldTrialFlag receiver_limit_caps_only_; }; } // namespace webrtc diff --git a/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc b/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc index 52baab06c7..6dadf8b9c4 100644 --- a/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc +++ b/modules/congestion_controller/goog_cc/test/goog_cc_printer.cc @@ -87,7 +87,7 @@ std::deque GoogCcStatePrinter::CreateLoggers() { }; auto loss_cont = [&] { return &controller_->bandwidth_estimation_ - ->loss_based_bandwidth_estimation_; + ->loss_based_bandwidth_estimator_v1_; }; std::deque loggers({ Log("time", [=] { return target_.at_time; }),