/* * Copyright 2026 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_SCREAM_LOSS_ESTIMATOR_H_ #define MODULES_CONGESTION_CONTROLLER_SCREAM_LOSS_ESTIMATOR_H_ #include #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "modules/congestion_controller/scream/scream_feedback.h" #include "modules/congestion_controller/scream/scream_v2_parameters.h" namespace webrtc { // Estimates the short-term congestion level of the connection using a biased // asymmetric step filter (counting a weighted sum of RTTs with loss minus // RTTs without loss). // // The congestion level is updated on RTT boundaries. If one or more packets are // reported lost during an RTT interval, the filter treats the interval as // a loss event. // // To handle packet reordering robustly, the estimator maintains a count of // outstanding unrecovered lost packets. When a packet is reported lost for the // first time, the outstanding count increments. If a subsequently received // feedback report indicates that an out-of-order packet has been recovered, // the count decrements. If the unrecovered count reaches zero, the connection // is considered loss-free, and the congestion level is reset to zero. // // Asymmetric Step Filter Motivation: // A simple sliding window or traditional EWMA is highly sensitive to spurious // uniform losses (e.g. 1% uniform random loss in wireless links). Because we // transmit many packets per RTT (e.g. 50), a 1% packet loss scales up to a // ~40% RTT loss event probability. // // This filter solves this by using an asymmetric step filter (leaky bucket) // scaled between [0.0, 1.0] (with step-up of +1/3 on loss, step-down of -0.5 on // no loss). This guarantees that the expected drift per RTT under 1% uniform // loss is strongly negative (-0.5), keeping the congestion level at ~0.0. // Meanwhile, it takes exactly 3 consecutive loss RTTs to climb to 1.0 to react. class LossEstimator { public: explicit LossEstimator(const ScreamV2Parameters& params); ~LossEstimator() = default; // Updates the congestion level estimate. Returns true if a loss has been // detected in this feedback message, false otherwise. bool Update(const ScreamFeedback& parsed, TimeDelta rtt); // Returns the congestion level, which is a value between 0.0 (loss-free) // and 1.0 (congested). double congestion_level() const { return congestion_level_; } // Returns true if the short-term congestion level has reached the threshold. bool congested() const { return congestion_level_ >= 0.99; } private: const TimeDelta virtual_rtt_; const int rtts_with_loss_before_backoff_; const int lossless_rtts_before_clear_; // `loss_event_this_rtt_` tracks whether any packet loss was detected // specifically within the current RTT interval. Consumed and reset on RTT // boundaries. Needed separately from `unrecovered_lost_packets_` because // unrecovered counts persist across RTT updates to track delayed reordered // recoveries. bool loss_event_this_rtt_ = false; double congestion_level_ = 0.0; int unrecovered_lost_packets_ = 0; Timestamp last_loss_or_recovery_time_ = Timestamp::MinusInfinity(); Timestamp last_rtt_update_time_ = Timestamp::MinusInfinity(); }; } // namespace webrtc #endif // MODULES_CONGESTION_CONTROLLER_SCREAM_LOSS_ESTIMATOR_H_