RFC 8298
Self-Clocked Rate Adaptation for Multimedia
RFC 8298: Self-Clocked Rate Adaptation for Multimedia ๐
okay bestie, this is RFC 8298 ๐ It's published by the IETF (Internet Engineering Task Force) and written by I. Johansson and Z. Sarker from Ericsson AB. It came out in December 2017, it's ISSN 2070-1721, and it's marked as Experimental ๐งช The doc doesn't say it obsoletes or updates any other RFC, so it stands on its own โจ
Abstract ๐
this memo walks through a rate adaptation algorithm built for conversational media, like interactive video ๐น The solution follows the packet conservation principle and uses a hybrid loss-and-delay-based congestion control algorithm ๐ง They tested it on simulated Internet bottleneck scenarios AND in a Long Term Evolution (LTE) system simulator, and it reached both low latency and high video throughput ๐ฅ literally iconic.
Status of This Memo ๐
this document is not an Internet Standards Track specification ๐ โโ๏ธ It's getting published so people can examine it, try out experimental implementations, and evaluate it ๐ฌ
It defines an Experimental Protocol for the Internet community. It's a product of the IETF and represents the consensus of the IETF community ๐ซถ It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). Not every IESG-approved document is a candidate for any level of Internet Standard, so see Section 2 of RFC 7841 for the details ๐
Info about the current status of this document, any errata, and how to send feedback is at https://www.rfc-editor.org/info/rfc8298 ๐
Copyright Notice โ๏ธ
Copyright (c) 2017 IETF Trust and the persons identified as the document authors. All rights reserved ยฉ๏ธ
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication. Please read these carefully, because they explain your rights and restrictions for this document ๐ Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they're provided without warranty as described in the Simplified BSD License ๐
Table of Contents ๐บ๏ธ
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1. Wireless (LTE) Access Properties . . . . . . . . . . . . 4
1.2. Why is it a self-clocked algorithm? . . . . . . . . . . . 5
2. Requirements Language . . . . . . . . . . . . . . . . . . . . 5
3. Overview of SCReAM Algorithm . . . . . . . . . . . . . . . . 6
3.1. Network Congestion Control . . . . . . . . . . . . . . . 8
3.2. Sender Transmission Control . . . . . . . . . . . . . . . 9
3.3. Media Rate Control . . . . . . . . . . . . . . . . . . . 9
4. Detailed Description of SCReAM . . . . . . . . . . . . . . . 10
4.1. SCReAM Sender . . . . . . . . . . . . . . . . . . . . . . 10
4.1.1. Constants and Parameter Values . . . . . . . . . . . 10
4.1.1.1. Constants . . . . . . . . . . . . . . . . . . . . 11
4.1.1.2. State Variables . . . . . . . . . . . . . . . . . 12
4.1.2. Network Congestion Control . . . . . . . . . . . . . 14
4.1.2.1. Reaction to Packet Loss and ECN . . . . . . . . . 17
4.1.2.2. Congestion Window Update . . . . . . . . . . . . 17
4.1.2.3. Competing Flows Compensation . . . . . . . . . . 20
4.1.2.4. Lost Packet Detection . . . . . . . . . . . . . . 22
4.1.2.5. Send Window Calculation . . . . . . . . . . . . . 23
4.1.2.6. Packet Pacing . . . . . . . . . . . . . . . . . . 24
4.1.2.7. Resuming Fast Increase Mode . . . . . . . . . . . 24
4.1.2.8. Stream Prioritization . . . . . . . . . . . . . . 24
4.1.3. Media Rate Control . . . . . . . . . . . . . . . . . 25
4.2. SCReAM Receiver . . . . . . . . . . . . . . . . . . . . . 28
4.2.1. Requirements on Feedback Elements . . . . . . . . . . 28
4.2.2. Requirements on Feedback Intensity . . . . . . . . . 30
5. Discussion . . . . . . . . . . . . . . . . . . . . . . . . . 31
6. Suggested Experiments . . . . . . . . . . . . . . . . . . . . 31
7. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 32
8. Security Considerations . . . . . . . . . . . . . . . . . . . 32
9. References . . . . . . . . . . . . . . . . . . . . . . . . . 33
9.1. Normative References . . . . . . . . . . . . . . . . . . 33
9.2. Informative References . . . . . . . . . . . . . . . . . 34
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . 36
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 36
1. Introduction ๐ธ
okay so congestion on the Internet happens when the bitrate you're sending is higher than the capacity the path can actually handle ๐ญ Apps on the Internet have to use congestion control to perform robustly and to avoid congestion collapse, periodt. Interactive real-time communication puts a LOT of demands on the transport, so a robust, efficient rate adaptation for all access types is a big deal, since the transmission channel bandwidth can change over time ๐
Wireless access like LTE is an integral part of the current Internet, and it makes rate adaptation even more important because the channel bandwidth of a default LTE bearer [QoS-3GPP] can change a lot in a super short time frame ๐ So a rate adaptation solution for interactive real-time media, like WebRTC [RFC7478], should be both quick and able to operate over a large range of channel capacity โจ
This memo describes Self-Clocked Rate Adaptation for Multimedia (SCReAM) ๐ฆ, a solution that implements congestion control for RTP streams [RFC3550]. SCReAM was originally devised for WebRTC, but it can also be used for other applications where congestion control of RTP streams is necessary. It's based on the self-clocking principle of TCP and uses techniques similar to the rate adaptation algorithm based on Low Extra Delay Background Transport (LEDBAT) [RFC6817] ๐ง
SCReAM isn't totally self-clocked, though, because it adds pacing and a minimum send rate on top ๐ It can take advantage of Explicit Congestion Notification (ECN) in cases where ECN is supported by the network and the hosts. However, ECN is not required for the basic congestion control functionality in SCReAM ๐ซถ
1.1. Wireless (LTE) Access Properties ๐ถ
[WIRELESS-TESTS] describes the complications you can observe in wireless environments ๐ Wireless access like LTE typically can't guarantee a given bandwidth, and that's especially true for default bearers. The network throughput can vary a lot, for instance when the wireless terminal is moving around. Even though LTE can support bitrates well above 100 Mbps, there are cases when the available bitrate can be way lower, like during high network load and poor coverage ๐ฉ Another wrinkle is that the network throughput can drop for short time intervals (e.g., at handover), and these short glitches are super hard to tell apart from more permanent reductions in throughput at first.
Unlike wireline bottlenecks with large statistical multiplexing, you can't just try to hold a given bitrate when congestion is detected and hope other flows will yield ๐ That's because there are generally only a few other flows competing for the same bottleneck. Each user gets their own variable throughput bottleneck, where the throughput depends on things like channel quality, network load, and historical throughput. The bottom line is, if the throughput drops, the sender has no other option than to reduce the bitrate ๐ Once the radio scheduler has reduced the resource allocation for a bearer, a flow (which is using RTP Media Congestion Avoidance Techniques (RMCAT)) in that bearer aims to reduce the sending rate quite quickly (within one RTT) so it avoids excessive queuing delay or packet loss ๐
1.2. Why is it a self-clocked algorithm? ๐ค
Self-clocked congestion control algorithms have a perk over their rate-based counterparts, because the former has two adaptation mechanisms:
- A congestion window computation that evolves over a longer timescale (several RTTs), especially when the congestion window evolution is dictated by estimated delay (to minimize vulnerability to, e.g., short-term delay variations) ๐
- A fine-grained congestion control given by the self-clocking. It operates on a shorter timescale (1 RTT). The benefits of self-clocking are also elaborated upon in [TFWC] โก
A rate-based congestion control algorithm typically adjusts the rate based on delay and loss. The congestion detection needs to happen with a certain time lag so it doesn't overreact to spurious congestion events, like delay spikes ๐ซ Even though there are two or more congestion indications, the outcome is still only one mechanism to adjust the sending rate. That makes it hard to reach the goals of high throughput and prompt reaction to congestion at the same time ๐
2. Requirements Language ๐
the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are meant to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, just like shown here ๐ซถ
3. Overview of SCReAM Algorithm โจ
okay so the core SCReAM algorithm is giving cousin energy to the self-clocking stuff in TCP-friendly window-based congestion control [TFWC] ๐, and it follows the packet conservation principle. Packet conservation is described as a key factor in protecting networks from congestion [Packet-conservation]. Iconic, honestly ๐
Here's the vibe: the receiver of the media echoes back a list of received RTP packets, plus the timestamp of the RTP packet with the highest sequence number, in feedback packets ๐. The sender keeps a list of transmitted packets, their sizes, and the time each one was sent. That info tells it how many bytes it can send at any given moment โฐ. A congestion window puts an upper limit on how many bytes can be in flight, meaning sent but not yet acknowledged ๐ฆ.
The congestion window is figured out in a way that's similar to LEDBAT [RFC6817]. LEDBAT uses send and receive timestamps to estimate the queuing delay (from now on called "qdelay") along the transmission path ๐ธ. That estimate is used to adjust the congestion window, and LEDBAT is the reason end-to-end latency stays low ๐ซถ. [LEDBAT-delay-impact] shows that LEDBAT has some inherent issues that work against its goal of low delay ๐ญ. The big problem the paper describes is that the base delay gets offset by LEDBAT's own queue buildup.
Now here's the twist ๐ง. With SCReAM, the source is rate limited, and the RTP queue has to be kept short (ideally empty). Also, a video encoder's output is rarely constant bitrate. Static content, like talking heads, gives almost zero video bitrate ๐. That gives us two useful properties when LEDBAT is used with SCReAM, and they help avoid the issues described in [LEDBAT-delay-impact]:
- There is always a certain probability that SCReAM is short of data to transmit. This means the network queue will become empty every once in a while.
- The max video bitrate can be lower than the link capacity. If the max video bitrate is 5 Mbps and the capacity is 10 Mbps, then the network queue will become empty.
Only one of those two conditions has to be fulfilled for the base delay to update properly ๐. Also, [LEDBAT-delay-impact] describes an issue with short-lived competing flows. In SCReAM, those short-lived flows make the self-clocking slow down, which builds up the RTP queue. That in turn lowers the media video bitrate ๐ค. So SCReAM slows the bitrate down more when there are competing short-lived flows than traditional LEDBAT does. The basic LEDBAT-in-SCReAM idea is pretty simple, but a few steps are needed to make it work with conversational media ๐:
- Congestion window validation techniques. These are similar to the method described in [RFC7661]. Congestion window validation makes sure the congestion window is limited by the actual number of bytes in flight, which matters a lot for rate-limited sources like video ๐ฅ. Without it, the reaction to congestion is slow, because the congestion window doesn't properly reflect the congestion state in the network. The allowed idle period in this memo is shorter than in [RFC7661], to avoid excessive delays when, for example, wireless throughput has decreased during a stretch where the media coder's output bitrate was low (say, because of inactivity) ๐ฌ. This memo also allows more relaxed rules for when the congestion window is allowed to grow, because variable output bitrate usually means the congestion window is often underutilized ๐.
- Fast increase mode makes the bitrate increase faster when no congestion is detected ๐. It makes the media bitrate ramp up within 5 to 10 seconds. The behavior is similar to TCP slowstart. Fast increase mode is exited when congestion is detected. However, fast increase mode can resume if the congestion level is low, which allows a reasonably quick rate increase when link throughput goes up ๐.
- A qdelay trend is computed for earlier detection of incipient congestion. As a result, it reduces jitter โจ.
- Addition of a media rate control function.
- Use of inflection points in the media rate calculation to achieve reduced jitter.
- Adjustment of qdelay target for better performance when competing with other loss-based congestion-controlled flows.
The features above are described in more detail in Sections 3.1 to 3.3. The full details are in Section 4 ๐.
+---------------------------+
| Media encoder |
+---------------------------+
^ |
| |(1)
|(3) RTP
| V
| +-----------+
+---------+ | |
| Media | (2) | Queue |
| rate |<------| |
| control | |RTP packets|
+---------+ | |
+-----------+
|
|(4)
RTP
|
v
+------------+ +--------------+
| Network | (7) | Sender |
+-->| congestion |------>| Transmission |
| | control | | Control |
| +------------+ +--------------+
| |
|-------------RTCP----------| |(5)
(6) | RTP
| v
+------------+
| UDP |
| socket |
+------------+
Figure 1: SCReAM Sender Functional View
The SCReAM algorithm is made of three main parts: network congestion control, sender transmission control, and media rate control. All of these live on the sender side ๐ง. Figure 1 shows the functional overview of a SCReAM sender. The receiver-side algorithm is way simpler in comparison, since it only generates feedback with acknowledgements of received RTP packets and an ECN count ๐.
3.1. Network Congestion Control
The network congestion control sets an upper limit on how much data can be in the network (bytes in flight). This limit is called CWND (congestion window), and it's used in the sender transmission control ๐ท.
SCReAM's congestion control uses techniques similar to LEDBAT [RFC6817] to measure the qdelay. Just like with LEDBAT, the sender and receiver don't need synchronized clocks to compute the qdelay ๐ฐ๏ธ. But they do need to use the same clock frequency, or the sender must be able to reliably infer the clock frequency at the receiver. If this isn't met, SCReAM's congestion control malfunctions, because it estimates the network queue delay incorrectly ๐.
The SCReAM sender calculates the congestion window based on feedback from the SCReAM receiver. The congestion window is allowed to increase if the qdelay is below a predefined qdelay target. Otherwise, the congestion window decreases. The qdelay target is typically set to 50-100 ms, which keeps the queuing delay low ๐. Reacting to a loss or ECN event causes an instant reduction of CWND ๐ฅ. Note that because real-time media like video is source rate limited, the queuing delay will mostly stay below the delay target. That's different from large file transfers using LEDBAT congestion control, where the queuing delay stays close to the delay target ๐ฆ.
3.2. Sender Transmission Control
The sender transmission control limits the output of data, based on the relation between the number of bytes in flight and the congestion window. Packet pacing is used to mitigate issues with ACK compression that MAY cause increased jitter and/or packet loss in the media traffic ๐ถ. Packet pacing limits the packet transmission rate to the estimated link throughput. Even if the send window allows a bunch of packets to go out, they aren't sent right away. Instead, they're sent at intervals based on the packet size and the estimated link throughput ๐๏ธ.
3.3. Media Rate Control
The media rate control adjusts the media bitrate so it ramps up quickly enough to get a fair share of system resources when link throughput increases ๐.
The reaction to reduced throughput MUST be prompt, so too much data doesn't pile up in the RTP packet queue(s) at the sender ๐จ. The media bitrate is decreased if the RTP queue size exceeds a threshold.
When the sender's frame queues grow rapidly, like during a Radio Access Type (RAT) handover, the SCReAM sender MAY implement extra actions, such as discarding encoded media frames or frame skipping, to drain the RTP queues quickly ๐งน. Frame skipping temporarily reduces the frame rate. Which method to use is a design choice and is outside the scope of this algorithm description ๐คทโโ๏ธ.
4. Detailed Description of SCReAM
4.1. SCReAM Sender โจ
okay so this part gets into the sender side in way more detail ๐ง it's split across three pieces: the network congestion control, the sender transmission control, and the media rate control. three main characters, one team ๐
each SCReAM sender has media rate control plus an RTP queue for every media type or source. that queue is where RTP packets holding encoded media frames wait around before they get sent ๐ Figure 1 shows the setup with a single media source (or stream). to handle multiple streams you add a transmission scheduler (it's not in the figure, bestie). the scheduler can enforce different priorities between streams and kinda acts like a coupled congestion controller for multiple flows ๐ซถ multi-stream support is done in [SCReAM-CPP-implementation], so go peek there if you're curious ๐ฆ
media frames get encoded and forwarded to the RTP queue (1) in Figure 1. the media rate adaptation looks at the size of the RTP queue (2) and gives the media encoder a target rate (3). the sender transmission controller then picks RTP packets from the RTP queue (4). with multiple flows, it pulls from each RTP queue based on some defined priority order or just round-robin ๐ the sender transmission controller (or the transmission scheduler, if there are multiple flows) sends those RTP packets to the UDP socket (5). in the general case, all media SHOULD go through the sender transmission controller, and it's limited so that the bytes in flight stay less than the congestion window. RTCP packets come in (6), and the info about bytes in flight and the congestion window gets swapped between the network congestion control and the sender transmission control (7) ๐ฅ
4.1.1. Constants and Parameter Values
constants and state variables are listed in this section ๐ temporary variables are not listed. instead, they get '_t' tacked on in the pseudocode to show they're local to that spot ๐
4.1.1.1. Constants
the RECOMMENDED values (the ones in parentheses "()") were figured out from experiments ๐งช
QDELAY_TARGET_LO (0.1 s): target value for the minimum qdelay.QDELAY_TARGET_HI (0.4 s): target value for the maximum qdelay. this is the upper limit on how far the target qdelay (qdelay_target) can climb to cope with competing loss-based flows. but the target qdelay doesn't have to start at this high value, since that would bump up end-to-end delay and make the rate control and congestion control loops sluggish ๐ขQDELAY_WEIGHT (0.1): averaging factor for qdelay_fraction_avg.QDELAY_TREND_TH (0.2): threshold for detecting incipient congestion.MIN_CWND (3000 bytes): minimum congestion window.MAX_BYTES_IN_FLIGHT_HEAD_ROOM (1.1): headroom for limiting CWND.GAIN (1.0): gain factor for congestion window adjustment.BETA_LOSS (0.8): CWND scale factor due to a loss event.BETA_ECN (0.9): CWND scale factor due to an ECN event.BETA_R (0.9): scale factor for target rate due to a loss event.MSS (1000 byte): maximum segment size = max RTP packet size.RATE_ADJUST_INTERVAL (0.2 s): interval between media bitrate adjustments.TARGET_BITRATE_MIN: minimum target bitrate in bps (bits per second).TARGET_BITRATE_MAX: maximum target bitrate in bps.RAMP_UP_SPEED (200000 bps/s): maximum allowed rate increase speed.PRE_CONGESTION_GUARD (0.0..1.0): guard factor against early congestion onset. a higher value gives less jitter, possibly at the expense of lower link utilization. this value MAY be subject to tuning depending on e.g., media coder characteristics. experiments with H264 and VP8 indicate that 0.1 is a suitable value. see [SCReAM-CPP-implementation] and [SCReAM-implementation-experience] for evaluation of a real implementation.TX_QUEUE_SIZE_FACTOR (0.0..2.0): guard factor against RTP queue buildup. this value MAY be subject to tuning depending on, e.g., media coder characteristics. experiments with H264 and VP8 indicate that 1.0 is a suitable value. see [SCReAM-CPP-implementation] and [SCReAM-implementation-experience] for evaluation of a real implementation.RTP_QDELAY_TH (0.02 s): RTP queue delay threshold for a target rate reduction.TARGET_RATE_SCALE_RTP_QDELAY (0.95): scale factor for target rate when the RTP qdelay threshold is exceeded (RTP_QDELAY_TH).QDELAY_TREND_LO (0.2): threshold value for qdelay_trend.T_RESUME_FAST_INCREASE (5 s): time span until fast increase mode can be resumed, given that the qdelay_trend is below QDELAY_TREND_LO.RATE_PACE_MIN (50000 bps): minimum pacing rate.
4.1.1.2. State Variables
the values within parentheses "()" are the initial values ๐ฑ
qdelay_target (QDELAY_TARGET_LO): qdelay target. a variable qdelay target is introduced to manage cases where a fixed qdelay target would otherwise starve the RMCAT flow under such circumstances (e.g., FTP competes for bandwidth over the same bottleneck). the qdelay target is allowed to vary between QDELAY_TARGET_LO and QDELAY_TARGET_HI.qdelay_fraction_avg (0.0): fractional qdelay filtered by the Exponentially Weighted Moving Average (EWMA).qdelay_fraction_hist[20] ({0,..,0}): vector of the last 20 fractional qdelay samples.qdelay_trend (0.0): qdelay trend, which indicates incipient congestion.qdelay_trend_mem (0.0): low-pass filtered version of qdelay_trend.qdelay_norm_hist[100] ({0,..,0}): vector of the last 100 normalized qdelay samples.in_fast_increase (true): true if in fast increase mode.cwnd (MIN_CWND): congestion window.bytes_newly_acked (0): the number of bytes acknowledged with the last received acknowledgement, i.e., bytes acknowledged since the last CWND update.max_bytes_in_flight (0): the maximum number of bytes in flight over a sliding time window, i.e., transmitted but not yet acknowledged bytes.send_wnd (0): upper limit to how many bytes can currently be transmitted. updated when cwnd is updated and when an RTP packet is transmitted.target_bitrate (0 bps): media target bitrate.target_bitrate_last_max (1 bps): inflection point of the media target bitrate, i.e., the last known highest target_bitrate. used to limit bitrate increase speed close to the last known congestion point.rate_transmit (0.0 bps): measured transmit bitrate.rate_ack (0.0 bps): measured throughput based on received acknowledgements.rate_media (0.0 bps): measured bitrate from the media encoder.rate_media_median (0.0 bps): median value of rate_media, computed over more than 10 s.s_rtt (0.0s): smoothed RTT (in seconds), computed with a similar method to that described in [RFC6298].rtp_queue_size (0 bits): sum of the sizes of RTP packets in queue.rtp_size (0 byte): size of the last transmitted RTP packet.loss_event_rate (0.0): the estimated fraction of RTTs with lost packets detected.
4.1.2. Network Congestion Control โจ
okay so this section is where the magic happens ๐ฆ network congestion control does two big jobs:
- Figuring out the congestion window on the sender side: this is the upper limit on how many bytes can be in flight at once ๐
- Calculating the send window on the sender side: RTP packets only get sent if the number of bytes in flight fits within the congestion window, and the send window is what controls that ๐ซถ
SCReAM is a window-based, byte-oriented congestion control protocol ๐ธ It counts bytes by looking at the size of the RTP packets that were actually sent. So the sender has to keep a list of every transmitted RTP packet and when it went out (wall-clock time) ๐ bestie, that list is non-negotiable, it MUST be kept for the later math ๐ญ
Now the bytes_in_flight variable ๐งฎ it's the sum of the sizes of RTP packets from the most recently transmitted one all the way down to, but not including, the acknowledged packet with the highest sequence number. Put another way, it's the gap between the highest transmitted byte sequence number and the highest acknowledged byte sequence number ๐
Picture this: you send packet SN, and the latest ACK says the highest thing received is SN-5. Then bytes_in_flight is the sum of the sizes of packets SN-4, SN-3, SN-2, SN-1, and SN โจ and get this, even if SN-3 got lost, its size still counts ๐ no skipping it, it's still in the total.
Next up is bytes_newly_acked, which goes up by however much the highest sequence number has moved since the last feedback ๐ say the last feedback said N and the new one says N+3. Then bytes_newly_acked goes up by the sizes of N+1, N+2, and N+3 ๐ lost packets get included here too, so even if N+2 disappeared into the void, its size still gets added ๐ and bytes_newly_acked gets reset to zero after every CWND update ๐งน
The receiver's feedback is assumed to have these pieces:
- A list of the sequence numbers of the received RTP packets ๐
- The wall-clock timestamp of the received RTP packet with the highest sequence number โฐ
- The accumulated number of ECN-CE-marked packets (
n_ECN) ๐ CE just means "Congestion Experienced" ๐ฆ
When the sender gets RTCP feedback, the qdelay gets calculated the way [RFC6817] describes ๐ง you get a qdelay sample for every acknowledgement you receive. There's no smoothing on the qdelay itself, BUT some smoothing happens anyway because the CWND computation acts like a low-pass filter ๐ซง the pseudocode below updates a bunch of variables, and temporary variables get a _t on the end ๐
Also heads up: as mentioned in Section 6, getting the congestion window and media bitrate just right might benefit from extra optimizations for very high and very low bitrates, plus some extra damping for periodic packet bursts ๐ธ some of those tricks live in [SCReAM-CPP-implementation], but they are NOT part of the SCReAM spec right now, so don't go thinking they're required ๐ โโ๏ธ
update_variables(qdelay):
qdelay_fraction_t = qdelay / qdelay_target
# Calculate moving average
qdelay_fraction_avg = (1 - QDELAY_WEIGHT) * qdelay_fraction_avg +
QDELAY_WEIGHT * qdelay_fraction_t
update_qdelay_fraction_hist(qdelay_fraction_t)
# Compute the average of the values in qdelay_fraction_hist
avg_t = average(qdelay_fraction_hist)
# R is an autocorrelation function of qdelay_fraction_hist,
# with the mean (DC component) removed, at lag K
# The subtraction of the scalar avg_t from
# qdelay_fraction_hist is performed element-wise
a_t = R(qdelay_fraction_hist-avg_t, 1) /
R(qdelay_fraction_hist-avg_t, 0)
# Calculate qdelay trend
qdelay_trend = min(1.0, max(0.0, a_t * qdelay_fraction_avg))
# Calculate a 'peak-hold' qdelay_trend; this gives a memory
# of congestion in the past
qdelay_trend_mem = max(0.99 * qdelay_trend_mem, qdelay_trend)
The qdelay fraction gets sampled every 50 ms, and the last 20 samples are kept in a vector called qdelay_fraction_hist ๐ that vector feeds into a qdelay trend that lands somewhere between 0.0 and 1.0, depending on how congested things look โจ the prediction coefficient a_t is positive when qdelay is trending up OR down, which means you get a congestion heads-up BEFORE qdelay even hits the target ๐จ lowkey genius. As a side effect, if qdelay goes down, that also counts as a sign of congestion ๐ and honestly? Experiments showed this is actually beneficial, because a queue delay that's climbing or dropping means the transmit rate is super close to the path capacity ๐ฅ
The autocorrelation function R works like this: let x be a vector of N values, then the biased autocorrelation for a given lag k of the vector x is ๐
n=N-k
R(x,k) = SUM x(n) * x(n + k)
n=1
The prediction coefficient gets multiplied by qdelay_fraction_avg so it's less sensitive to increasing qdelay when qdelay is tiny ๐งธ Sampling every 50 ms is a simplification, so the same qdelay might get sampled a few times in a row, but that's totally fine ๐ the vector only cares whether qdelay is going up or down anyway. The qdelay_trend gets used in media rate control to spot incipient congestion and to decide when to leave fast increase mode ๐โโ๏ธ and qdelay_trend_mem is there to enforce a less aggressive rate increase after congestion events ๐ซถ finally, update_qdelay_fraction_hist(..) kicks out the oldest element and adds the newest qdelay fraction to the qdelay_fraction_hist vector ๐
4.1.2.1. Reaction to Packet Loss and ECN
okay now let's talk about how SCReAM reacts when things go wrong ๐ญ a loss event happens when one or more RTP packets get declared missing ๐ how that loss detection works is in Section 4.1.2.4 ๐ once a loss event is detected, further lost RTP packets SHOULD be ignored for a full smoothed round-trip time ๐ the goal is to keep the congestion window from dropping more than once per round trip ๐
Here's the spicy part ๐ถ๏ธ the congestion window back-off after loss is deliberately a bit gentler than, say, TCP Reno ๐ why? TCP is usually moving whole files, so it acts kinda like a source with infinite bitrate until the file is all sent ๐ SCReAM, on the other hand, has a source whose rate stays close to the available transmit rate and is often even below it ๐ฅบ so SCReAM just has less chances to grab free capacity than a TCP file transfer does. To make up for that, it is RECOMMENDED to let SCReAM reduce the congestion window less than TCP would when loss events happen ๐
Now ECN ๐ฆ an ECN event is detected if the n_ECN counter in the feedback report has gone up since the last feedback you received โจ once an ECN event is detected, the n_ECN counter gets ignored for a full smoothed round-trip time, again so the congestion window only drops at most once per round trip ๐ the back-off from an ECN event MAY be smaller than the one from a loss event ๐ this lines up with the idea in [ALT-BACKOFF], which lets ECN marking thresholds sit lower than the packet drop thresholds ๐ง
4.1.2.2. Congestion Window Update ๐
okay so the congestion window (cwnd) update depends on whether loss happens, an ECN mark shows up, or neither ๐ซถ. The pseudocode below spells out what to do in each case โจ
on congestion event(qdelay):
# Either loss or ECN mark is detected
in_fast_increase = false
if (is loss)
# Loss is detected
cwnd = max(MIN_CWND, cwnd * BETA_LOSS)
else
# No loss, so it is then an ECN mark
cwnd = max(MIN_CWND, cwnd * BETA_ECN)
end
adjust_qdelay_target(qdelay) #compensating for competing flows
calculate_send_window(qdelay, qdelay_target)
# When no congestion event
on acknowledgement(qdelay):
update_bytes_newly_acked()
update_cwnd(bytes_newly_acked)
adjust_qdelay_target(qdelay) # compensating for competing flows
calculate_send_window(qdelay, qdelay_target)
check_to_resume_fast_increase()
The methods are explained in more detail below ๐ The cwnd update is based on qdelay, except when there's a loss event (one or more lost RTP packets in one RTT) or an ECN event, which we already covered earlier ๐ธ
Here's the pseudocode for the cwnd update ๐
update_cwnd(bytes_newly_acked):
# In fast increase mode?
if (in_fast_increase)
if (qdelay_trend >= QDELAY_TREND_TH)
# Incipient congestion detected; exit fast increase mode
in_fast_increase = false
else
# No congestion yet; increase cwnd if it
# is sufficiently used
# Additional slack of bytes_newly_acked is
# added to ensure that CWND growth occurs
# even when feedback is sparse
if (bytes_in_flight * 1.5 + bytes_newly_acked > cwnd)
cwnd = cwnd + bytes_newly_acked
end
return
end
end
# Not in fast increase mode
# off_target calculated as with LEDBAT
off_target_t = (qdelay_target - qdelay) / qdelay_target
gain_t = GAIN
# Adjust congestion window
cwnd_delta_t =
gain_t * off_target_t * bytes_newly_acked * MSS / cwnd
if (off_target_t > 0 &&
bytes_in_flight * 1.25 + bytes_newly_acked <= cwnd)
# No cwnd increase if window is underutilized
# Additional slack of bytes_newly_acked is
# added to ensure that CWND growth occurs
# even when feedback is sparse
cwnd_delta_t = 0;
end
# Apply delta
cwnd += cwnd_delta_t
# limit cwnd to the maximum number of bytes in flight
cwnd = min(cwnd, max_bytes_in_flight *
MAX_BYTES_IN_FLIGHT_HEAD_ROOM)
cwnd = max(cwnd, MIN_CWND)
cwnd gets updated differently depending on whether or not the congestion control is in fast increase mode, which is tracked by the variable in_fast_increase ๐ฆ
When it's in fast increase mode, the cwnd grows by the number of newly acknowledged bytes, as long as the window is sufficiently used ๐ Sparse feedback can slow cwnd growth down, so some extra slack (equal to the number of newly acknowledged bytes) gets added ๐ง
When in_fast_increase is false, the cwnd growth is decided by how qdelay compares to qdelay_target, and growth gets limited if the window isn't used enough ๐
SCReAM works out the GAIN in a similar way to [RFC6817] ๐ The difference is that [RFC6817] says the CWND increase is also limited by an extra function controlled by a constant called ALLOWED_INCREASE. This specification removes that extra limit ๐ โโ๏ธ
The CWND is also capped by max_bytes_in_flight and MIN_CWND ๐ Limiting the cwnd by the maximum number of bytes in flight over the last 5 seconds (max_bytes_in_flight) helps avoid overestimating throughput after things like idle periods ๐ And MAX_BYTES_IN_FLIGHT_HEAD_ROOM gives some slack so the cwnd can handle a bit of variability in the media coder's output rate ๐
4.1.2.3. Competing Flows Compensation ๐ธ
It's pretty likely that a SCReAM flow will have to share congested bottlenecks with flows using more aggressive congestion control algorithms (like big FTP flows using loss-based congestion control) ๐ญ The worst case is when the bottleneck queues are tail-drop with a large buffer size ๐ SCReAM handles these situations by adjusting the qdelay_target when loss-based flows are detected, as shown in the pseudocode below โจ
adjust_qdelay_target(qdelay)
qdelay_norm_t = qdelay / QDELAY_TARGET_LOW
update_qdelay_norm_history(qdelay_norm_t)
# Compute variance
qdelay_norm_var_t = VARIANCE(qdelay_norm_history(200))
# Compensation for competing traffic
# Compute average
qdelay_norm_avg_t = AVERAGE(qdelay_norm_history(50))
# Compute upper limit to target delay
new_target_t = qdelay_norm_avg_t + sqrt(qdelay_norm_var_t)
new_target_t *= QDELAY_TARGET_LO
if (loss_event_rate > 0.002)
# Packet losses detected
qdelay_target = 1.5 * new_target_t
else
if (qdelay_norm_var_t < 0.2)
# Reasonably safe to set target qdelay
qdelay_target = new_target_t
else
# Check if target delay can be reduced; this helps prevent
# the target delay from being locked to high values forever
if (new_target_t < QDELAY_TARGET_LO)
# Decrease target delay quickly, as measured queuing
# delay is lower than target
qdelay_target = max(qdelay_target * 0.5, new_target_t)
else
# Decrease target delay slowly
qdelay_target *= 0.9
end
end
end
# Apply limits
qdelay_target = min(QDELAY_TARGET_HI, qdelay_target)
qdelay_target = max(QDELAY_TARGET_LO, qdelay_target)
Two temporary variables get calculated here ๐ง qdelay_norm_avg_t is the long-term average queue delay, and qdelay_norm_var_t is the long-term variance of the queue delay ๐ A high qdelay_norm_var_t means the queue delay is jumping around, which can mean the bottleneck bandwidth dropped or a competing flow just showed up ๐ So it's not safe to adjust the queue delay target right then ๐
A low qdelay_norm_var_t means the queue delay is pretty stable ๐งโโ๏ธ That could be because the queue delay is low, but it could also be a competing flow pushing the bottleneck to the point where packet losses start, in which case the queue delay stays relatively high for longer ๐ซ
The queue delay target is allowed to go up if either the loss event rate is above a given threshold or qdelay_norm_var_t is low ๐ฅ Both of those conditions hint that a competing flow might be around ๐ต๏ธโโ๏ธ In every other case, the queue delay target goes down ๐
The function that adjusts qdelay_target is simple, so it can give false positives and false negatives ๐ If the algorithm mistakes its own self-inflicted congestion for competing loss-based FTP flows, that's a false positive. The opposite, where it fails to notice a competing FTP flow, is a false negative ๐ฌ
Extensive simulations showed the algorithm performs well in LTE test cases, and it also does well in simple bandwidth-limited bottleneck test cases with competing FTP flows ๐ Still, the algorithm could potentially fail, and that can't be ruled out completely ๐ฅบ A false positive (where self-inflicted congestion gets wrongly identified as competing flows) is especially problematic when it leads to raising the target queue delay, because that can make the end-to-end delay jump up dramatically ๐จ
If it's deemed unlikely that competing flows share the same bottleneck, the algorithm described in this section MAY be turned off ๐ท One example is QoS-enabled bearers in 3GPP-based access such as LTE ๐ฑ But when sending over the Internet, the network conditions are often not known for sure, so it's generally not possible to make safe assumptions about how the network is being used or whether competing flows share the same bottleneck ๐ So turning this algorithm off must be considered with caution, because it can lead to basically zero throughput when competing with loss-based traffic โ ๏ธ
Turning this algorithm off without thinking can leave you with basically zero throughput if loss-based traffic is competing with you. Think carefully first, bestie ๐
4.1.2.4. Lost Packet Detection ๐ต๏ธโโ๏ธ
Lost packet detection is based on the received sequence number list ๐ A reordering window SHOULD be applied so that packet reordering doesn't trigger loss events ๐ The reordering window is specified as a time unit, which is similar to the ideas behind Recent ACKnowledgement (RACK) [RACK] ๐ซ Computing the reordering window is possible thanks to a lost flag in the list of transmitted RTP packets ๐ท๏ธ This flag gets set if the received sequence number list shows that a given RTP packet is missing ๐โโ๏ธ If later feedback shows that a previously lost, marked packet was actually received, the reordering window gets updated to reflect the reordering delay โฑ๏ธ The reordering window is the difference in time between the event where the packet was marked as lost and the event where it was indicated as successfully received ๐งฎ Loss is detected if a given RTP packet isn't acknowledged within a time window (the reordering window) after an RTP packet with a higher sequence number was acknowledged ๐ โจ
4.1.2.5. Send Window Calculation โจ
Okay bestie, the core SCReAM sending rule is giving simple: only send when the bytes in flight are less than the congestion window ๐ซถ. But this strict rule doesn't work optimally, and there are two reasons why ๐ญ:
- Bitrate variations: Video encoders and other media sources usually produce frames whose size varies to a larger or smaller extent. The RTP queue soaks up those natural variations ๐. But the RTP queue should be as short as possible so the end-to-end delay doesn't climb ๐. So the media rate control takes the RTP queue size into account when it computes the target bitrate. A strict "send only when bytes in flight is less than the congestion window" rule can make the RTP queue grow just because the send window is limited, and that can push the target bitrate down ๐. The congestion window is only allowed to increase when there's enough data in flight, so it won't increase, or will increase very slowly ๐ข. The final result is that the media bitrate increases very slowly or not at all ๐.
- Reverse (feedback) path congestion: Especially when you're sending over buffer-bloated networks, the one-way delay in the reverse direction can jump because of congestion ๐. That delays the acknowledgements and temporarily halts the self-clocking, even though the forward path isn't congested ๐ค.
So the send window gets adjusted based on qdelay, how qdelay compares to the qdelay target, and the relationship between the congestion window and the bytes in flight ๐
. A strict rule kicks in when qdelay is higher than qdelay_target, so the queue in the network doesn't keep building ๐ง. When qdelay is lower than qdelay_target, a more relaxed rule applies. That lets the bitrate ramp up fast when no congestion is detected, while still behaving stably when things get congested ๐.
The send window is given by the relationship between the adjusted congestion window and the bytes in flight, following the pseudocode below ๐ง.
calculate_send_window(qdelay, qdelay_target)
# send window is computed differently depending on congestion level
if (qdelay <= qdelay_target)
send_wnd = cwnd + MSS - bytes_in_flight
else
send_wnd = cwnd - bytes_in_flight
end
The send window gets updated every time an RTP packet is transmitted or an RTCP feedback messaged is received ๐ฌ.
4.1.2.6. Packet Pacing ๐ฆ
Packet pacing exists to mitigate coalescing, which is when packets get sent in bursts ๐ฅ. Bursts bring increased jitter and potentially increased packet loss ๐ฌ. Pacing also helps with queue overflow caused by key-frame generation in video coders ๐ฌ. The time interval between consecutive packet transmissions is greater than or equal to t_pace, where t_pace is given by the equations below :
pace_bitrate = max (RATE_PACE_MIN, cwnd * 8 / s_rtt)
t_pace = rtp_size * 8 / pace_bitrate
Here, rtp_size is the size of the last transmitted RTP packet, and s_rtt is the smoothed round trip time โฑ๏ธ. RATE_PACE_MIN is the minimum pacing rate ๐.
4.1.2.7. Resuming Fast Increase Mode ๐
Fast increase mode can resume to speed up the bitrate increase if congestion abates ๐ธ. The condition for resuming fast increase mode (in_fast_increase = true) is that qdelay_trend stays less than QDELAY_TREND_LO for T_RESUME_FAST_INCREASE seconds or more โญ.
4.1.2.8. Stream Prioritization ๐
The SCReAM algorithm makes a really good distinction between network congestion control and media rate control ๐. This is easily extended to many streams: RTP packets from two or more RTP queues get scheduled at the rate the network congestion control permits ๐ซง.
There are a few different scheduling regimes it can use. For example, the method for coupled congestion control specified in [COUPLED-CC] can be used ๐. One implementation of SCReAM [SCReAM-CPP-implementation] uses credit-based scheduling ๐ง. In credit-based scheduling, queues accumulate credit as they wait for service, and they spend that credit while they're being serviced ๐ธ. For instance, if one queue is allowed to transmit 1000 bytes, then a credit of 1000 bytes is allocated to the other unscheduled queues ๐. This principle can be extended to weighted scheduling, where the credit allocated to unscheduled queues depends on the relative weights โ๏ธ. The latter is also implemented in [SCReAM-CPP-implementation] โจ.
4.1.3. Media Rate Control ๐
The media rate control algorithm runs at regular intervals, indicated by RATE_ADJUSTMENT_INTERVAL, except when it needs to react promptly to loss events โฐ. It works from the size of the RTP packet send queue and the loss events it observes ๐. On top of that, qdelay_trend is also considered in the media rate control, which helps reduce the amount of induced network jitter ๐งโโ๏ธ.
The media rate control's job is to strike a reasonable balance between a low amount of queuing in the RTP queue(s) and enough data to send to keep the data path busy ๐. If you set it too cautiously, you can underutilize the network capacity, and then the flow gets starved out by more opportunistic traffic ๐ฅ. But if you set it too aggressively, you get increased jitter ๐ตโ๐ซ.
The target_bitrate gets adjusted depending on the congestion state ๐ฏ. It can vary between a minimum value (TARGET_BITRATE_MIN) and a maximum value (TARGET_BITRATE_MAX) ๐. TARGET_BITRATE_MIN SHOULD be set to a low enough value to stop RTP packets from piling up when the network throughput drops ๐. The sender SHOULD also have a mechanism that discards RTP packets when the network throughput becomes very low and RTP packets are getting excessively delayed ๐๏ธ.
For the overall bitrate adjustment, two network throughput estimates get computed ๐งฎ:
- rate_transmit: The measured transmit bitrate ๐ค.
- rate_ack: The ACKed bitrate, i.e., the volume of ACKed bits per second ๐ฅ.
Both estimates get updated every 200 ms โฒ๏ธ.
The current throughput, current_rate, is computed as the maximum value of rate_transmit and rate_ack ๐ฏ. Why use rate_ack as well as rate_transmit? Because rate_transmit is also affected by how much data is available to transmit. So a lack of data to send can look like reduced throughput, and that can cause an unnecessary rate reduction ๐ฉ. To overcome this shortcoming, rate_ack gets used too, and that gives a more stable throughput estimate ๐ซถ.
The rate change behaviour depends on whether a loss or ECN event has happened, and on whether the congestion control is in fast increase mode ๐.
# The target_bitrate is updated at a regular interval according
# to RATE_ADJUST_INTERVAL
on loss:
# Loss event detected
target_bitrate = max(BETA_R * target_bitrate,
TARGET_BITRATE_MIN)
exit
on ecn_mark:
# ECN event detected
target_bitrate = max(BETA_ECN * target_bitrate,
TARGET_BITRATE_MIN)
exit
ramp_up_speed_t = min(RAMP_UP_SPEED, target_bitrate / 2.0)
scale_t = (target_bitrate - target_bitrate_last_max) /
target_bitrate_last_max
scale_t = max(0.2, min(1.0, (scale_t * 4)^2))
# min scale_t value 0.2, as the bitrate should be allowed to
# increase slowly. This prevents locking the rate to
# target_bitrate_last_max
if (in_fast_increase = true)
increment_t = ramp_up_speed_t * RATE_ADJUST_INTERVAL
increment_t *= scale_t
target_bitrate += increment_t
else
current_rate_t = max(rate_transmit, rate_ack)
# Compute a bitrate change
delta_rate_t = current_rate_t * (1.0 - PRE_CONGESTION_GUARD *
queue_delay_trend) - TX_QUEUE_SIZE_FACTOR * rtp_queue_size
# Limit a positive increase if close to target_bitrate_last_max
if (delta_rate_t > 0)
delta_rate_t *= scale_t
delta_rate_t =
min(delta_rate_t, ramp_up_speed_t * RATE_ADJUST_INTERVAL)
end
target_bitrate += delta_rate_t
# Force a slight reduction in bitrate if RTP queue
# builds up
rtp_queue_delay_t = rtp_queue_size / current_rate_t
if (rtp_queue_delay_t > RTP_QDELAY_TH)
target_bitrate *= TARGET_RATE_SCALE_RTP_QDELAY
end
end
rate_media_limit_t =
max(current_rate_t, max(rate_media, rtp_rate_median))
rate_media_limit_t *= (2.0 - qdelay_trend_mem)
target_bitrate = min(target_bitrate, rate_media_limit_t)
target_bitrate = min(TARGET_BITRATE_MAX,
max(TARGET_BITRATE_MIN, target_bitrate))
If a loss event happens, the target_bitrate gets updated and the rate change procedure is exited โ. Otherwise, the rate change procedure carries on ๐โโ๏ธ. The reasoning behind cutting the rate on loss is that a congestion window reduction is about to take effect, so cutting the rate proactively stops RTP packets from queuing up when the transmit rate drops because the congestion window shrank ๐ง . ECN events get a similar rate reduction ๐ซง.
The rate update frequency is limited by RATE_ADJUST_INTERVAL, unless a loss event happens ๐ฐ๏ธ. That value is based on experimentation, with real-life limitations in video coders taken into account [SCReAM-CPP-implementation] ๐ฅ. An interval that's too short makes the rate control loop in video coders more unstable ๐, and an interval that's too long makes the overall congestion control sluggish ๐.
In fast increase mode (in_fast_increase = true), the bitrate increase is set by the desired ramp-up speed (RAMP_UP_SPEED) ๐. The ramp-up speed gets limited when the target bitrate is low, so the rate doesn't oscillate at low bottleneck bitrates ๐ข. The setting for RAMP_UP_SPEED depends on preferences ๐ซต. A high setting, such as 1000 kbps/s, gets you high-quality media quickly ๐คฉ. The tradeoff is increased jitter, which can show up as choppy video rendering, for example ๐.
When in_fast_increase is false, the bitrate increase depends on the current bitrate, and it's also controlled by the estimated RTP queue and the qdelay trend ๐. So an increased congestion level sensed by the network congestion control is enough to limit the bitrate ๐ฆ. The target_bitrate_last_max gets updated whenever congestion is detected ๐.
Finally, the target_bitrate stays within the defined min and max values ๐.
The aware reader might notice that the target bitrate computation depends on qdelay too. This shows up in the use of qdelay_trend. These parameters are also used in the network congestion control, so you might suspect some odd interaction between the media rate control and the network congestion control ๐. And that's actually the case ifPRE_CONGESTION_GUARDis set to a high value ๐ณ. The use of qdelay_trend in the media rate control is solely to reduce jitter, and you can remove the dependency by settingPRE_CONGESTION_GUARD=0๐ซถ. The effect is a somewhat larger rate increase after congestion, at the expense of increased jitter in congested situations ๐ .
4.2. SCReAM Receiver โจ
okay so the SCReAM receiver has one super simple job ๐ it sends acknowledgements for the packets it got, plus the total ECN count, back to the SCReAM sender ๐ฆ it also echoes back the receive time of the RTP packet with the highest sequence number โฐ every time an RTP packet shows up, the receiver MUST keep enough info around to send all of that back in an RTCP transport-layer feedback message ๐ซถ how often that feedback goes out depends on how much RTCP bandwidth is available. the requirements for the feedback elements and the feedback interval are below ๐
4.2.1. Requirements on Feedback Elements
the following feedback elements are REQUIRED for basic functionality in SCReAM ๐
- A list of received RTP packets. This list SHOULD be sufficiently long to cover all received RTP packets. This list can be realized with the Loss RLE (Run Length Encoding) Report Block in [RFC3611].
- A wall-clock timestamp corresponding to the received RTP packet with the highest sequence number is required in order to compute the qdelay. This can be realized by means of the Packet Receipt Times Report Block in [RFC3611]. begin_seq MUST be set to the highest received sequence number (which has possibly wrapped around); end_seq MUST be set to begin_seq+1 modulo 65536. The timestamp clock MAY be set according to [RFC3611], i.e., equal to the RTP timestamp clock. Detailed individual packet receive times are not necessary, as SCReAM does currently not describe how they can be used. ๐
The basic feedback needed for SCReAM involves the use of the Loss RLE Report Block and the Packet Receipt Times Report Block as shown in Figure 2 ๐ธ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|reserved | PT=XR=207 | length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=2 | rsvd. | T=0 | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| begin_seq | end_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk 1 | chunk 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
: ... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| chunk n-1 | chunk n |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| BT=3 | rsvd. | T=0 | block length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SSRC of source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| begin_seq | end_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Receipt time of packet begin_seq |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 2: Basic Feedback Message for SCReAM, Based on RFC 3611
in a typical use case, no more than four Loss RLE chunks are needed, thus the feedback message will be 44 bytes. it is obvious from Figure 2 that there is a lot of redundant information in the feedback message ๐ญ a more optimized feedback format, including the additional feedback elements listed below, could reduce the feedback message size a bit.
an additional feedback element that can improve the performance of SCReAM is:
- Accumulated number of ECN-CE-marked packets (
n_ECN). For instance, this can be realized with the ECN Feedback Report Format in [RFC6679]. The given feedback report format is slightly overkill, as SCReAM would do quite well with only a counter that increments by one for each received packet with the ECN-CE codepoint set. The more bulky format could nevertheless be useful for, e.g., ECN black-hole detection. ๐ง
4.2.2. Requirements on Feedback Intensity
SCReAM benefits from relatively frequent feedback. It is RECOMMENDED that a SCReAM implementation follows the guidelines below ๐
the feedback interval depends on the media bitrate. at low bitrates, it is sufficient with a feedback interval of 100 to 400 ms; while at high bitrates, a feedback interval of roughly 20 ms is preferred. at very high bitrates, even shorter feedback intervals MAY be needed in order to keep the self-clocking in SCReAM working well. one indication that feedback is too sparse is that the SCReAM implementation cannot reach high bitrates, even in uncongested links. more frequent feedback might solve this issue โจ
the numbers above can be formulated as a feedback interval function that can be useful for the computation of the desired RTCP bandwidth. the following equation expresses the feedback rate:
rate_fb = min(50, max(2.5, rate_media / 10000))
rate_media is the RTP media bitrate expressed in bps; rate_fb is the feedback rate expressed in packets/s. converting to feedback interval, we get:
fb_int = 1.0 / min(50, max(2.5, rate_media / 10000))
the transmission interval is not critical ๐ซง so, in the case of multi-stream handling between two hosts, the feedback for two or more synchronization sources (SSRCs) can be bundled to save UDP/IP overhead. however, the final realized feedback interval SHOULD not exceed 2*fb_int in such cases, meaning that a scheduled feedback transmission event should not be delayed more than fb_int.
SCReAM works with AVPF regular mode; immediate or early mode is not required by SCReAM but can nonetheless be useful for RTCP messages not directly related to SCReAM, such as those specified in [RFC4585]. It is RECOMMENDED to use reduced-size RTCP [RFC5506], where regular full compound RTCP transmission is controlled by trr-int as described in [RFC4585] ๐
5. Discussion โจ
this section covers a few discussion points ๐ฌ
- Clock drift: SCReAM can suffer from the same issues with clock drift as is the case with LEDBAT [RFC6817]. However, Appendix A.2 in [RFC6817] describes ways to mitigate issues with clock drift.
- Support for alternate ECN semantics: This specification adopts the proposal in [ALT-BACKOFF] to reduce the congestion window less when ECN-based congestion events are detected. Future work on Low Loss, Low Latency for Scalable throughput (L4S) may lead to updates in a future document that describes SCReAM support for L4S.
- A new transport-layer feedback message (as specified in RFC 4585) could be standardized if the use of the already existing RTCP extensions as described in Section 4.2 is not deemed sufficient.
- The target bitrate given by SCReAM is the bitrate including the RTP and Forward Error Correction (FEC) overhead. The media encoder SHOULD take this overhead into account when the media bitrate is set. This means that the media coder bitrate SHOULD be computed as
media_rate = target_bitrate - rtp_plus_fec_overhead_bitrate
It is not necessary to make a 100% perfect compensation for the overhead, as the SCReAM algorithm will inherently compensate for moderate errors. Under-compensating for the overhead has the effect of increasing jitter, while overcompensating will cause the bottleneck link to become underutilized ๐
6. Suggested Experiments ๐งช
SCReAM has been evaluated in a number of different ways, mostly in a simulator. The OpenWebRTC implementation work ([OpenWebRTC] and [SCReAM-implementation]) involved extensive testing with artificial bottlenecks with varying bandwidths and using two different video coders (OpenH264 and VP9) ๐
Preferably, further experiments will be done by means of implementation in real clients and web browsers. RECOMMENDED experiments are:
- Trials with various access technologies: EDGE/3G/4G, Wi-Fi, DSL. Some experiments have already been carried out with LTE access; see [SCReAM-CPP-implementation] and [SCReAM-implementation-experience].
- Trials with different kinds of media: Audio, video, slideshow content. Evaluation of multi-stream handling in SCReAM.
- Evaluation of functionality of the compensation mechanism when there are competing flows: Evaluate how SCReAM performs with competing TCP-like traffic and to what extent the compensation for competing flows causes self-inflicted congestion.
- Determine proper parameters: A set of default parameters are given that makes SCReAM work over a reasonably large operation range. However, for very low or very high bitrates, it may be necessary to use different values for the
RAMP_UP_SPEED, for instance. - Experimentation with further improvements to the congestion window and media bitrate calculation. [SCReAM-CPP-implementation] implements some optimizations, not described in this memo, that improve performance slightly. Further experiments are likely to lead to more optimizations of the algorithm.
7. IANA Considerations ๐ง
this document does not require any IANA actions ๐ซถ
8. Security Considerations ๐จ
the feedback can be vulnerable to attacks similar to those that can affect TCP. It is therefore RECOMMENDED that the RTCP feedback is at least integrity protected. Furthermore, as SCReAM is self-clocked, a malicious middlebox can drop RTCP feedback packets and thus cause the self-clocking in SCReAM to stall. However, this attack is mitigated by the minimum send rate maintained by SCReAM when no feedback is received. ๐
9. References ๐
9.1. Normative References โจ
Okay, these are the big ones bestie, the docs this spec straight up leans on ๐ซถ Normative means you really need them to get the whole picture ๐
[RFC2119]Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997,https://www.rfc-editor.org/info/rfc2119.[RFC3550]Schulzrinne, H., Casner, S., Frederick, R., and V. Jacobson, "RTP: A Transport Protocol for Real-Time Applications", STD 64, RFC 3550, DOI 10.17487/RFC3550, July 2003,https://www.rfc-editor.org/info/rfc3550.[RFC3611]Friedman, T., Ed., Caceres, R., Ed., and A. Clark, Ed., "RTP Control Protocol Extended Reports (RTCP XR)", RFC 3611, DOI 10.17487/RFC3611, November 2003,https://www.rfc-editor.org/info/rfc3611.[RFC4585]Ott, J., Wenger, S., Sato, N., Burmeister, C., and J. Rey, "Extended RTP Profile for Real-time Transport Control Protocol (RTCP)-Based Feedback (RTP/AVPF)", RFC 4585, DOI 10.17487/RFC4585, July 2006,https://www.rfc-editor.org/info/rfc4585.[RFC5506]Johansson, I. and M. Westerlund, "Support for Reduced-Size Real-Time Transport Control Protocol (RTCP): Opportunities and Consequences", RFC 5506, DOI 10.17487/RFC5506, April 2009,https://www.rfc-editor.org/info/rfc5506.[RFC6298]Paxson, V., Allman, M., Chu, J., and M. Sargent, "Computing TCP's Retransmission Timer", RFC 6298, DOI 10.17487/RFC6298, June 2011,https://www.rfc-editor.org/info/rfc6298.[RFC6817]Shalunov, S., Hazel, G., Iyengar, J., and M. Kuehlewind, "Low Extra Delay Background Transport (LEDBAT)", RFC 6817, DOI 10.17487/RFC6817, December 2012,https://www.rfc-editor.org/info/rfc6817.[RFC8174]Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017,https://www.rfc-editor.org/info/rfc8174.
9.2. Informative References ๐ฆ
These ones are the extra reading, the background, the experiments and the implementations ๐ Super useful if you want to go deeper, no cap ๐ธ
[ALT-BACKOFF]Khademi, N., Welzl, M., Armitage, G., and G. Fairhurst, "TCP Alternative Backoff with ECN (ABE)", Work in Progress, draft-ietf-tcpm-alternativebackoff-ecn-04, November 2017.[COUPLED-CC]Islam, S., Welzl, M., and S. Gjessing, "Coupled congestion control for RTP media", Work in Progress, draft-ietf-rmcat-coupled-cc-07, September 2017.[LEDBAT-delay-impact]Ros, D. and M. Welzl, "Assessing LEDBAT's Delay Impact", IEEE Communications Letters, Vol. 17, No. 5, DOI 10.1109/LCOMM.2013.040213.130137, May 2013,http://home.ifi.uio.no/michawe/research/publications/ledbat-impact-letters.pdf.[OpenWebRTC]Ericsson Research, "OpenWebRTC",http://www.openwebrtc.org.[Packet-conservation]Jacobson, V., "Congestion Avoidance and Control", ACM SIGCOMM Computer Communication Review, DOI 10.1145/52325.52356, August 1988.[QoS-3GPP]3GPP, "Policy and charging control architecture", 3GPP TS 23.203, July 2017,http://www.3gpp.org/ftp/specs/archive/23_series/23.203/.[RACK]Cheng, Y., Cardwell, N., and N. Dukkipati, "RACK: a time-based fast loss detection algorithm for TCP", Work in Progress, draft-ietf-tcpm-rack-02, March 2017.[RFC6679]Westerlund, M., Johansson, I., Perkins, C., O'Hanlon, P., and K. Carlberg, "Explicit Congestion Notification (ECN) for RTP over UDP", RFC 6679, DOI 10.17487/RFC6679, August 2012,https://www.rfc-editor.org/info/rfc6679.[RFC7478]Holmberg, C., Hakansson, S., and G. Eriksson, "Web Real-Time Communication Use Cases and Requirements", RFC 7478, DOI 10.17487/RFC7478, March 2015,https://www.rfc-editor.org/info/rfc7478.[RFC7661]Fairhurst, G., Sathiaseelan, A., and R. Secchi, "Updating TCP to Support Rate-Limited Traffic", RFC 7661, DOI 10.17487/RFC7661, October 2015,https://www.rfc-editor.org/info/rfc7661.[SCReAM-CPP-implementation]Ericsson Research, "SCReAM - Mobile optimised congestion control algorithm",https://github.com/EricssonResearch/scream.[SCReAM-implementation]Ericsson Research, "OpenWebRTC specific GStreamer plugins",https://github.com/EricssonResearch/openwebrtc-gst-plugins.[SCReAM-implementation-experience]Sarker, Z. and I. Johansson, "Updates on SCReAM: An implementation experience", November 2015,https://www.ietf.org/proceedings/94/slides/slides-94-rmcat-8.pdf.[TFWC]Choi, S. and M. Handley, "Fairer TCP-Friendly Congestion Control Protocol for Multimedia Streaming Applications", DOI 10.1145/1364654.1364717, December 2007,http://www-dept.cs.ucl.ac.uk/staff/M.Handley/papers/tfwc-conext.pdf.[WIRELESS-TESTS]Sarker, Z., Johansson, I., Zhu, X., Fu, J., Tan, W., and M. Ramalho, "Evaluation Test Cases for Interactive Real-Time Media over Wireless Networks", Work in Progress, draft-ietf-rmcat-wireless-tests-04, May 2017.
Acknowledgements ๐
Huge thank-yous to everyone who helped make this happen ๐ซถ Markus Andersson, Bo Burman, Tomas Frankkila, Frederic Gabin, Laurits Hamm, Hans Hannu, Nikolas Hermanns, Stefan Haakansson, Erlendur Karlsson, Daniel Lindstroem, Mats Nordberg, Jonathan Samuelsson, Rickard Sjoeberg, Robert Swain, Magnus Westerlund, and Stefan Aalund, you're all iconic ๐ for the comments, questions, and support during the work that led to this memo.
Many, many more thanks to the RMCAT chairs, Karen E. E. Nielsen and Mirja Kuehlewind ๐ They patiently read everything, suggested improvements, and kept asking all the difficult but necessary questions, which is literally the best kind of friend energy ๐ญ
Thanks also to Stefan Holmer, Xiaoqing Zhu, Safiqul Islam, and David Hayes for the additional review of this document โจ Thanks to Ralf Globisch for taking time to try out SCReAM in his challenging low-bitrate use cases ๐ Thanks to Robert Hedman for finding a few additional flaws in the running code ๐ And thanks to Gustavo Garcia and 'miseri' for code contributions ๐
Authors' Addresses ๐
Ingemar Johansson
Ericsson AB
Laboratoriegraend 11
Luleaa 977 53
Sweden
Phone: +46 730783289 Email: ingemar.s.johansson@ericsson.com
Zaheduzzaman Sarker
Ericsson AB
Laboratoriegraend 11
Luleaa 977 53
Sweden
Phone: +46 761153743 Email: zaheduzzaman.sarker@ericsson.com
๐ฆ where this page comes from
- ietf: rfc8298 2017-12
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms