| # 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 ($\\ge |
| 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 difference between |
| max and min latency in feedback. 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. |