blob: 964b9b465d7c54bc2b96f11979e72361c358212e [file]
/*
* 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 <stdint.h>
#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_