From 57f3ad0f8d41abd85e2942a86ca039f54e4f7b80 Mon Sep 17 00:00:00 2001 From: Sebastian Jansson Date: Fri, 23 Nov 2018 14:49:18 +0100 Subject: [PATCH] Adds stable bandwidth estimate to GoogCC. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intention is to provide a bandwidth estimate that only updates if the actual available bandwidth is known to have changed. This will be used in media streams to avoid changing the configuration (such as frame size, audio frame length etc), just because the control target rate changed. Bug: webrtc:9718 Change-Id: I17ba5a2f9e5bd408a71f89c690d45541655a68e2 Reviewed-on: https://webrtc-review.googlesource.com/c/107726 Commit-Queue: Sebastian Jansson Reviewed-by: Björn Terelius Cr-Commit-Position: refs/heads/master@{#25772} --- .../send_side_bandwidth_estimation.cc | 84 ++++++++++++++++--- .../send_side_bandwidth_estimation.h | 25 +++++- .../goog_cc/goog_cc_network_control.cc | 27 +++--- .../goog_cc/goog_cc_network_control.h | 4 +- .../goog_cc_network_control_unittest.cc | 56 +++++++++++++ test/scenario/simulated_time.cc | 5 ++ test/scenario/simulated_time.h | 2 + 7 files changed, 177 insertions(+), 26 deletions(-) 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_;