blob: 786233ab902dee0a865114f299f686f22381158f [file] [view]
<!-- go/cmark -->
<!--* freshness: {owner: 'perkj' reviewed: '2026-10-13'} *-->
# SCReAMv2 Implementation Differences
**Date:** October 13, 2026\
**Commit:**
[65217155f735a780676d34c073ef2447589a71c1](https://webrtc.googlesource.com/src/+/65217155f735a780676d34c073ef2447589a71c1)\
**Note:** This document was automatically generated by an AI assistant (Gemini)
comparing the WebRTC source code with the IETF specification.
This document describes the differences between the WebRTC implementation of
SCReAMv2 in `/modules/congestion_controller/scream` (see
[index.md](/modules/congestion_controller/scream/g3doc/index.md)) and the IETF
Working Group draft
[draft-ietf-ccwg-rfc8298bis-screamv2-01](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html)
([datatracker](https://datatracker.ietf.org/doc/draft-ietf-ccwg-rfc8298bis-screamv2/)).
The WebRTC implementation is currently in development and some features are not
yet fully implemented.
[TOC]
## 4. Detailed Description of SCReAMv2 Sender Algorithm
### 4.1. Sender Side State
#### 4.1.1. Status Update When Sending Data
- **Differences in Default Parameters:**
- `min_ref_window` (`MIN_REF_WND` in spec): The spec recommends **3000 bytes**
([Section 4.2.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2)).
The WebRTC implementation defaults to **1000 bytes** in
[scream_v2_parameters.cc:L26](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2_parameters.cc#26).
- `max_segment_size` (`MSS` in spec): The spec recommends **1000 bytes**
([Section 4.2.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2)).
The WebRTC implementation defaults to **1280 bytes** in
[scream_v2_parameters.cc:L34](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2_parameters.cc#34).
- `bytes_in_flight_head_room` (`BYTES_IN_FLIGHT_HEAD_ROOM` in spec): The spec
recommends **1.5**
([Section 4.2.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2)).
The WebRTC implementation defaults to **1.1** in
[scream_v2_parameters.cc:L35](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2_parameters.cc#35).
- **Initial Reference Window Initialization:**
- The spec initializes `ref_wnd` to `MIN_REF_WND`
([Section 4.2.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2)).
- The WebRTC implementation initializes `ref_window_` to `min_ref_window` at
construction, and upon receiving the **first feedback** with valid delay
metrics, resets `ref_window_` to
`max(min_ref_window, target_rate_ * (rtt + feedback_hold_time_))` using the
configured starting bitrate (default 300 kbps). See
[scream_v2.cc:L83-L94](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#83).
#### 4.1.2. Status Update on Receiving Feedback
- The WebRTC implementation normalizes `TransportPacketsFeedback` into a flat
`ScreamFeedback` struct in
[scream_feedback.h](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_feedback.h)
and
[scream_feedback.cc](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_feedback.cc).
- **`acked_not_marked_size` vs. `bytes_newly_acked`:**
- Instead of accumulating `bytes_newly_acked` and `bytes_newly_acked_ce`
across feedback messages and resetting them on window updates as described
in the spec
([Section 4.1.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.1.2)),
`ParseScreamFeedback` directly computes `acked_not_marked_size` per feedback
(the sum of sizes of all packets in the feedback that are not ECN-CE marked,
including lost packets) and `num_ce_marked_packets`. See
[scream_feedback.cc:L44-L46](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_feedback.cc#44).
- **Burst-Windowed One-Way Delay Filtering:**
- Instead of computing queue delay over every packet in a feedback report,
`ParseScreamFeedback` computes `min_one_way_delay` and `max_one_way_delay`
only over packets with unambiguous receive times sent within a recent
tail/burst window from the latest received packet: a base window of
`burst_window_min` (default 25 ms), extended up to `burst_window_max`
(default 50 ms) as long as consecutive packets are sent within
`burst_window_max_gap` (default 3 ms). This prevents earlier sparse packets
in long feedback intervals (e.g., audio-only or ALR) from skewing delay and
jitter estimates. See
[scream_feedback.cc:L86-L118](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_feedback.cc#86).
- **Feedback Hold Time and Feedback Interval Tracking:**
- **Feedback Hold Time (`feedback_hold_time_`):** Tracks an EWMA (gain
`feedback_hold_time_avg_g = 1/8`) of the duration between the receive times
of the first and last packets in a feedback report plus the last packet's
receiver `arrival_time_offset`. This hold time is added to `rtt` when
initializing `ref_window_`, clamping `ref_window_` on initial congestion,
scaling window increase for small RTTs, and computing `target_rate_`. See
[scream_feedback.cc:L124-L127](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_feedback.cc#124)
and
[scream_v2.cc:L396-L404](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#396).
- **Feedback Interval (`feedback_interval_`):** Tracks an EWMA (gain
`feedback_interval_avg_g = 0.1`) of the time between consecutive feedback
messages. When `use_feedback_interval_for_virtual_rtt` is enabled (default
`true`), `ReactionInterval()` is defined as
`max(virtual_rtt, feedback_interval_)` instead of fixed `VIRTUAL_RTT`. See
[scream_v2.cc:L115-L119](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#115)
and
[scream_v2.cc:L406-L419](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#406).
### 4.2. Network Congestion Control
#### 4.2.1. Congestion Detection: Delay, Data Unit Loss and ECN-CE
##### 4.2.1.1. Detecting Lost Data Units
- **Biased Asymmetric Step Filter (`LossEstimator`):**
- The spec describes RACK-like loss detection with a reordering window in
[Section 4.2.1.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.1.1)
and an optional per-packet EWMA `loss_rate` filter in
[Section 4.5.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.5.2).
- WebRTC relies on transport-level feedback flags
(`reported_lost_for_the_first_time` and
`reported_recovered_for_the_first_time`) and passes them to
[LossEstimator](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/loss_estimator.h)
([loss_estimator.cc](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/loss_estimator.cc)),
which implements a **biased asymmetric step filter** (leaky bucket) tracking
`congestion_level_` in `[0.0, 1.0]` at RTT boundaries
(`max(virtual_rtt, rtt)`):
- **Reordering Recovery:** Tracks `unrecovered_lost_packets_`, incrementing
on first-time loss reports and decrementing on subsequent out-of-order
recovery reports (and resetting after a full RTT without losses or
recoveries). If `unrecovered_lost_packets_` drops to `0`,
`congestion_level_` and `loss_event_this_rtt_` are immediately reset to
`0`.
- **Step Filter Mechanics:** Steps up by
`1.0 / rtts_with_loss_before_backoff` (default `+1/3`) on an RTT with
loss, and steps down by `1.0 / lossless_rtts_before_clear` (default
`-1/2`) on a lossless RTT. This produces a net-negative drift under low
uniform random wireless loss (e.g., 1% packet loss) while reaching
`congested()` (`>= 0.99`) after 3 consecutive RTTs with loss.
- **Impact on Backoff:** Loss triggers a reference window backoff only if
`loss_estimator_.congested()` (`congestion_level_ >= 0.99`) **or** queue
delay congestion (`IsQueueDelayDetected()`, i.e.
`queue_delay_avg_ > queue_delay_target / 2`) is simultaneously active. See
[scream_v2.cc:L203-L210](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#203).
- **Impact on Window Increase:** Reference window growth is blocked whenever
`loss_congestion_level() >= 0.01` (unless simultaneously backing off in the
same feedback). Because `congestion_level_` decays by `0.5` per lossless
RTT, it takes **2 full lossless RTTs** after peak loss congestion for window
growth to resume. See
[scream_v2.cc:L284](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#284).
##### 4.2.1.2. Receiving ECN-CE with classic ECN
- **Classic ECN Not Supported:**
- The spec defines a classic ECN mode (`IS_L4S == false`) where `ref_wnd` is
scaled by a fixed `BETA_ECN` (default 0.8)
([Section 4.2.1.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.1.2)).
- The WebRTC implementation **does not support classic ECN** and always
operates in L4S mode when ECN-CE markings are received.
##### 4.2.1.3. Receiving ECN-CE for L4S
- **`l4s_alpha` Update Frequency and RTT Gain Scaling:**
- Draft `-01` accumulates delivered and marked data units and updates
`l4s_alpha` at most once per `min(10 ms, s_rtt)` using an asymmetric
fast-attack / slow-decay EWMA (`L4S_AVG_G_UP = 1/8`,
`L4S_AVG_G_DOWN = 1/128`)
([Section 4.2.1.3](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.1.3)),
while the latest editor's draft (`draft-ietf-ccwg-rfc8298bis-screamv2-02`)
uses a symmetric EWMA with `L4S_AVG_G = 1/16`.
- The WebRTC implementation updates `l4s_alpha_` on **every feedback message**
with received packets using a standard EWMA filter with `l4s_avg_g = 1/16`
([scream_v2_parameters.cc:L27](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2_parameters.cc#27)).
- When `scale_l4s_alpha_with_rtt` is enabled (default `true`), WebRTC divides
`l4s_avg_g` by `steps_per_rtt = max(1.0, rtt / ReactionInterval())` so that
`l4s_alpha_` converges at a consistent rate per RTT regardless of how many
feedback reports arrive per RTT. See
[scream_v2.cc:L178-L194](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#178).
##### 4.2.1.4. Detecting Increased Queue Delay
- **Queue Delay Average (`queue_delay_avg_`) Filtering and Update Frequency:**
- The spec updates `qdelay_avg` at most once per `min(virtual_rtt, s_rtt)`
directly from the latest `qdelay` sample
([Section 4.2.1.4](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.1.4)).
- WebRTC updates `queue_delay_avg_` on **every feedback message**, passing
`min(min_queue_delay, last_queue_delay_sample_)` (the minimum of the current
and previous feedback's minimum queue delay) into the slow-attack
(`queue_delay_avg_g = 1/4`), instantaneous-decay EWMA filter. Taking the
minimum of two consecutive samples filters out single-feedback delay spikes.
See
[delay_based_congestion_control.cc:L50-L75](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/delay_based_congestion_control.cc#50).
- **Jitter and Standing-Queue Scaling Factors (`ref_wnd_delay_scale`):**
- In draft `-01`, the spec defines an optional `REDUCE_JITTER` mechanism that
tracks `qdelay_max_avg` and `qdelay_min_avg`, computes an EWMA of their
difference `qdelay_dev_avg` (`QDELAY_DEV_AVG_G = 1/32`), and derives a
single scaling factor
`ref_wnd_delay_scale = clamp(1.0 - qdelay_dev_avg / QDELAY_DEV_THRESHOLD, 0.0, 1.0)`
with `QDELAY_DEV_THRESHOLD = 10 ms`
([Section 4.2.1.4](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.1.4)).
- The WebRTC implementation (always enabled) instead computes **two separate
scaling factors** in
[DelayBasedCongestionControl](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/delay_based_congestion_control.h)
([delay_based_congestion_control.cc](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/delay_based_congestion_control.cc)),
both using an EWMA gain of `delay_min_and_latency_diff_avg_g = 1/32` (input
capped at `2 * threshold`) and a linear mapping
`clamp(0.1 + 1.2 * (norm - avg) / norm, min_val, 1.0)` that is `1.0` at or
below `norm / 4` (2.5 ms) and `0.1` at `norm` (10 ms):
1. `ref_window_scale_factor_due_to_avg_min_delay` (threshold
`queue_delay_min_threshold = 10 ms`): Based on `queue_delay_min_avg_`
(EWMA of `min_queue_delay` per feedback). Used to scale down L4S backoff,
scale down reference window increase, and (with `allow_zero = true`,
reaching `0.0` at `13/12 * norm`) scale `ref_window_overhead` in
`max_data_in_flight()`. See
[delay_based_congestion_control.cc:L125-L131](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/delay_based_congestion_control.cc#125).
2. `ref_window_scale_factor_due_to_latency_difference` (threshold
`latency_diff_threshold = 10 ms`): Based on `latency_difference_avg_`
(EWMA of `max_one_way_delay - min_one_way_delay` within the feedback's
burst window). Used to scale down reference window increase when
intra-burst delay variation indicates the send rate is close to
bottleneck capacity. See
[delay_based_congestion_control.cc:L134-L140](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/delay_based_congestion_control.cc#134).
#### 4.2.2. Reference Window Update
##### 4.2.2.1. Reference Window Reduction
- **Congestion Reaction Interval:**
- The spec gates congestion reactions by
`now - last_reaction_to_congestion_time >= min(VIRTUAL_RTT, s_rtt)`
([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.1)).
- WebRTC gates reactions by `min(rtt, ReactionInterval())`, where
`ReactionInterval()` is `max(virtual_rtt, feedback_interval_)` when
`use_feedback_interval_for_virtual_rtt` is true. See
[scream_v2.cc:L218-L220](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#218).
- **Non-Linear Backoff Scaling (`ScaleBackoff`) & In-Flight Burst
Compensation:**
- The spec scales `backoff_t` linearly by dividing by
`max(1.0, s_rtt / VIRTUAL_RTT)` on every reaction interval during an RTT
([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.1)).
- When `use_non_linear_backoff_scale` is enabled (default `true`), WebRTC uses
a stateful 4-state backoff scaler in `ScreamV2::ScaleBackoff()`
([scream_v2.cc:L121-L176](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#121)):
- **Immediate Full Reduction:** Applies the full `backoff` on the first
congestion event of a cycle, rather than spreading it across multiple
feedbacks over the RTT.
- **Wait for In-Flight Data to Drain:** Keeps resetting the 1-RTT
observation timer while `data_in_flight > ref_window_` (for up to one RTT
after the last window decrease), since bottleneck queues cannot drain
until actual in-flight bytes drop to `ref_window_`.
- **Incremental Worsening Only:** During the 1-RTT observation window, if
`backoff` increases above `last_backoff_`, only the incremental delta
`(backoff - last_backoff_) / (1.0 - last_backoff_)` is applied; otherwise
`0.0` is returned.
- **Excess In-Flight Scaling (`ref_window_share`):** If
`data_in_flight > ref_window_ * bytes_in_flight_head_room`, the backoff is
scaled by `ref_window_ / data_in_flight` so transient bursts (e.g.,
keyframes) do not cause an excessive reduction of `ref_window_`.
- **Initial Reference Window Clamping on First Congestion:**
- When `allow_initial_ref_window_clamping` is enabled (default `true`), on the
very first congestion reaction (if `received_rate_` is finite), WebRTC
clamps `ref_window_` down to
`max(min_ref_window, received_rate_ * (rtt + feedback_hold_time_))` if that
produces a larger reduction than `backoff`. This quickly corrects an overly
high initial starting bitrate. See
[scream_v2.cc:L265-L278](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#265).
- **Inflection Point (`ref_window_i_`) Scaling Factor:**
- The spec uses a factor of **8** in
`scl_t = ((ref_wnd - ref_wnd_i) / ref_wnd_i) * 8`
([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.1)).
- WebRTC defaults to a lower factor of **2.0**
(`backoff_scale_factor_close_to_ref_window_i` in
[scream_v2_parameters.cc:L43-L45](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2_parameters.cc#43)),
resulting in slower, more cautious window changes near `ref_window_i_`.
- **L4S Backoff Attenuation Condition and Floor:**
- The spec attenuates L4S backoff by `max(0.25, scl_t)` and
`max(0.25, ref_wnd_delay_scale)` when `qdelay < qdelay_target * 0.25`
([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.1)).
- WebRTC applies this attenuation when
`!delay_based_congestion_control_.IsQueueDelayDetected()`, which corresponds
to `queue_delay_avg_ <= queue_delay_target / 2` (default 30 ms), and uses a
lower floor of `0.1` (instead of `0.25`) for
`ref_window_scale_factor_due_to_avg_min_delay()`. See
[scream_v2.cc:L228-L240](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#228).
- **Inflection Point (`ref_window_i_`) Update Timing:**
- In the spec, `ref_wnd_i` is updated to `ref_wnd` *before* the reduction
whenever `is_loss_t || is_ce_t || is_virtual_ce_t` is true and
`ref_wnd_i_update_allowed` is true, and `ref_wnd_i_update_allowed` is
re-enabled when `ref_wnd` increases during the increase step
([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.1)).
- In WebRTC, because window reduction and window increase can both execute on
the same feedback message, `ref_window_i_` and `allow_ref_window_i_update_`
are updated at the end of `UpdateRefWindow()` based on the **net change** in
`ref_window_` compared to `previous_ref_window` (recording
`previous_ref_window` only on a net decrease, and re-arming
`allow_ref_window_i_update_` only on a net increase). See
[scream_v2.cc:L340-L352](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#340).
##### 4.2.2.2. Reference Window Increase
- **When Reference Window Increase Runs:**
- In the spec, the reference window increase step runs unconditionally on
feedback (even when congestion occurs, to maintain the L4S 2-marks/RTT
equilibrium)
([Section 4.2.2.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.2)).
- In WebRTC, the increase runs only when either no congestion is active
(`!is_ce && loss_congestion_level() < 0.01 && !is_virtual_ce`) **or** a
congestion backoff reaction occurred on the current feedback
(`last_reaction_to_congestion_time_ == parsed.feedback_time`). If congestion
is present on a feedback that is within `ReactionInterval()` of the previous
reaction, window growth is skipped. See
[scream_v2.cc:L284-L285](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#284).
- **RTT Scaling for Small RTTs Includes `feedback_hold_time_`:**
- The spec scales down window increase for small RTTs using
`min(1.0, s_rtt / VIRTUAL_RTT)`
([Section 4.2.2.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.2)).
- WebRTC includes `feedback_hold_time_` in the numerator when scaling for
small RTTs: `(rtt + feedback_hold_time_) / virtual_rtt`. See
[scream_v2.cc:L294-L300](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#294).
- **Application-Limited (ALR) State Tracking:**
- The spec restricts `ref_wnd` growth using
`max_allowed_t = MSS + max(max_bytes_in_flight, max_bytes_in_flight_prev) * BYTES_IN_FLIGHT_HEAD_ROOM`
and `target_bitrate < TARGET_BITRATE_MAX`
([Section 4.2.2.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.2.2.2)),
and notes in
[Section 6](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-6)
that RTT should be updated with a longer time constant when bytes in flight
is low.
- WebRTC implements both `max_allowed_ref_window()` clamping
([scream_v2.cc:L337-L341](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#337))
and an explicit `is_application_limited_` (ALR) state (enabled via
`enable_alr`, default `true`), entered when
`max_allowed_ref_window() < ref_window_` and
`received_rate_ < alr_threshold * target_rate_` (with `alr_threshold = 0.9`,
where `received_rate_` is measured over `received_rate_window = 100 ms`).
See
[scream_v2.cc:L69-L73](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#69).
- When `is_application_limited_` is true:
1. The smoothed RTT EWMA gain is reduced from `smoothed_rtt_avg_g` (`1/8`)
to `smoothed_rtt_avg_in_alr_g` (`1/128`). See
[delay_based_congestion_control.cc:L104-L106](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/delay_based_congestion_control.cc#104).
2. `feedback_hold_time_` updates are frozen. See
[scream_v2.cc:L78-L80](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#78).
3. `ScreamNetworkController` reports `is_bandwidth_limited = false` in
`TargetTransferRate`. See
[scream_network_controller.cc:L239](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#239).
### 4.3. Sender Transmission Control
#### 4.3.1. Send Window Calculation
- `ScreamV2::max_data_in_flight()`
([scream_v2.cc:L371-L382](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#371))
computes `ref_window_ * ref_window_overhead` with `ref_window_overhead`
interpolated between `ref_window_overhead_min` (1.5) and
`ref_window_overhead_max` (3.0) as in
[Section 4.3.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.3.1),
using `ref_window_scale_factor_due_to_avg_min_delay(/*allow_zero=*/true)` in
place of `ref_wnd_delay_scale`.
- `ScreamNetworkController` exports `max_data_in_flight()` as
`update.congestion_window` to WebRTC's pacer/transport
([scream_network_controller.cc:L276](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#276)).
#### 4.3.2. Packet Pacing
- **Pacing Rate Formula & No Relaxed Pacing Multiplier:**
- The spec defines `PACKET_PACING_HEADROOM = 1.5`, `RATE_PACE_MIN = 50 kbps`,
and a "relaxed pacing" multiplier (`MAX_RELAXED_PACING_FACTOR = 4.0`,
`RELAXED_PACING_LIMIT_LOW = 0.8`) that increases `pace_bitrate` up to `4x`
when `target_bitrate` approaches `TARGET_BITRATE_MAX`
([Section 4.3.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.3.2)).
- WebRTC does not implement the spec's relaxed pacing rate multiplier.
Instead:
- `ScreamV2::pacing_rate()` computes
`max(target_rate() * pacing_factor, pacing_rate_received_factor * received_rate_)`
with `pacing_factor = 1.1` and `pacing_rate_received_factor = 0.8`
([scream_v2.h:L50-L57](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.h#50)).
- `ScreamNetworkController::GetPacingRate()` floors this at
`min_total_allocated_bitrate * pacing_factor`
([scream_network_controller.cc:L215-L220](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#215)).
- To relax pacing when the link is uncongested,
`ScreamNetworkController::MaybeCreatePacerConfig()` adjusts the WebRTC
pacer's **burst time window** (`current_pacing_window_`): it uses a tight
`10 ms` pacing window after recent congestion, and relaxes to
`default_pacing_window_` once
`allow_large_pacing_bursts_after_congestion_time` (default 15 s) has
elapsed since `last_reaction_to_congestion_time()`. See
[scream_network_controller.cc:L284-L289](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#284).
- **Bandwidth Probing and Periodic Padding (WebRTC Addition):**
- The spec does not define active bandwidth probing or padding.
- `ScreamNetworkController` implements initial BWE probing
(`initial_probing_duration = 6 s`) and periodic padding bursts
(`periodic_padding_duration = 3 s` every
`time_between_periodic_padding = 3 s`, gated by
`allow_padding_after_last_congestion_time = 1 s` since the last reference
window decrease) when `target_rate` is below the maximum allocated/target
bitrate. See
[scream_network_controller.cc:L64-L89](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#64)
and
[scream_network_controller.cc:L291-L326](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#291).
### 4.4. Media Rate Control
- **Target Bitrate Calculation:**
- The spec calculates `target_bitrate = tmp_t * 8 * ref_wnd / s_rtt`, where
`tmp_t` includes:
1. Small-window scaling `1.0 - clamp(ref_wnd_ratio - 0.1, 0.0, 0.2)`,
2. Packetization overhead scaling `MSS / (MSS + PACKET_OVERHEAD)`, and
3. Sender queue and frame size variation division
`1 / (1.2 + rate_adjust_factor + frame_size_dev)`
([Section 4.4](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.4)).
- WebRTC's `ScreamV2::UpdateTargetRate()` includes the small-window scaling
`(1.0 - clamp(ref_window_mss_ratio() - 0.1, 0.0, 0.2))` and computes
`scale_target_rate * (ref_window_ / (non_zero_smoothed_rtt + feedback_hold_time_))`,
clamped to `[min_target_bitrate_, max_target_bitrate_]`. It **omits** the
`MSS / (MSS + PACKET_OVERHEAD)` factor and the
`1.2 + rate_adjust_factor + frame_size_dev` divisor (noted as a TODO in
[scream_v2.cc:L458-L459](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#458)).
- **Pacer Queue Pushback and Congestion Encoder Pause (WebRTC Alternative):**
- Instead of `rate_adjust_factor` and `frame_size_dev` inside `ScreamV2`,
`ScreamNetworkController` monitors the WebRTC pacer queue delay
(`pacing_delay = pacer_queue_size_ / pacing_rate`):
- **`cwnd_reduce_ratio` Pushback:** Scales linearly from `0.0` at
`min_pacing_delay_for_pushback` (default 100 ms) to `1.0` at
`max_pacing_delay_for_pushback` (default 500 ms) and is reported in
`TargetTransferRate::cwnd_reduce_ratio` to push back video encoders. See
[scream_network_controller.cc:L222-L235](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#222).
- **Encoder Pause:** If `data_in_flight_ > max_data_in_flight()` and
`pacing_delay > max_pacing_delay_for_pushback` (500 ms),
`ScreamNetworkController` sets `encoder_paused_due_to_congestion_ = true`
and reports a `target_rate` of `0` until `!is_congested` and
`pacing_delay < min_pacing_delay_for_pushback` (100 ms). See
[scream_network_controller.cc:L246-L258](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_network_controller.cc#246).
### 4.5. Additional Functions
#### 4.5.1. Clock Drift Issues and Remedies
- **Base Delay Filter and Queue Draining:**
- The spec describes tracking `qdelay_min` per RTT into a slow EWMA
`qdelay_min_avg` (`QDELAY_MIN_AVG_ALPHA = 1/256`). If
`qdelay_min_avg > qdelay_target / 4`, it resets the queue delay history and
halves `target_bitrate` for `max(5 * s_rtt, 0.2 s)`
([Section 4.5.1](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.5.1)).
- WebRTC implements two complementary mechanisms:
1. **LEDBAT Sliding Window Base Delay Filter:** Tracks minimum one-way delay
using `WindowedMinFilter<TimeDelta>` over `base_delay_window_length = 10`
intervals of `base_delay_history_update_interval = 1 minute`
([delay_based_congestion_control.cc:L56-L61](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/delay_based_congestion_control.cc#56)).
2. **Persistent Queue Delay Drain and Reset:** Instead of checking an EWMA
against `qdelay_target / 4` and resetting *before* draining, WebRTC
checks whether `min_queue_delay` has remained continuously above
`queue_delay_drain_threshold` (default 5 ms) for
`queue_delay_drain_period` (default 20 s) via `IsQueueDrainedInTime()`.
When exceeded, `UpdateTargetRate()` **first** halves `target_rate`
(`0.5 * target_rate`) for
`max(100 ms, queue_delay_drain_rtts * smoothed_rtt)` (default 5 RTTs) to
attempt to drain a standing self-inflicted queue. Only if
`min_queue_delay` fails to drop below the threshold by the end of that
drain period does WebRTC call `ResetQueueDelay()` to reset the base delay
history. See
[scream_v2.cc:L435-L456](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.cc#435).
#### 4.5.2. Link Layer Losses and Rate Policers
- **Link Layer Losses:**
- Instead of the per-packet EWMA `loss_rate` filter in
[Section 4.5.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.5.2),
WebRTC filters non-congestion link-layer losses using `LossEstimator`'s
biased asymmetric step filter (see Section 4.2.1.1 above), while sharing the
spec's principle of triggering immediate loss backoff when queue delay is
also elevated (`is_virtual_ce`, i.e.,
`queue_delay_avg_ > queue_delay_target / 2` vs.
`qdelay_avg > qdelay_target / 4` in the spec).
- **Rate Policer Detection Not Implemented:**
- The optional rate policer detection and `max_policed_ref_wnd` cap
(`LOSS_RATE_THRESHOLD_POLICER`, `BETA_LOSS_POLICER`) in
[Section 4.5.2](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.5.2)
are **not implemented** in WebRTC.
#### 4.5.3. Reference Window Undershoot at Congestion
- **Different Undershoot Prevention Mechanism:**
- The spec describes an optional check
(`if target_bitrate < acked_bitrate * ACKED_BITRATE_MARGIN`, with
`ACKED_BITRATE_MARGIN = 0.8`, scale `backoff_t` by
`BACKOFF_SCALE_LOW_TARGETRATE = 0.25`) to avoid undershooting when an RTT
spike has already depressed `target_bitrate`
([Section 4.5.3](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.5.3)).
- WebRTC **does not implement** `ACKED_BITRATE_MARGIN` or
`BACKOFF_SCALE_LOW_TARGETRATE`. Instead, WebRTC mitigates backoff undershoot
via `ScaleBackoff()` (waiting one full RTT after
`data_in_flight <= ref_window_` before starting a new full reduction, and
scaling backoff by `ref_window_ / data_in_flight` when in-flight bytes
exceed `ref_window_ * bytes_in_flight_head_room`; see Section 4.2.2.1).
- WebRTC also uses `received_rate_` (acknowledged bitrate over a 100 ms
window) to floor the pacing rate (`0.8 * received_rate_`) so pacing does not
collapse if `target_rate()` temporarily dips during an RTT spike
([scream_v2.h:L51-L55](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2.h#51)).
#### 4.5.4. Competing Flows Compensation
- **Not Implemented:**
- The spec describes an optional dynamic `qdelay_target` adjustment
(`adjust_qdelay_target` between `QDELAY_TARGET_LO = 60 ms` and
`QDELAY_TARGET_HI = 400 ms`) to compete with aggressive loss-based flows
over tail-drop queues
([Section 4.5.4](https://www.ietf.org/archive/id/draft-ietf-ccwg-rfc8298bis-screamv2-01.html#section-4.5.4)).
- The WebRTC implementation **does not implement** competing flows
compensation; `queue_delay_target` remains static (default 60 ms). This is
noted as a TODO in
[scream_v2_parameters.h:L121-L122](https://webrtc.googlesource.com/src/+/main/modules/congestion_controller/scream/scream_v2_parameters.h#121).