diff --git a/modules/bitrate_controller/send_side_bandwidth_estimation.cc b/modules/bitrate_controller/send_side_bandwidth_estimation.cc index 4694aa622d..394c14747b 100644 --- a/modules/bitrate_controller/send_side_bandwidth_estimation.cc +++ b/modules/bitrate_controller/send_side_bandwidth_estimation.cc @@ -104,6 +104,48 @@ bool ReadBweLossExperimentParameters(float* low_loss_threshold, } } // namespace +LinkCapacityTracker::LinkCapacityTracker() + : tracking_rate("rate", TimeDelta::seconds(10)) { + ParseFieldTrial({&tracking_rate}, + field_trial::FindFullName("WebRTC-Bwe-LinkCapacity")); +} + +LinkCapacityTracker::~LinkCapacityTracker() {} + +void LinkCapacityTracker::OnOveruse(DataRate acknowledged_rate, + Timestamp at_time) { + capacity_estimate_bps_ = + std::min(capacity_estimate_bps_, acknowledged_rate.bps()); + last_link_capacity_update_ = at_time; +} + +void LinkCapacityTracker::OnStartingRate(DataRate start_rate) { + if (last_link_capacity_update_.IsInfinite()) + capacity_estimate_bps_ = start_rate.bps(); +} + +void LinkCapacityTracker::OnRateUpdate(DataRate acknowledged, + Timestamp at_time) { + if (acknowledged.bps() > capacity_estimate_bps_) { + TimeDelta delta = at_time - last_link_capacity_update_; + double alpha = delta.IsFinite() ? exp(-(delta / tracking_rate.Get())) : 0; + capacity_estimate_bps_ = alpha * capacity_estimate_bps_ + + (1 - alpha) * acknowledged.bps(); + } + last_link_capacity_update_ = at_time; +} + +void LinkCapacityTracker::OnRttBackoff(DataRate backoff_rate, + Timestamp at_time) { + capacity_estimate_bps_ = + std::min(capacity_estimate_bps_, backoff_rate.bps()); + last_link_capacity_update_ = at_time; +} + +DataRate LinkCapacityTracker::estimate() const { + return DataRate::bps(capacity_estimate_bps_); +} + RttBasedBackoff::RttBasedBackoff() : rtt_limit_("limit", TimeDelta::PlusInfinity()), drop_fraction_("fraction", 0.5), @@ -220,8 +262,10 @@ void SendSideBandwidthEstimation::SetBitrates( DataRate max_bitrate, Timestamp at_time) { SetMinMaxBitrate(min_bitrate, max_bitrate); - if (send_bitrate) + if (send_bitrate) { + link_capacity_.OnStartingRate(*send_bitrate); SetSendBitrate(*send_bitrate, at_time); + } } void SendSideBandwidthEstimation::SetSendBitrate(DataRate bitrate, @@ -261,6 +305,10 @@ void SendSideBandwidthEstimation::CurrentEstimate(int* bitrate, *rtt = last_round_trip_time_.ms(); } +DataRate SendSideBandwidthEstimation::GetEstimatedLinkCapacity() const { + return link_capacity_.estimate(); +} + void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time, DataRate bandwidth) { bwe_incoming_ = bandwidth; @@ -269,21 +317,31 @@ void SendSideBandwidthEstimation::UpdateReceiverEstimate(Timestamp at_time, void SendSideBandwidthEstimation::UpdateDelayBasedEstimate(Timestamp at_time, DataRate bitrate) { + if (acknowledged_rate_) { + if (bitrate < delay_based_bitrate_) { + link_capacity_.OnOveruse(*acknowledged_rate_, at_time); + } + } delay_based_bitrate_ = bitrate; CapBitrateToThresholds(at_time, current_bitrate_); } -void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( - const TransportPacketsFeedback& report, - absl::optional acked_bitrate) { - if (!loss_based_bandwidth_estimation_.Enabled()) - return; - if (acked_bitrate) { +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( - *acked_bitrate, report.feedback_time); + *acknowledged_rate, at_time); + } +} + +void SendSideBandwidthEstimation::IncomingPacketFeedbackVector( + const TransportPacketsFeedback& report) { + if (loss_based_bandwidth_estimation_.Enabled()) { + loss_based_bandwidth_estimation_.UpdateLossStatistics( + report.packet_feedbacks, report.feedback_time); } - loss_based_bandwidth_estimation_.UpdateLossStatistics(report.packet_feedbacks, - report.feedback_time); } void SendSideBandwidthEstimation::UpdateReceiverBlock(uint8_t fraction_loss, @@ -378,6 +436,7 @@ void SendSideBandwidthEstimation::UpdateEstimate(Timestamp at_time) { if (at_time - time_last_decrease_ >= rtt_backoff_.drop_interval_) { time_last_decrease_ = at_time; new_bitrate = current_bitrate_ * rtt_backoff_.drop_fraction_; + link_capacity_.OnRttBackoff(new_bitrate, at_time); } CapBitrateToThresholds(at_time, new_bitrate); return; @@ -580,5 +639,10 @@ void SendSideBandwidthEstimation::CapBitrateToThresholds(Timestamp at_time, last_rtc_event_log_ = at_time; } current_bitrate_ = bitrate; + + if (acknowledged_rate_) { + link_capacity_.OnRateUpdate(std::min(current_bitrate_, *acknowledged_rate_), + at_time); + } } } // namespace webrtc diff --git a/modules/bitrate_controller/send_side_bandwidth_estimation.h b/modules/bitrate_controller/send_side_bandwidth_estimation.h index ab43c5277c..b016faba2e 100644 --- a/modules/bitrate_controller/send_side_bandwidth_estimation.h +++ b/modules/bitrate_controller/send_side_bandwidth_estimation.h @@ -31,6 +31,22 @@ namespace webrtc { class RtcEventLog; +class LinkCapacityTracker { + public: + LinkCapacityTracker(); + ~LinkCapacityTracker(); + void OnOveruse(DataRate acknowledged_rate, Timestamp at_time); + void OnStartingRate(DataRate start_rate); + void OnRateUpdate(DataRate acknowledged, Timestamp at_time); + void OnRttBackoff(DataRate backoff_rate, Timestamp at_time); + DataRate estimate() const; + + private: + FieldTrialParameter tracking_rate; + double capacity_estimate_bps_ = 0; + Timestamp last_link_capacity_update_ = Timestamp::MinusInfinity(); +}; + class RttBasedBackoff { public: RttBasedBackoff(); @@ -57,7 +73,7 @@ class SendSideBandwidthEstimation { void OnRouteChange(); void CurrentEstimate(int* bitrate, uint8_t* loss, int64_t* rtt) const; - + DataRate GetEstimatedLinkCapacity() const; // Call periodically to update estimate. void UpdateEstimate(Timestamp at_time); void OnSentPacket(SentPacket sent_packet); @@ -90,8 +106,9 @@ class SendSideBandwidthEstimation { void SetSendBitrate(DataRate bitrate, Timestamp at_time); void SetMinMaxBitrate(DataRate min_bitrate, DataRate max_bitrate); int GetMinBitrate() const; - void IncomingPacketFeedbackVector(const TransportPacketsFeedback& report, - absl::optional acked_bitrate); + void SetAcknowledgedRate(absl::optional acknowledged_rate, + Timestamp at_time); + void IncomingPacketFeedbackVector(const TransportPacketsFeedback& report); private: enum UmaState { kNoUpdate, kFirstDone, kDone }; @@ -112,6 +129,7 @@ class SendSideBandwidthEstimation { void CapBitrateToThresholds(Timestamp at_time, DataRate bitrate); RttBasedBackoff rtt_backoff_; + LinkCapacityTracker link_capacity_; std::deque > min_bitrate_history_; @@ -119,6 +137,7 @@ class SendSideBandwidthEstimation { int lost_packets_since_last_loss_update_; int expected_packets_since_last_loss_update_; + absl::optional acknowledged_rate_; DataRate current_bitrate_; DataRate min_bitrate_configured_; DataRate max_bitrate_configured_; diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc index 93fb9e1b44..4376953b7c 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.cc @@ -134,6 +134,8 @@ GoogCcNetworkController::GoogCcNetworkController(RtcEventLog* event_log, packet_feedback_only_(feedback_only), safe_reset_on_route_change_("Enabled"), safe_reset_acknowledged_rate_("ack"), + use_stable_bandwidth_estimate_( + field_trial::IsEnabled("WebRTC-Bwe-StableBandwidthEstimate")), probe_controller_(new ProbeController()), congestion_window_pushback_controller_( MaybeInitalizeCongestionWindowPushbackController()), @@ -145,7 +147,7 @@ GoogCcNetworkController::GoogCcNetworkController(RtcEventLog* event_log, acknowledged_bitrate_estimator_( absl::make_unique()), initial_config_(config), - last_bandwidth_(*config.constraints.starting_rate), + last_target_rate_(*config.constraints.starting_rate), pacing_factor_(config.stream_based_config.pacing_factor.value_or( kDefaultPaceMultiplier)), min_pacing_rate_(config.stream_based_config.min_pacing_rate.value_or( @@ -484,8 +486,9 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( acknowledged_bitrate_estimator_->IncomingPacketFeedbackVector( received_feedback_vector); auto acknowledged_bitrate = acknowledged_bitrate_estimator_->bitrate(); - bandwidth_estimation_->IncomingPacketFeedbackVector(report, - acknowledged_bitrate); + bandwidth_estimation_->SetAcknowledgedRate(acknowledged_bitrate, + report.feedback_time); + bandwidth_estimation_->IncomingPacketFeedbackVector(report); for (const auto& feedback : received_feedback_vector) { if (feedback.pacing_info.probe_cluster_id != PacedPacketInfo::kNotAProbe) { probe_bitrate_estimator_->HandleProbeAndEstimateBitrate(feedback); @@ -528,7 +531,7 @@ NetworkControlUpdate GoogCcNetworkController::OnTransportPacketsFeedback( const DataSize kMinCwnd = DataSize::bytes(2 * 1500); TimeDelta time_window = TimeDelta::ms(min_feedback_max_rtt_ms + accepted_queue_ms_); - DataSize data_window = last_bandwidth_ * time_window; + DataSize data_window = last_target_rate_ * time_window; if (current_data_window_) { data_window = std::max(kMinCwnd, (data_window + current_data_window_.value()) / 2); @@ -595,14 +598,16 @@ void GoogCcNetworkController::MaybeTriggerOnNetworkChanged( alr_detector_->SetEstimatedBitrate(estimated_bitrate_bps); - DataRate bandwidth = DataRate::bps(estimated_bitrate_bps); - last_bandwidth_ = bandwidth; + last_target_rate_ = DataRate::bps(estimated_bitrate_bps); + DataRate bandwidth = use_stable_bandwidth_estimate_ + ? bandwidth_estimation_->GetEstimatedLinkCapacity() + : last_target_rate_; TimeDelta bwe_period = delay_based_bwe_->GetExpectedBwePeriod(); // Set the target rate to the full estimated bandwidth since the estimation // for legacy reasons includes target rate constraints. - DataRate target_rate = bandwidth; + DataRate target_rate = last_target_rate_; if (congestion_window_pushback_controller_) { int64_t pushback_rate = congestion_window_pushback_controller_->UpdateTargetBitrate( @@ -623,8 +628,8 @@ void GoogCcNetworkController::MaybeTriggerOnNetworkChanged( update->target_rate = target_rate_msg; - auto probes = - probe_controller_->SetEstimatedBitrate(bandwidth.bps(), at_time.ms()); + auto probes = probe_controller_->SetEstimatedBitrate( + last_target_rate_.bps(), at_time.ms()); update->probe_cluster_configs.insert(update->probe_cluster_configs.end(), probes.begin(), probes.end()); update->pacer_config = GetPacingRates(at_time); @@ -633,8 +638,8 @@ void GoogCcNetworkController::MaybeTriggerOnNetworkChanged( PacerConfig GoogCcNetworkController::GetPacingRates(Timestamp at_time) const { DataRate pacing_rate = - std::max(min_pacing_rate_, last_bandwidth_) * pacing_factor_; - DataRate padding_rate = std::min(max_padding_rate_, last_bandwidth_); + std::max(min_pacing_rate_, last_target_rate_) * pacing_factor_; + DataRate padding_rate = std::min(max_padding_rate_, last_target_rate_); PacerConfig msg; msg.at_time = at_time; msg.time_window = TimeDelta::seconds(1); diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control.h b/modules/congestion_controller/goog_cc/goog_cc_network_control.h index 382f027d5f..a62f59bb44 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control.h +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control.h @@ -66,6 +66,7 @@ class GoogCcNetworkController : public NetworkControllerInterface { const bool packet_feedback_only_; FieldTrialFlag safe_reset_on_route_change_; FieldTrialFlag safe_reset_acknowledged_rate_; + const bool use_stable_bandwidth_estimate_; const std::unique_ptr probe_controller_; const std::unique_ptr @@ -87,8 +88,7 @@ class GoogCcNetworkController : public NetworkControllerInterface { std::deque feedback_max_rtts_; - DataRate last_bandwidth_; - absl::optional last_target_rate_; + DataRate last_target_rate_; int32_t last_estimated_bitrate_bps_ = 0; uint8_t last_estimated_fraction_loss_ = 0; diff --git a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc index 55f22b640d..fecd1b32d4 100644 --- a/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc +++ b/modules/congestion_controller/goog_cc/goog_cc_network_control_unittest.cc @@ -399,5 +399,61 @@ TEST_F(GoogCcNetworkControllerTest, UpdatesTargetRateBasedOnLinkCapacity) { EXPECT_NEAR(client->target_rate_kbps(), 90, 20); } +TEST_F(GoogCcNetworkControllerTest, DefaultEstimateVariesInSteadyState) { + ScopedFieldTrials trial("WebRTC-Bwe-StableBandwidthEstimate/Disabled/"); + Scenario s("googcc_unit/no_stable_varies", false); + SimulatedTimeClientConfig config; + config.transport.cc = + TransportControllerConfig::CongestionController::kGoogCcFeedback; + NetworkNodeConfig net_conf; + net_conf.simulation.bandwidth = DataRate::kbps(500); + net_conf.simulation.delay = TimeDelta::ms(100); + net_conf.update_frequency = TimeDelta::ms(5); + auto send_net = s.CreateSimulationNode(net_conf); + auto ret_net = s.CreateSimulationNode(net_conf); + SimulatedTimeClient* client = s.CreateSimulatedTimeClient( + "send", config, {PacketStreamConfig()}, {send_net}, {ret_net}); + // Run for a while to allow the estimate to stabilize. + s.RunFor(TimeDelta::seconds(20)); + DataRate min_estimate = DataRate::PlusInfinity(); + DataRate max_estimate = DataRate::MinusInfinity(); + // Measure variation in steady state. + for (int i = 0; i < 20; ++i) { + min_estimate = std::min(min_estimate, client->link_capacity()); + max_estimate = std::max(max_estimate, client->link_capacity()); + s.RunFor(TimeDelta::seconds(1)); + } + // We should expect drops by at least 15% (default backoff.) + EXPECT_LT(min_estimate / max_estimate, 0.85); +} + +TEST_F(GoogCcNetworkControllerTest, StableEstimateDoesNotVaryInSteadyState) { + ScopedFieldTrials trial("WebRTC-Bwe-StableBandwidthEstimate/Enabled/"); + Scenario s("googcc_unit/stable_is_stable", false); + SimulatedTimeClientConfig config; + config.transport.cc = + TransportControllerConfig::CongestionController::kGoogCcFeedback; + NetworkNodeConfig net_conf; + net_conf.simulation.bandwidth = DataRate::kbps(500); + net_conf.simulation.delay = TimeDelta::ms(100); + net_conf.update_frequency = TimeDelta::ms(5); + auto send_net = s.CreateSimulationNode(net_conf); + auto ret_net = s.CreateSimulationNode(net_conf); + SimulatedTimeClient* client = s.CreateSimulatedTimeClient( + "send", config, {PacketStreamConfig()}, {send_net}, {ret_net}); + // Run for a while to allow the estimate to stabilize. + s.RunFor(TimeDelta::seconds(20)); + DataRate min_estimate = DataRate::PlusInfinity(); + DataRate max_estimate = DataRate::MinusInfinity(); + // Measure variation in steady state. + for (int i = 0; i < 20; ++i) { + min_estimate = std::min(min_estimate, client->link_capacity()); + max_estimate = std::max(max_estimate, client->link_capacity()); + s.RunFor(TimeDelta::seconds(1)); + } + // We expect no variation under the trial in steady state. + EXPECT_GT(min_estimate / max_estimate, 0.95); +} + } // namespace test } // namespace webrtc diff --git a/test/scenario/simulated_time.cc b/test/scenario/simulated_time.cc index 954c54ce88..a5977e7d96 100644 --- a/test/scenario/simulated_time.cc +++ b/test/scenario/simulated_time.cc @@ -308,6 +308,7 @@ void SimulatedTimeClient::Update(NetworkControlUpdate update) { DataRate rate_per_stream = update.target_rate->target_rate * ratio_per_stream; target_rate_ = update.target_rate->target_rate; + link_capacity_ = update.target_rate->network_estimate.bandwidth; for (auto& stream : packet_streams_) stream->OnTargetRateUpdate(rate_per_stream); } @@ -349,6 +350,10 @@ TimeDelta SimulatedTimeClient::GetNetworkControllerProcessInterval() const { return network_controller_factory_.GetProcessInterval(); } +DataRate SimulatedTimeClient::link_capacity() const { + return link_capacity_; +} + double SimulatedTimeClient::target_rate_kbps() const { return target_rate_.kbps(); } diff --git a/test/scenario/simulated_time.h b/test/scenario/simulated_time.h index 42f176e18b..79fdf84573 100644 --- a/test/scenario/simulated_time.h +++ b/test/scenario/simulated_time.h @@ -132,6 +132,7 @@ class SimulatedTimeClient : NetworkReceiverInterface { void TriggerFakeReroute(Timestamp at_time); TimeDelta GetNetworkControllerProcessInterval() const; double target_rate_kbps() const; + DataRate link_capacity() const; bool TryDeliverPacket(rtc::CopyOnWriteBuffer packet, uint64_t receiver, @@ -147,6 +148,7 @@ class SimulatedTimeClient : NetworkReceiverInterface { SimulatedFeedback feedback_; TargetRateConstraints current_contraints_; DataRate target_rate_ = DataRate::Infinity(); + DataRate link_capacity_ = DataRate::Infinity(); FILE* packet_log_ = nullptr; std::vector> packet_streams_;