| /* |
| * 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 <algorithm> |
| #include <cmath> |
| #include <cstddef> |
| #include <cstdlib> |
| #include <limits> |
| #include <optional> |
| #include <vector> |
| |
| #include "absl/algorithm/container.h" |
| #include "api/array_view.h" |
| #include "api/field_trials_view.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 "modules/remote_bitrate_estimator/include/bwe_defines.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 { |
| |
| constexpr TimeDelta kInitHoldDuration = TimeDelta::Millis(300); |
| constexpr TimeDelta kMaxHoldDuration = TimeDelta::Seconds(60); |
| |
| bool IsValid(DataRate datarate) { |
| return datarate.IsFinite(); |
| } |
| |
| bool IsValid(std::optional<DataRate> datarate) { |
| return datarate.has_value() && IsValid(datarate.value()); |
| } |
| |
| bool IsValid(Timestamp timestamp) { |
| return timestamp.IsFinite(); |
| } |
| |
| double ToKiloBytes(DataSize datasize) { return datasize.bytes() / 1000.0; } |
| |
| 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); |
| } |
| |
| double loss_probability = inherent_loss; |
| if (IsValid(sending_rate) && IsValid(loss_limited_bandwidth) && |
| (sending_rate > loss_limited_bandwidth)) { |
| loss_probability += (1 - inherent_loss) * |
| (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 FieldTrialsView* 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_best_estimate_.inherent_loss = |
| config_->initial_inherent_loss_estimate; |
| observations_.resize(config_->observation_window_size); |
| temporal_weights_.resize(config_->observation_window_size); |
| instant_upper_bound_temporal_weights_.resize( |
| config_->observation_window_size); |
| CalculateTemporalWeights(); |
| last_hold_info_.duration = kInitHoldDuration; |
| } |
| |
| bool LossBasedBweV2::IsEnabled() const { |
| return config_.has_value(); |
| } |
| |
| bool LossBasedBweV2::IsReady() const { |
| return IsEnabled() && |
| IsValid(current_best_estimate_.loss_limited_bandwidth) && |
| num_observations_ >= config_->min_num_observations; |
| } |
| |
| bool LossBasedBweV2::ReadyToUseInStartPhase() const { |
| return IsReady() && config_->use_in_start_phase; |
| } |
| |
| bool LossBasedBweV2::UseInStartPhase() const { |
| return config_->use_in_start_phase; |
| } |
| |
| LossBasedBweV2::Result LossBasedBweV2::GetLossBasedResult() const { |
| if (!IsReady()) { |
| if (!IsEnabled()) { |
| RTC_LOG(LS_WARNING) |
| << "The estimator must be enabled before it can be used."; |
| } else { |
| if (!IsValid(current_best_estimate_.loss_limited_bandwidth)) { |
| RTC_LOG(LS_WARNING) |
| << "The estimator must be initialized before it can be used."; |
| } |
| if (num_observations_ <= config_->min_num_observations) { |
| RTC_LOG(LS_WARNING) << "The estimator must receive enough loss " |
| "statistics before it can be used."; |
| } |
| } |
| return {.bandwidth_estimate = IsValid(delay_based_estimate_) |
| ? delay_based_estimate_ |
| : DataRate::PlusInfinity(), |
| .state = LossBasedState::kDelayBasedEstimate}; |
| } |
| return loss_based_result_; |
| } |
| |
| void LossBasedBweV2::SetAcknowledgedBitrate(DataRate acknowledged_bitrate) { |
| if (IsValid(acknowledged_bitrate)) { |
| acknowledged_bitrate_ = acknowledged_bitrate; |
| CalculateInstantLowerBound(); |
| } 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_best_estimate_.loss_limited_bandwidth = bandwidth_estimate; |
| loss_based_result_ = {.bandwidth_estimate = bandwidth_estimate, |
| .state = LossBasedState::kDelayBasedEstimate}; |
| } else { |
| RTC_LOG(LS_WARNING) << "The bandwidth estimate must be finite: " |
| << ToString(bandwidth_estimate); |
| } |
| } |
| |
| void LossBasedBweV2::SetMinMaxBitrate(DataRate min_bitrate, |
| DataRate max_bitrate) { |
| if (IsValid(min_bitrate)) { |
| min_bitrate_ = min_bitrate; |
| CalculateInstantLowerBound(); |
| } else { |
| RTC_LOG(LS_WARNING) << "The min bitrate must be finite: " |
| << ToString(min_bitrate); |
| } |
| |
| if (IsValid(max_bitrate)) { |
| max_bitrate_ = max_bitrate; |
| } else { |
| RTC_LOG(LS_WARNING) << "The max bitrate must be finite: " |
| << ToString(max_bitrate); |
| } |
| } |
| |
| void LossBasedBweV2::UpdateBandwidthEstimate( |
| rtc::ArrayView<const PacketResult> packet_results, |
| DataRate delay_based_estimate, |
| bool in_alr) { |
| delay_based_estimate_ = delay_based_estimate; |
| 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_best_estimate_.loss_limited_bandwidth)) { |
| if (!IsValid(delay_based_estimate)) { |
| RTC_LOG(LS_WARNING) << "The delay based estimate must be finite: " |
| << ToString(delay_based_estimate); |
| return; |
| } |
| current_best_estimate_.loss_limited_bandwidth = delay_based_estimate; |
| loss_based_result_ = {.bandwidth_estimate = delay_based_estimate, |
| .state = LossBasedState::kDelayBasedEstimate}; |
| } |
| |
| ChannelParameters best_candidate = current_best_estimate_; |
| double objective_max = std::numeric_limits<double>::lowest(); |
| for (ChannelParameters candidate : GetCandidates(in_alr)) { |
| NewtonsMethodUpdate(candidate); |
| |
| const double candidate_objective = GetObjective(candidate); |
| if (candidate_objective > objective_max) { |
| objective_max = candidate_objective; |
| best_candidate = candidate; |
| } |
| } |
| if (best_candidate.loss_limited_bandwidth < |
| current_best_estimate_.loss_limited_bandwidth) { |
| last_time_estimate_reduced_ = last_send_time_most_recent_observation_; |
| } |
| |
| // Do not increase the estimate if the average loss is greater than current |
| // inherent loss. |
| if (average_reported_loss_ratio_ > best_candidate.inherent_loss && |
| config_->not_increase_if_inherent_loss_less_than_average_loss && |
| current_best_estimate_.loss_limited_bandwidth < |
| best_candidate.loss_limited_bandwidth) { |
| best_candidate.loss_limited_bandwidth = |
| current_best_estimate_.loss_limited_bandwidth; |
| } |
| |
| if (IsInLossLimitedState()) { |
| // Bound the estimate increase if: |
| // 1. The estimate has been increased for less than |
| // `delayed_increase_window` ago, and |
| // 2. The best candidate is greater than bandwidth_limit_in_current_window. |
| if (recovering_after_loss_timestamp_.IsFinite() && |
| recovering_after_loss_timestamp_ + config_->delayed_increase_window > |
| last_send_time_most_recent_observation_ && |
| best_candidate.loss_limited_bandwidth > |
| bandwidth_limit_in_current_window_) { |
| best_candidate.loss_limited_bandwidth = |
| bandwidth_limit_in_current_window_; |
| } |
| |
| bool increasing_when_loss_limited = IsEstimateIncreasingWhenLossLimited( |
| /*old_estimate=*/current_best_estimate_.loss_limited_bandwidth, |
| /*new_estimate=*/best_candidate.loss_limited_bandwidth); |
| // Bound the best candidate by the acked bitrate. |
| if (increasing_when_loss_limited && IsValid(acknowledged_bitrate_)) { |
| double rampup_factor = config_->bandwidth_rampup_upper_bound_factor; |
| if (IsValid(last_hold_info_.rate) && |
| acknowledged_bitrate_ < |
| config_->bandwidth_rampup_hold_threshold * last_hold_info_.rate) { |
| rampup_factor = config_->bandwidth_rampup_upper_bound_factor_in_hold; |
| } |
| |
| best_candidate.loss_limited_bandwidth = |
| std::max(current_best_estimate_.loss_limited_bandwidth, |
| std::min(best_candidate.loss_limited_bandwidth, |
| rampup_factor * (*acknowledged_bitrate_))); |
| // Increase current estimate by at least 1kbps to make sure that the state |
| // will be switched to kIncreasing, thus padding is triggered. |
| if (loss_based_result_.state == LossBasedState::kDecreasing && |
| best_candidate.loss_limited_bandwidth == |
| current_best_estimate_.loss_limited_bandwidth) { |
| best_candidate.loss_limited_bandwidth = |
| current_best_estimate_.loss_limited_bandwidth + |
| DataRate::BitsPerSec(1); |
| } |
| } |
| } |
| |
| DataRate bounded_bandwidth_estimate = DataRate::PlusInfinity(); |
| if (IsValid(delay_based_estimate_)) { |
| bounded_bandwidth_estimate = |
| std::max(GetInstantLowerBound(), |
| std::min({best_candidate.loss_limited_bandwidth, |
| GetInstantUpperBound(), delay_based_estimate_})); |
| } else { |
| bounded_bandwidth_estimate = std::max( |
| GetInstantLowerBound(), std::min(best_candidate.loss_limited_bandwidth, |
| GetInstantUpperBound())); |
| } |
| if (config_->bound_best_candidate && |
| bounded_bandwidth_estimate < best_candidate.loss_limited_bandwidth) { |
| RTC_LOG(LS_INFO) << "Resetting loss based BWE to " |
| << bounded_bandwidth_estimate.kbps() |
| << "due to loss. Avg loss rate: " |
| << average_reported_loss_ratio_; |
| current_best_estimate_.loss_limited_bandwidth = bounded_bandwidth_estimate; |
| current_best_estimate_.inherent_loss = 0; |
| } else { |
| current_best_estimate_ = best_candidate; |
| if (config_->lower_bound_by_acked_rate_factor > 0.0) { |
| current_best_estimate_.loss_limited_bandwidth = |
| std::max(current_best_estimate_.loss_limited_bandwidth, |
| GetInstantLowerBound()); |
| } |
| } |
| |
| if (loss_based_result_.state == LossBasedState::kDecreasing && |
| last_hold_info_.timestamp > last_send_time_most_recent_observation_ && |
| bounded_bandwidth_estimate < delay_based_estimate_) { |
| // Ensure that acked rate is the lower bound of HOLD rate. |
| if (config_->lower_bound_by_acked_rate_factor > 0.0) { |
| last_hold_info_.rate = |
| std::max(GetInstantLowerBound(), last_hold_info_.rate); |
| } |
| // BWE is not allowed to increase above the HOLD rate. The purpose of |
| // HOLD is to not immediately ramp up BWE to a rate that may cause loss. |
| loss_based_result_.bandwidth_estimate = |
| std::min(last_hold_info_.rate, bounded_bandwidth_estimate); |
| return; |
| } |
| |
| if (IsEstimateIncreasingWhenLossLimited( |
| /*old_estimate=*/loss_based_result_.bandwidth_estimate, |
| /*new_estimate=*/bounded_bandwidth_estimate) && |
| CanKeepIncreasingState(bounded_bandwidth_estimate) && |
| bounded_bandwidth_estimate < delay_based_estimate_ && |
| bounded_bandwidth_estimate < max_bitrate_) { |
| if (config_->padding_duration > TimeDelta::Zero() && |
| bounded_bandwidth_estimate > last_padding_info_.padding_rate) { |
| // Start a new padding duration. |
| last_padding_info_.padding_rate = bounded_bandwidth_estimate; |
| last_padding_info_.padding_timestamp = |
| last_send_time_most_recent_observation_; |
| } |
| loss_based_result_.state = config_->padding_duration > TimeDelta::Zero() |
| ? LossBasedState::kIncreaseUsingPadding |
| : LossBasedState::kIncreasing; |
| } else if (bounded_bandwidth_estimate < delay_based_estimate_ && |
| bounded_bandwidth_estimate < max_bitrate_) { |
| if (loss_based_result_.state != LossBasedState::kDecreasing && |
| config_->hold_duration_factor > 0) { |
| RTC_LOG(LS_INFO) << this << " " |
| << "Switch to HOLD. Bounded BWE: " |
| << bounded_bandwidth_estimate.kbps() |
| << ", duration: " << last_hold_info_.duration.ms(); |
| last_hold_info_ = { |
| .timestamp = last_send_time_most_recent_observation_ + |
| last_hold_info_.duration, |
| .duration = |
| std::min(kMaxHoldDuration, last_hold_info_.duration * |
| config_->hold_duration_factor), |
| .rate = bounded_bandwidth_estimate}; |
| } |
| last_padding_info_ = PaddingInfo(); |
| loss_based_result_.state = LossBasedState::kDecreasing; |
| } else { |
| // Reset the HOLD info if delay based estimate works to avoid getting |
| // stuck in low bitrate. |
| last_hold_info_ = {.timestamp = Timestamp::MinusInfinity(), |
| .duration = kInitHoldDuration, |
| .rate = DataRate::PlusInfinity()}; |
| last_padding_info_ = PaddingInfo(); |
| loss_based_result_.state = LossBasedState::kDelayBasedEstimate; |
| } |
| loss_based_result_.bandwidth_estimate = bounded_bandwidth_estimate; |
| |
| if (IsInLossLimitedState() && |
| (recovering_after_loss_timestamp_.IsInfinite() || |
| recovering_after_loss_timestamp_ + config_->delayed_increase_window < |
| last_send_time_most_recent_observation_)) { |
| bandwidth_limit_in_current_window_ = |
| std::max(kCongestionControllerMinBitrate, |
| current_best_estimate_.loss_limited_bandwidth * |
| config_->max_increase_factor); |
| recovering_after_loss_timestamp_ = last_send_time_most_recent_observation_; |
| } |
| } |
| |
| bool LossBasedBweV2::IsEstimateIncreasingWhenLossLimited( |
| DataRate old_estimate, DataRate new_estimate) { |
| return (old_estimate < new_estimate || |
| (old_estimate == new_estimate && |
| (loss_based_result_.state == LossBasedState::kIncreasing || |
| loss_based_result_.state == |
| LossBasedState::kIncreaseUsingPadding))) && |
| IsInLossLimitedState(); |
| } |
| |
| // Returns a `LossBasedBweV2::Config` iff the `key_value_config` specifies a |
| // configuration for the `LossBasedBweV2` which is explicitly enabled. |
| std::optional<LossBasedBweV2::Config> LossBasedBweV2::CreateConfig( |
| const FieldTrialsView* key_value_config) { |
| FieldTrialParameter<bool> enabled("Enabled", true); |
| FieldTrialParameter<double> bandwidth_rampup_upper_bound_factor( |
| "BwRampupUpperBoundFactor", 1000000.0); |
| FieldTrialParameter<double> bandwidth_rampup_upper_bound_factor_in_hold( |
| "BwRampupUpperBoundInHoldFactor", 1000000.0); |
| FieldTrialParameter<double> bandwidth_rampup_hold_threshold( |
| "BwRampupUpperBoundHoldThreshold", 1.3); |
| FieldTrialParameter<double> rampup_acceleration_max_factor( |
| "BwRampupAccelMaxFactor", 0.0); |
| FieldTrialParameter<TimeDelta> rampup_acceleration_maxout_time( |
| "BwRampupAccelMaxoutTime", TimeDelta::Seconds(60)); |
| FieldTrialList<double> candidate_factors("CandidateFactors", |
| {1.02, 1.0, 0.95}); |
| FieldTrialParameter<double> higher_bandwidth_bias_factor("HigherBwBiasFactor", |
| 0.0002); |
| FieldTrialParameter<double> higher_log_bandwidth_bias_factor( |
| "HigherLogBwBiasFactor", 0.02); |
| FieldTrialParameter<double> inherent_loss_lower_bound( |
| "InherentLossLowerBound", 1.0e-3); |
| FieldTrialParameter<double> loss_threshold_of_high_bandwidth_preference( |
| "LossThresholdOfHighBandwidthPreference", 0.15); |
| FieldTrialParameter<double> bandwidth_preference_smoothing_factor( |
| "BandwidthPreferenceSmoothingFactor", 0.002); |
| FieldTrialParameter<DataRate> inherent_loss_upper_bound_bandwidth_balance( |
| "InherentLossUpperBoundBwBalance", DataRate::KilobitsPerSec(75.0)); |
| FieldTrialParameter<double> inherent_loss_upper_bound_offset( |
| "InherentLossUpperBoundOffset", 0.05); |
| FieldTrialParameter<double> initial_inherent_loss_estimate( |
| "InitialInherentLossEstimate", 0.01); |
| FieldTrialParameter<int> newton_iterations("NewtonIterations", 1); |
| FieldTrialParameter<double> newton_step_size("NewtonStepSize", 0.75); |
| FieldTrialParameter<bool> append_acknowledged_rate_candidate( |
| "AckedRateCandidate", true); |
| FieldTrialParameter<bool> append_delay_based_estimate_candidate( |
| "DelayBasedCandidate", true); |
| FieldTrialParameter<bool> append_upper_bound_candidate_in_alr( |
| "UpperBoundCandidateInAlr", false); |
| FieldTrialParameter<TimeDelta> observation_duration_lower_bound( |
| "ObservationDurationLowerBound", TimeDelta::Millis(250)); |
| FieldTrialParameter<int> observation_window_size("ObservationWindowSize", 20); |
| FieldTrialParameter<double> sending_rate_smoothing_factor( |
| "SendingRateSmoothingFactor", 0.0); |
| FieldTrialParameter<double> instant_upper_bound_temporal_weight_factor( |
| "InstantUpperBoundTemporalWeightFactor", 0.9); |
| FieldTrialParameter<DataRate> instant_upper_bound_bandwidth_balance( |
| "InstantUpperBoundBwBalance", DataRate::KilobitsPerSec(75.0)); |
| FieldTrialParameter<double> instant_upper_bound_loss_offset( |
| "InstantUpperBoundLossOffset", 0.05); |
| FieldTrialParameter<double> temporal_weight_factor("TemporalWeightFactor", |
| 0.9); |
| FieldTrialParameter<double> bandwidth_backoff_lower_bound_factor( |
| "BwBackoffLowerBoundFactor", 1.0); |
| FieldTrialParameter<double> max_increase_factor("MaxIncreaseFactor", 1.3); |
| FieldTrialParameter<TimeDelta> delayed_increase_window( |
| "DelayedIncreaseWindow", TimeDelta::Millis(300)); |
| FieldTrialParameter<bool> |
| not_increase_if_inherent_loss_less_than_average_loss( |
| "NotIncreaseIfInherentLossLessThanAverageLoss", true); |
| FieldTrialParameter<bool> not_use_acked_rate_in_alr("NotUseAckedRateInAlr", |
| true); |
| FieldTrialParameter<bool> use_in_start_phase("UseInStartPhase", false); |
| FieldTrialParameter<int> min_num_observations("MinNumObservations", 3); |
| FieldTrialParameter<double> lower_bound_by_acked_rate_factor( |
| "LowerBoundByAckedRateFactor", 0.0); |
| FieldTrialParameter<double> hold_duration_factor("HoldDurationFactor", 0.0); |
| FieldTrialParameter<bool> use_byte_loss_rate("UseByteLossRate", false); |
| FieldTrialParameter<TimeDelta> padding_duration("PaddingDuration", |
| TimeDelta::Zero()); |
| FieldTrialParameter<bool> bound_best_candidate("BoundBestCandidate", false); |
| FieldTrialParameter<bool> pace_at_loss_based_estimate( |
| "PaceAtLossBasedEstimate", false); |
| FieldTrialParameter<double> median_sending_rate_factor( |
| "MedianSendingRateFactor", 2.0); |
| if (key_value_config) { |
| ParseFieldTrial({&enabled, |
| &bandwidth_rampup_upper_bound_factor, |
| &bandwidth_rampup_upper_bound_factor_in_hold, |
| &bandwidth_rampup_hold_threshold, |
| &rampup_acceleration_max_factor, |
| &rampup_acceleration_maxout_time, |
| &candidate_factors, |
| &higher_bandwidth_bias_factor, |
| &higher_log_bandwidth_bias_factor, |
| &inherent_loss_lower_bound, |
| &loss_threshold_of_high_bandwidth_preference, |
| &bandwidth_preference_smoothing_factor, |
| &inherent_loss_upper_bound_bandwidth_balance, |
| &inherent_loss_upper_bound_offset, |
| &initial_inherent_loss_estimate, |
| &newton_iterations, |
| &newton_step_size, |
| &append_acknowledged_rate_candidate, |
| &append_delay_based_estimate_candidate, |
| &append_upper_bound_candidate_in_alr, |
| &observation_duration_lower_bound, |
| &observation_window_size, |
| &sending_rate_smoothing_factor, |
| &instant_upper_bound_temporal_weight_factor, |
| &instant_upper_bound_bandwidth_balance, |
| &instant_upper_bound_loss_offset, |
| &temporal_weight_factor, |
| &bandwidth_backoff_lower_bound_factor, |
| &max_increase_factor, |
| &delayed_increase_window, |
| ¬_increase_if_inherent_loss_less_than_average_loss, |
| ¬_use_acked_rate_in_alr, |
| &use_in_start_phase, |
| &min_num_observations, |
| &lower_bound_by_acked_rate_factor, |
| &hold_duration_factor, |
| &use_byte_loss_rate, |
| &padding_duration, |
| &bound_best_candidate, |
| &pace_at_loss_based_estimate, |
| &median_sending_rate_factor}, |
| key_value_config->Lookup("WebRTC-Bwe-LossBasedBweV2")); |
| } |
| |
| if (!enabled.Get()) { |
| return std::nullopt; |
| } |
| Config config; |
| config.bandwidth_rampup_upper_bound_factor = |
| bandwidth_rampup_upper_bound_factor.Get(); |
| config.bandwidth_rampup_upper_bound_factor_in_hold = |
| bandwidth_rampup_upper_bound_factor_in_hold.Get(); |
| config.bandwidth_rampup_hold_threshold = |
| bandwidth_rampup_hold_threshold.Get(); |
| config.rampup_acceleration_max_factor = rampup_acceleration_max_factor.Get(); |
| config.rampup_acceleration_maxout_time = |
| rampup_acceleration_maxout_time.Get(); |
| config.candidate_factors = candidate_factors.Get(); |
| config.higher_bandwidth_bias_factor = higher_bandwidth_bias_factor.Get(); |
| config.higher_log_bandwidth_bias_factor = |
| higher_log_bandwidth_bias_factor.Get(); |
| config.inherent_loss_lower_bound = inherent_loss_lower_bound.Get(); |
| config.loss_threshold_of_high_bandwidth_preference = |
| loss_threshold_of_high_bandwidth_preference.Get(); |
| config.bandwidth_preference_smoothing_factor = |
| bandwidth_preference_smoothing_factor.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.append_acknowledged_rate_candidate = |
| append_acknowledged_rate_candidate.Get(); |
| config.append_delay_based_estimate_candidate = |
| append_delay_based_estimate_candidate.Get(); |
| config.append_upper_bound_candidate_in_alr = |
| append_upper_bound_candidate_in_alr.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.instant_upper_bound_temporal_weight_factor = |
| instant_upper_bound_temporal_weight_factor.Get(); |
| config.instant_upper_bound_bandwidth_balance = |
| instant_upper_bound_bandwidth_balance.Get(); |
| config.instant_upper_bound_loss_offset = |
| instant_upper_bound_loss_offset.Get(); |
| config.temporal_weight_factor = temporal_weight_factor.Get(); |
| config.bandwidth_backoff_lower_bound_factor = |
| bandwidth_backoff_lower_bound_factor.Get(); |
| config.max_increase_factor = max_increase_factor.Get(); |
| config.delayed_increase_window = delayed_increase_window.Get(); |
| config.not_increase_if_inherent_loss_less_than_average_loss = |
| not_increase_if_inherent_loss_less_than_average_loss.Get(); |
| config.not_use_acked_rate_in_alr = not_use_acked_rate_in_alr.Get(); |
| config.use_in_start_phase = use_in_start_phase.Get(); |
| config.min_num_observations = min_num_observations.Get(); |
| config.lower_bound_by_acked_rate_factor = |
| lower_bound_by_acked_rate_factor.Get(); |
| config.hold_duration_factor = hold_duration_factor.Get(); |
| config.use_byte_loss_rate = use_byte_loss_rate.Get(); |
| config.padding_duration = padding_duration.Get(); |
| config.bound_best_candidate = bound_best_candidate.Get(); |
| config.pace_at_loss_based_estimate = pace_at_loss_based_estimate.Get(); |
| config.median_sending_rate_factor = median_sending_rate_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_->bandwidth_rampup_upper_bound_factor_in_hold <= 1.0) { |
| RTC_LOG(LS_WARNING) << "The bandwidth rampup upper bound factor in hold " |
| "must be greater than 1: " |
| << config_->bandwidth_rampup_upper_bound_factor_in_hold; |
| valid = false; |
| } |
| if (config_->bandwidth_rampup_hold_threshold < 0.0) { |
| RTC_LOG(LS_WARNING) << "The bandwidth rampup hold threshold must" |
| "must be non-negative.: " |
| << config_->bandwidth_rampup_hold_threshold; |
| valid = false; |
| } |
| if (config_->rampup_acceleration_max_factor < 0.0) { |
| RTC_LOG(LS_WARNING) |
| << "The rampup acceleration max factor must be non-negative.: " |
| << config_->rampup_acceleration_max_factor; |
| valid = false; |
| } |
| if (config_->rampup_acceleration_maxout_time <= TimeDelta::Zero()) { |
| RTC_LOG(LS_WARNING) |
| << "The rampup acceleration maxout time must be above zero: " |
| << config_->rampup_acceleration_maxout_time.seconds(); |
| valid = false; |
| } |
| for (double candidate_factor : config_->candidate_factors) { |
| if (candidate_factor <= 0.0) { |
| RTC_LOG(LS_WARNING) << "All candidate factors must be greater than zero: " |
| << candidate_factor; |
| valid = false; |
| } |
| } |
| |
| // Ensure that the configuration allows generation of at least one candidate |
| // other than the current estimate. |
| if (!config_->append_acknowledged_rate_candidate && |
| !config_->append_delay_based_estimate_candidate && |
| !absl::c_any_of(config_->candidate_factors, |
| [](double cf) { return cf != 1.0; })) { |
| RTC_LOG(LS_WARNING) |
| << "The configuration does not allow generating candidates. Specify " |
| "a candidate factor other than 1.0, allow the acknowledged rate " |
| "to be a candidate, and/or allow the delay based estimate to be a " |
| "candidate."; |
| 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_->loss_threshold_of_high_bandwidth_preference < 0.0 || |
| config_->loss_threshold_of_high_bandwidth_preference >= 1.0) { |
| RTC_LOG(LS_WARNING) |
| << "The loss threshold of high bandwidth preference must be in [0, 1): " |
| << config_->loss_threshold_of_high_bandwidth_preference; |
| valid = false; |
| } |
| if (config_->bandwidth_preference_smoothing_factor <= 0.0 || |
| config_->bandwidth_preference_smoothing_factor > 1.0) { |
| RTC_LOG(LS_WARNING) |
| << "The bandwidth preference smoothing factor must be in (0, 1]: " |
| << config_->bandwidth_preference_smoothing_factor; |
| 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_->instant_upper_bound_temporal_weight_factor <= 0.0 || |
| config_->instant_upper_bound_temporal_weight_factor > 1.0) { |
| RTC_LOG(LS_WARNING) |
| << "The instant upper bound temporal weight factor must be in (0, 1]" |
| << config_->instant_upper_bound_temporal_weight_factor; |
| valid = false; |
| } |
| if (config_->instant_upper_bound_bandwidth_balance <= DataRate::Zero()) { |
| RTC_LOG(LS_WARNING) |
| << "The instant upper bound bandwidth balance must be positive: " |
| << ToString(config_->instant_upper_bound_bandwidth_balance); |
| valid = false; |
| } |
| if (config_->instant_upper_bound_loss_offset < 0.0 || |
| config_->instant_upper_bound_loss_offset >= 1.0) { |
| RTC_LOG(LS_WARNING) |
| << "The instant upper bound loss offset must be in [0, 1): " |
| << config_->instant_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; |
| } |
| if (config_->bandwidth_backoff_lower_bound_factor > 1.0) { |
| RTC_LOG(LS_WARNING) |
| << "The bandwidth backoff lower bound factor must not be greater than " |
| "1: " |
| << config_->bandwidth_backoff_lower_bound_factor; |
| valid = false; |
| } |
| if (config_->max_increase_factor <= 0.0) { |
| RTC_LOG(LS_WARNING) << "The maximum increase factor must be positive: " |
| << config_->max_increase_factor; |
| valid = false; |
| } |
| if (config_->delayed_increase_window <= TimeDelta::Zero()) { |
| RTC_LOG(LS_WARNING) << "The delayed increase window must be positive: " |
| << config_->delayed_increase_window.ms(); |
| valid = false; |
| } |
| if (config_->min_num_observations <= 0) { |
| RTC_LOG(LS_WARNING) << "The min number of observations must be positive: " |
| << config_->min_num_observations; |
| valid = false; |
| } |
| if (config_->lower_bound_by_acked_rate_factor < 0.0) { |
| RTC_LOG(LS_WARNING) |
| << "The estimate lower bound by acknowledged rate factor must be " |
| "non-negative: " |
| << config_->lower_bound_by_acked_rate_factor; |
| valid = false; |
| } |
| return valid; |
| } |
| |
| void LossBasedBweV2::UpdateAverageReportedLossRatio() { |
| average_reported_loss_ratio_ = |
| (config_->use_byte_loss_rate ? CalculateAverageReportedByteLossRatio() |
| : CalculateAverageReportedPacketLossRatio()); |
| } |
| |
| double LossBasedBweV2::CalculateAverageReportedPacketLossRatio() const { |
| if (num_observations_ <= 0) { |
| return 0.0; |
| } |
| |
| double num_packets = 0; |
| double num_lost_packets = 0; |
| for (const Observation& observation : observations_) { |
| if (!observation.IsInitialized()) { |
| continue; |
| } |
| |
| double instant_temporal_weight = |
| instant_upper_bound_temporal_weights_[(num_observations_ - 1) - |
| observation.id]; |
| num_packets += instant_temporal_weight * observation.num_packets; |
| num_lost_packets += instant_temporal_weight * observation.num_lost_packets; |
| } |
| |
| return num_lost_packets / num_packets; |
| } |
| |
| double LossBasedBweV2::CalculateAverageReportedByteLossRatio() const { |
| if (num_observations_ <= 0) { |
| return 0.0; |
| } |
| |
| DataSize total_bytes = DataSize::Zero(); |
| DataSize lost_bytes = DataSize::Zero(); |
| double min_loss_rate = 1.0; |
| double max_loss_rate = 0.0; |
| DataSize min_lost_bytes = DataSize::Zero(); |
| DataSize max_lost_bytes = DataSize::Zero(); |
| DataSize min_bytes_received = DataSize::Zero(); |
| DataSize max_bytes_received = DataSize::Zero(); |
| DataRate send_rate_of_max_loss_observation = DataRate::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; |
| |
| double loss_rate = !observation.size.IsZero() |
| ? observation.lost_size / observation.size |
| : 0.0; |
| if (num_observations_ > 3) { |
| if (loss_rate > max_loss_rate) { |
| max_loss_rate = loss_rate; |
| max_lost_bytes = instant_temporal_weight * observation.lost_size; |
| max_bytes_received = instant_temporal_weight * observation.size; |
| send_rate_of_max_loss_observation = observation.sending_rate; |
| } |
| if (loss_rate < min_loss_rate) { |
| min_loss_rate = loss_rate; |
| min_lost_bytes = instant_temporal_weight * observation.lost_size; |
| min_bytes_received = instant_temporal_weight * observation.size; |
| } |
| } |
| } |
| if (GetMedianSendingRate() * config_->median_sending_rate_factor <= |
| send_rate_of_max_loss_observation) { |
| // If the median sending rate is less than half of the sending rate of the |
| // observation with max loss rate, i.e. we suddenly send a lot of data, then |
| // the loss rate might not be due to a spike. |
| return lost_bytes / total_bytes; |
| } |
| return (lost_bytes - min_lost_bytes - max_lost_bytes) / |
| (total_bytes - max_bytes_received - min_bytes_received); |
| } |
| |
| DataRate LossBasedBweV2::GetCandidateBandwidthUpperBound() const { |
| DataRate candidate_bandwidth_upper_bound = max_bitrate_; |
| if (IsInLossLimitedState() && IsValid(bandwidth_limit_in_current_window_)) { |
| candidate_bandwidth_upper_bound = bandwidth_limit_in_current_window_; |
| } |
| |
| if (!acknowledged_bitrate_.has_value()) |
| return candidate_bandwidth_upper_bound; |
| |
| if (config_->rampup_acceleration_max_factor > 0.0) { |
| const TimeDelta time_since_bandwidth_reduced = std::min( |
| config_->rampup_acceleration_maxout_time, |
| std::max(TimeDelta::Zero(), last_send_time_most_recent_observation_ - |
| last_time_estimate_reduced_)); |
| const double rampup_acceleration = config_->rampup_acceleration_max_factor * |
| time_since_bandwidth_reduced / |
| config_->rampup_acceleration_maxout_time; |
| |
| candidate_bandwidth_upper_bound += |
| rampup_acceleration * (*acknowledged_bitrate_); |
| } |
| return candidate_bandwidth_upper_bound; |
| } |
| |
| std::vector<LossBasedBweV2::ChannelParameters> LossBasedBweV2::GetCandidates( |
| bool in_alr) const { |
| ChannelParameters best_estimate = current_best_estimate_; |
| std::vector<DataRate> bandwidths; |
| for (double candidate_factor : config_->candidate_factors) { |
| bandwidths.push_back(candidate_factor * |
| best_estimate.loss_limited_bandwidth); |
| } |
| |
| if (acknowledged_bitrate_.has_value() && |
| config_->append_acknowledged_rate_candidate) { |
| if (!(config_->not_use_acked_rate_in_alr && in_alr) || |
| (config_->padding_duration > TimeDelta::Zero() && |
| last_padding_info_.padding_timestamp + config_->padding_duration >= |
| last_send_time_most_recent_observation_)) { |
| bandwidths.push_back(*acknowledged_bitrate_ * |
| config_->bandwidth_backoff_lower_bound_factor); |
| } |
| } |
| |
| if (IsValid(delay_based_estimate_) && |
| config_->append_delay_based_estimate_candidate) { |
| if (delay_based_estimate_ > best_estimate.loss_limited_bandwidth) { |
| bandwidths.push_back(delay_based_estimate_); |
| } |
| } |
| |
| if (in_alr && config_->append_upper_bound_candidate_in_alr && |
| best_estimate.loss_limited_bandwidth > GetInstantUpperBound()) { |
| bandwidths.push_back(GetInstantUpperBound()); |
| } |
| |
| const DataRate candidate_bandwidth_upper_bound = |
| GetCandidateBandwidthUpperBound(); |
| |
| std::vector<ChannelParameters> candidates; |
| candidates.resize(bandwidths.size()); |
| for (size_t i = 0; i < bandwidths.size(); ++i) { |
| ChannelParameters candidate = best_estimate; |
| candidate.loss_limited_bandwidth = |
| std::min(bandwidths[i], std::max(best_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]; |
| 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) { |
| 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::AdjustBiasFactor(double loss_rate, |
| double bias_factor) const { |
| return bias_factor * |
| (config_->loss_threshold_of_high_bandwidth_preference - loss_rate) / |
| (config_->bandwidth_preference_smoothing_factor + |
| std::abs(config_->loss_threshold_of_high_bandwidth_preference - |
| loss_rate)); |
| } |
| |
| double LossBasedBweV2::GetHighBandwidthBias(DataRate bandwidth) const { |
| if (IsValid(bandwidth)) { |
| return AdjustBiasFactor(average_reported_loss_ratio_, |
| config_->higher_bandwidth_bias_factor) * |
| bandwidth.kbps() + |
| AdjustBiasFactor(average_reported_loss_ratio_, |
| config_->higher_log_bandwidth_bias_factor) * |
| std::log(1.0 + bandwidth.kbps()); |
| } |
| return 0.0; |
| } |
| |
| double LossBasedBweV2::GetObjective( |
| const ChannelParameters& channel_parameters) const { |
| double objective = 0.0; |
| |
| const double high_bandwidth_bias = |
| GetHighBandwidthBias(channel_parameters.loss_limited_bandwidth); |
| |
| 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]; |
| 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; |
| } |
| |
| 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::GetInstantUpperBound() const { |
| return cached_instant_upper_bound_.value_or(max_bitrate_); |
| } |
| |
| void LossBasedBweV2::CalculateInstantUpperBound() { |
| DataRate instant_limit = max_bitrate_; |
| if (average_reported_loss_ratio_ > config_->instant_upper_bound_loss_offset) { |
| instant_limit = config_->instant_upper_bound_bandwidth_balance / |
| (average_reported_loss_ratio_ - |
| config_->instant_upper_bound_loss_offset); |
| } |
| |
| cached_instant_upper_bound_ = instant_limit; |
| } |
| |
| DataRate LossBasedBweV2::GetInstantLowerBound() const { |
| return cached_instant_lower_bound_.value_or(DataRate::Zero()); |
| } |
| |
| void LossBasedBweV2::CalculateInstantLowerBound() { |
| DataRate instance_lower_bound = DataRate::Zero(); |
| if (IsValid(acknowledged_bitrate_) && |
| config_->lower_bound_by_acked_rate_factor > 0.0) { |
| instance_lower_bound = config_->lower_bound_by_acked_rate_factor * |
| acknowledged_bitrate_.value(); |
| } |
| |
| if (IsValid(min_bitrate_)) { |
| instance_lower_bound = std::max(instance_lower_bound, min_bitrate_); |
| } |
| cached_instant_lower_bound_ = instance_lower_bound; |
| } |
| |
| void LossBasedBweV2::CalculateTemporalWeights() { |
| for (int i = 0; i < config_->observation_window_size; ++i) { |
| temporal_weights_[i] = std::pow(config_->temporal_weight_factor, i); |
| instant_upper_bound_temporal_weights_[i] = |
| std::pow(config_->instant_upper_bound_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<const PacketResult> packet_results) { |
| if (packet_results.empty()) { |
| return false; |
| } |
| |
| partial_observation_.num_packets += packet_results.size(); |
| Timestamp last_send_time = Timestamp::MinusInfinity(); |
| Timestamp first_send_time = Timestamp::PlusInfinity(); |
| for (const PacketResult& packet : packet_results) { |
| if (packet.IsReceived()) { |
| partial_observation_.lost_packets.erase( |
| packet.sent_packet.sequence_number); |
| } else { |
| partial_observation_.lost_packets.emplace( |
| packet.sent_packet.sequence_number, packet.sent_packet.size); |
| } |
| partial_observation_.size += packet.sent_packet.size; |
| last_send_time = std::max(last_send_time, packet.sent_packet.send_time); |
| first_send_time = std::min(first_send_time, packet.sent_packet.send_time); |
| } |
| |
| // This is the first packet report we have received. |
| if (!IsValid(last_send_time_most_recent_observation_)) { |
| last_send_time_most_recent_observation_ = first_send_time; |
| } |
| |
| const TimeDelta observation_duration = |
| last_send_time - last_send_time_most_recent_observation_; |
| // Too small to be meaningful. |
| // To consider: what if it is too long?, i.e. we did not receive any packets |
| // for a long time, then all the packets we received are too old. |
| if (observation_duration <= TimeDelta::Zero() || |
| 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_.lost_packets.size(); |
| observation.num_received_packets = |
| observation.num_packets - observation.num_lost_packets; |
| observation.sending_rate = |
| GetSendingRate(partial_observation_.size / observation_duration); |
| for (auto const& [key, packet_size] : partial_observation_.lost_packets) { |
| observation.lost_size += packet_size; |
| } |
| observation.size = partial_observation_.size; |
| observation.id = num_observations_++; |
| observations_[observation.id % config_->observation_window_size] = |
| observation; |
| |
| partial_observation_ = PartialObservation(); |
| UpdateAverageReportedLossRatio(); |
| CalculateInstantUpperBound(); |
| return true; |
| } |
| |
| bool LossBasedBweV2::IsInLossLimitedState() const { |
| return loss_based_result_.state != LossBasedState::kDelayBasedEstimate; |
| } |
| |
| bool LossBasedBweV2::CanKeepIncreasingState(DataRate estimate) const { |
| if (config_->padding_duration == TimeDelta::Zero() || |
| loss_based_result_.state != LossBasedState::kIncreaseUsingPadding) |
| return true; |
| |
| // Keep using the kIncreaseUsingPadding if either the state has been |
| // kIncreaseUsingPadding for less than kPaddingDuration or the estimate |
| // increases. |
| return last_padding_info_.padding_timestamp + config_->padding_duration >= |
| last_send_time_most_recent_observation_ || |
| last_padding_info_.padding_rate < estimate; |
| } |
| |
| bool LossBasedBweV2::PaceAtLossBasedEstimate() const { |
| return config_->pace_at_loss_based_estimate && |
| loss_based_result_.state != LossBasedState::kDelayBasedEstimate; |
| } |
| |
| DataRate LossBasedBweV2::GetMedianSendingRate() const { |
| std::vector<DataRate> sending_rates; |
| for (const Observation& observation : observations_) { |
| if (!observation.IsInitialized() || !IsValid(observation.sending_rate) || |
| observation.sending_rate.IsZero()) { |
| continue; |
| } |
| sending_rates.push_back(observation.sending_rate); |
| } |
| if (sending_rates.empty()) { |
| return DataRate::Zero(); |
| } |
| absl::c_sort(sending_rates); |
| if (sending_rates.size() % 2 == 0) { |
| return (sending_rates[sending_rates.size() / 2 - 1] + |
| sending_rates[sending_rates.size() / 2]) / |
| 2; |
| } |
| return sending_rates[sending_rates.size() / 2]; |
| } |
| |
| } // namespace webrtc |