| # SCReAM v2 Implementation Differences | |
| **Date:** June 9, 2026\ | |
| **Commit:** | |
| [f48df12def0d531a66a3dffcea021fbe0f99dcde](https://webrtc.googlesource.com/src/+/f48df12def0d531a66a3dffcea021fbe0f99dcde)\ | |
| **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 | |
| SCReAM v2 and the IETF draft | |
| [draft-johansson-ccwg-rfc8298bis-screamv2-07](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html). | |
| The WebRTC implementation is currently in development and some features are not | |
| yet fully implemented. | |
| ## 4. Detailed Description of SCReAMv2 Sender Algorithm | |
| ### 4.1. Sender Side State | |
| #### 4.1.1. Status Update When Sending Data | |
| - **Differences in Default Parameters:** | |
| - `MinRefWindow` (`MIN_REF_WND` in spec): The spec recommends **3000 bytes** | |
| ([Section 4.2.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2)). | |
| The WebRTC implementation defaults to **1000 bytes** in | |
| [scream_v2_parameters.cc:L21](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2_parameters.cc#21). | |
| - `MaxSegmentSize` (`MSS` in spec): The spec recommends **1000 bytes** | |
| ([Section 4.2.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2)). | |
| The WebRTC implementation defaults to **1280 bytes** in | |
| [scream_v2_parameters.cc:L29](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2_parameters.cc#29). | |
| - `BytesInFlightHeadRoom` (`BYTES_IN_FLIGHT_HEAD_ROOM` in spec): The spec | |
| recommends **1.5** | |
| ([Section 4.2.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2)). | |
| The WebRTC implementation defaults to **1.1** in | |
| [scream_v2_parameters.cc:L30](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2_parameters.cc#30). | |
| #### 4.1.2. Status Update on Receiving Feedback | |
| - The WebRTC implementation defines the feedback structure in | |
| [scream_feedback.h](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_feedback.h). | |
| - Instead of tracking `bytes_newly_acked` and `bytes_newly_acked_ce` separately | |
| and resetting them after window updates as described in the spec | |
| ([Section 4.1.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.1.2)), | |
| the WebRTC `ScreamFeedback` struct directly provides `acked_not_marked_size` | |
| (sum of sizes of packets in the feedback that are NOT ECN CE-marked) and | |
| `num_ce_marked_packets`. This simplifies the window update logic in | |
| [scream_v2.cc](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc). | |
| - **Feedback Hold Time Tracking:** | |
| - The WebRTC implementation explicitly tracks `feedback_hold_time_` (the | |
| average time feedback is delayed at the receiver before being sent) in | |
| [scream_v2.cc:L311](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#311). | |
| - This hold time is integrated into multiple RTT-based calculations (initial | |
| window, window increase scaling, and target rate calculation) to prevent | |
| underestimating the true round-trip loop time. | |
| ### 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 | |
| - **Different Loss Detection Mechanism:** | |
| - The spec describes a RACK-like loss detection mechanism based on a | |
| reordering window and a "lost" flag in the list of transmitted data units | |
| ([Section 4.2.1.1](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.1.1)). | |
| - The WebRTC implementation uses a custom | |
| [LossEstimator](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/loss_estimator.h) | |
| ([loss_estimator.cc](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/loss_estimator.cc)) | |
| which implements a **biased asymmetric step filter** (leaky bucket) to | |
| estimate a short-term `congestion_level_` (between 0.0 and 1.0). | |
| - **Filter Mechanics:** If one or more packets are lost during an RTT, the | |
| filter steps up the congestion level by | |
| `1.0 / rtts_with_loss_before_backoff` (default +1/3). If an RTT is | |
| lossless, it steps down by `1.0 / lossless_rtts_before_clear` (default | |
| -1/2). | |
| - **Spurious Loss Filtering:** This asymmetric design ensures that spurious | |
| random losses (e.g., 1% uniform loss on wireless links, which results in | |
| ~40% of RTTs having at least one loss) result in a net-negative drift, | |
| keeping the congestion level near 0.0 and preventing unnecessary backoffs. | |
| It requires 3 consecutive RTTs with loss to reach the 1.0 threshold. | |
| - **Impact on Backoff:** Instead of reacting to individual loss events | |
| immediately, the WebRTC implementation only triggers a loss-based backoff | |
| when the `LossEstimator` reports that the connection is `congested()` | |
| (congestion level >= 0.99) OR if queue delay is also detected. See | |
| [scream_v2.cc:L134-L139](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#134). | |
| - **Impact on Window Increase:** When the congestion level is elevated (>= | |
| 0.01), the reference window increase is **blocked**, even if the current | |
| feedback report contains zero packet loss. Because the congestion level | |
| decays by 0.5 per lossless RTT, it takes **2 full lossless RTTs** for the | |
| increase blocker to clear after congestion ends. This prevents the window | |
| from immediately expanding again after a congestion event. See | |
| [scream_v2.cc:L199](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#199) | |
| and | |
| [loss_estimator.cc:L77](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/loss_estimator.cc#77). | |
| ##### 4.2.1.2. Receiving ECN-CE with classic ECN | |
| - **Classic ECN Not Supported:** | |
| - The spec defines a classic ECN mode where the reference window is scaled by | |
| a fixed `BETA_ECN` (default 0.8) | |
| ([Section 4.2.1.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.1.2)). | |
| - The WebRTC implementation **does not support classic ECN**. It only | |
| implements L4S-style ECN marking reaction. There is no `BETA_ECN` parameter | |
| or classic ECN backoff path in | |
| [scream_v2.cc](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc). | |
| ##### 4.2.1.3. Receiving ECN-CE for L4S | |
| - **L4S Alpha Update Frequency:** | |
| - The spec recommends accumulating delivered and marked packets and updating | |
| `l4s_alpha` at most once per `min(10ms, s_rtt)` | |
| ([Section 4.2.1.3](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.1.3)). | |
| - The WebRTC implementation updates `l4s_alpha_` on **every feedback message** | |
| based on the fraction of marked packets in that specific feedback, without | |
| the time interval constraint. See | |
| [scream_v2.cc:L109-L125](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#109). | |
| ##### 4.2.1.4. Detecting Increased Queue Delay | |
| - **Jitter Reduction and Delay Variation Scaling:** | |
| - The spec describes an optional jitter reduction mechanism based on a | |
| normalized queue delay deviation `qdelay_dev_norm` | |
| ([Section 4.2.1.4](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.1.4)). | |
| - The WebRTC implementation replaces `qdelay_dev_norm` with two alternative | |
| scaling factors calculated in | |
| [DelayBasedCongestionControl](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/delay_based_congestion_control.h) | |
| ([delay_based_congestion_control.cc](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/delay_based_congestion_control.cc)): | |
| 1. `ref_window_scale_factor_due_to_avg_min_delay` (based on | |
| `queue_delay_min_avg_`): Scales growth based on the average minimum queue | |
| delay. See | |
| [delay_based_congestion_control.cc:L122](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/delay_based_congestion_control.cc#122). | |
| 2. `ref_window_scale_factor_due_to_latency_difference` (based on | |
| `latency_difference_avg_`): Scales growth based on the latency difference | |
| (`max_one_way_delay - min_one_way_delay`) calculated over the recent | |
| burst / tail window (base window `BurstWindowMin`, default 25ms, extended | |
| up to `BurstWindowMax`, default 50ms, if consecutive packets are sent | |
| within `BurstWindowMaxGap`, default 3ms). See | |
| [delay_based_congestion_control.cc:L131](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/delay_based_congestion_control.cc#131). | |
| - These factors are used in | |
| [scream_v2.cc](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc) | |
| to scale backoff and window increase, replacing the `qdelay_dev_norm` terms | |
| in the spec. | |
| - **Queue Delay Average Update Frequency:** | |
| - The spec updates `qdelay_avg` at most once per `min(virtual_rtt, s_rtt)`. | |
| - The WebRTC implementation updates `queue_delay_avg_` on every feedback in | |
| [delay_based_congestion_control.cc:L61-L72](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/delay_based_congestion_control.cc#61). | |
| ###### 4.2.1.4.1. Competing Flows Compensation | |
| - **Not Implemented:** | |
| - The spec describes a dynamic target delay adjustment | |
| (`adjust_qdelay_target`) to allow SCReAM v2 to compete with aggressive | |
| loss-based flows (like TCP Reno/Cubic) by increasing `qdelay_target` when | |
| competing traffic is detected | |
| ([Section 4.2.1.4.1](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.1.4.1)). | |
| - The WebRTC implementation **does not implement this feature**. The | |
| `queue_delay_target` remains static (default 60ms). This is explicitly | |
| marked as a TODO in | |
| [scream_v2_parameters.h:L107](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2_parameters.h#107). | |
| #### 4.2.2. Reference Window Update | |
| ##### 4.2.2.1. Reference Window Reduction | |
| - **Inflection Point Scaling Factor:** | |
| - The spec uses a factor of **8** in the calculation of `scl_t` (scaling | |
| factor close to inflection point `ref_wnd_i`) | |
| ([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2.1)). | |
| - The WebRTC implementation defaults to a lower factor of **2.0** | |
| (`backoff_scale_factor_close_to_ref_window_i` in | |
| [scream_v2_parameters.cc:L38](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2_parameters.cc#38)), | |
| meaning the increase/decrease around the inflection point is slower (more | |
| cautious) than in the spec. | |
| - **Backoff Scaling Condition:** | |
| - The spec scales down L4S backoff if `qdelay < qdelay_target * 0.25` | |
| ([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2.1)). | |
| - The WebRTC implementation scales it down if | |
| `!delay_based_congestion_control_.IsQueueDelayDetected()`, which translates | |
| to `queue_delay_avg_ <= queue_delay_target / 2` (default 30ms) | |
| ([scream_v2.cc:L158](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#158)). | |
| - **L4S Alpha Bump:** | |
| - When resetting the reference window after a long period of inactivity (> 100 | |
| RTTs), the WebRTC implementation also bumps `l4s_alpha_` to **0.25** to | |
| ensure a rapid reaction if congestion re-occurs | |
| ([scream_v2.cc:L187](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#187)). | |
| This is not explicitly specified in the spec. | |
| - **Inflection Point (`ref_window_i`) Update Logic:** | |
| - **Spec:** `ref_wnd_i` is updated to `ref_wnd` immediately when *any* | |
| congestion event is detected, protected by a simple time-based guard: it | |
| cannot be updated more than once every **10 RTTs** | |
| ([Section 4.2.2.1](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2.1)). | |
| - **WebRTC:** Implements a state-based guard using | |
| `allow_ref_window_i_update_`. It only updates `ref_window_i_` when a **net | |
| decrease** in `ref_window_` occurs, and then blocks further updates | |
| (`allow_ref_window_i_update_ = false`) until a **net increase** in | |
| `ref_window_` has been observed. This ensures `ref_window_i_` captures the | |
| true peak before a congestion episode and is not dragged down by consecutive | |
| back-to-back reductions. See | |
| [scream_v2.cc:L259-L271](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#259). | |
| ##### 4.2.2.2. Reference Window Increase | |
| - **RTT Scaling for Small RTTs:** | |
| - The spec scales down the window increase linearly using | |
| `s_rtt / VIRTUAL_RTT` | |
| ([Section 4.2.2.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2.2)). | |
| - The WebRTC implementation scales it down using the **square of the RTT | |
| ratio** (`rtt_ratio * rtt_ratio`) and also includes `feedback_hold_time_` in | |
| the RTT calculation: `(rtt + feedback_hold) / virtual_rtt`. This results in | |
| a more aggressive reduction of the increase rate for very small RTTs. See | |
| [scream_v2.cc:L209-L215](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#209). | |
| - **Application Limited State (ALR) Tracking:** | |
| - **Spec:** The spec discusses limiting the reference window growth when the | |
| amount of data in flight is small to avoid window inflation ("Increase | |
| ref_wnd only if bytes in flight is large enough") and recommends stopping | |
| the increase if `target_bitrate` reaches `TARGET_BITRATE_MAX` | |
| ([Section 4.2.2.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.2.2.2)). | |
| - **WebRTC:** Implements a formal, explicit **Application Limited State | |
| (ALR)** tracking mechanism (enabled by default via `EnableAlr` field trial). | |
| The sender enters ALR when the maximum data in flight during the last RTTs | |
| is insufficient to fill the reference window | |
| (`max_allowed_ref_window() < ref_window_`). See | |
| [scream_v2.cc:L68-L70](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#68). | |
| - **ALR Effects in WebRTC:** Entering ALR has three major impacts to ensure | |
| stability during sparse traffic periods: | |
| 1. **Halts Window Growth:** The reference window `ref_window_` is strictly | |
| prevented from increasing. See | |
| [scream_v2.cc:L253](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#253). | |
| 2. **Slows RTT Smoothing:** The EWMA gain used for smoothing the RTT is | |
| drastically reduced from `1/8` to `1/128` (`smoothed_rtt_avg_in_alr_g`). | |
| This prevents RTT estimates from being corrupted by sparse, potentially | |
| delayed feedback samples when traffic is low. See | |
| [delay_based_congestion_control.cc:L101](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/delay_based_congestion_control.cc#101). | |
| 3. **Freezes Feedback Hold Time Updates:** The running average of the | |
| receiver's feedback hold time is not updated during ALR, preventing stale | |
| or atypical delay samples from affecting the average. See | |
| [scream_v2.cc:L74](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#74). | |
| ### 4.3. Sender Transmission Control | |
| #### 4.3.1. Send Window Calculation | |
| - The WebRTC implementation calculates the maximum allowed data in flight (send | |
| window) in `ScreamV2::max_data_in_flight()` | |
| ([scream_v2.cc:L286-L297](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#286)) | |
| using the same formula as the spec: `ref_window * ref_wnd_overhead` | |
| ([Section 4.3.1](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.3.1)). | |
| - However, it uses `ref_window_scale_factor_due_to_avg_min_delay(true)` instead | |
| of `qdelay_dev_norm` to dynamically adjust `ref_wnd_overhead` between | |
| `REF_WND_OVERHEAD_MIN` (1.5) and `REF_WND_OVERHEAD_MAX` (3.0). | |
| #### 4.3.2. Packet Pacing | |
| - **No Relaxed Pacing Logic:** | |
| - The spec describes a complex "relaxed pacing" mechanism that increases the | |
| pacing rate (effectively allowing larger bursts) when the target bitrate is | |
| close to the maximum, to reduce application-layer latency | |
| ([Section 4.3.2](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.3.2)). | |
| - The WebRTC implementation **does not implement relaxed pacing** in the | |
| SCReAM module. Instead, it calculates a simple `pacing_rate` as | |
| `target_rate_ * pacing_factor` (default factor 1.1) in | |
| [scream_v2.h:L50](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.h#50) | |
| and delegates the actual pacing to the standard WebRTC pacer via | |
| [ScreamNetworkController::MaybeCreatePacerConfig](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_network_controller.cc#224). | |
| - **Bandwidth Probing and Periodic Padding (WebRTC Addition):** | |
| - **Spec:** The spec does not define any mechanism for bandwidth probing or | |
| background padding. | |
| - **WebRTC:** Adds a comprehensive probing and periodic padding mechanism to | |
| quickly discover and maintain link capacity estimates, especially during | |
| inactivity or at call start. This is implemented in | |
| [scream_network_controller.cc:L224](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_network_controller.cc#224) | |
| and controlled by WebRTC-specific parameters (e.g., | |
| `initial_probing_duration`, `time_between_periodic_padding`, | |
| `periodic_padding_duration`, `allow_padding_after_last_congestion_time`) | |
| defined in | |
| [scream_v2_parameters.h](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2_parameters.h). | |
| ### 4.4. Media Rate Control | |
| - **Simplified Target Rate Calculation (Missing Compensations):** | |
| - The spec describes a complex media rate control that adjusts the target rate | |
| using `rate_adjust_factor` (to compensate for media queue buildup) and | |
| `frame_size_dev` (to compensate for varying frame sizes) | |
| ([Section 4.4](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.4)). | |
| - The WebRTC implementation **does not implement these compensations**. This | |
| is marked as a TODO in | |
| [scream_v2.cc:L359](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#359). | |
| - Instead, it uses a simplified formula: | |
| `target_rate = scale_target_rate * (ref_window_ / (smoothed_rtt + feedback_hold_time_))` | |
| (where `scale_target_rate` scales down the rate slightly if the window is | |
| very small). See | |
| [scream_v2.cc:L321-L335](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#321). | |
| - **Queue Draining Logic:** | |
| - The WebRTC implementation adds a custom "drain queue" logic. If the minimum | |
| queue delay remains above a threshold (`queue_delay_drain_threshold`, | |
| default 5ms) for a prolonged period (`queue_delay_drain_period`, default | |
| 20s), it temporarily halves the target rate for a few RTTs to attempt to | |
| drain the queue. If this fails, it resets the queue delay estimate. See | |
| [scream_v2.cc:L336-L357](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/scream_v2.cc#336). | |
| This is not explicitly part of Section 4.4 in the spec, but serves as a | |
| robust guard against persistent queue build-up. | |
| ### 4.5. Clock drift issues and remedies | |
| - **Implementation Details:** | |
| - The spec describes a clock drift remedy that tracks `qdelay_min` over an | |
| RTT, averages it, and triggers a reset and 50% rate reduction if | |
| `qdelay_min_avg > qdelay_target / 4` | |
| ([Section 4.5](https://www.ietf.org/archive/id/draft-johansson-ccwg-rfc8298bis-screamv2-07.html#section-4.5)). | |
| - The WebRTC implementation uses a sliding window minimum filter | |
| (`base_delay_history_` with window length 10) in | |
| [DelayBasedCongestionControl](https://webrtc.googlesource.com/src/+/master/modules/congestion_controller/scream/delay_based_congestion_control.h) | |
| to track the base delay and compensate for clock drift. The history is | |
| updated every 1 minute (`base_delay_history_update_interval`). | |
| - The "drain queue" logic in `UpdateTargetRate` (described in Section 4.4 | |
| above) acts as the practical trigger for resetting the queue delay estimate | |
| if clock drift (or other non-congestion factors) causes the one-way delay to | |
| increase permanently. |