Skip to content

Commit 9211353

Browse files
committed
Add explainer for congestion control
This rescues the proposal for congestion control that was removed from the PR that became the keyframe request indication interface. Partial fix for #90
1 parent c631e72 commit 9211353

File tree

1 file changed

+153
-0
lines changed

1 file changed

+153
-0
lines changed

explainer-congestion-control-api.md

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Congestion Control API
2+
3+
This document describes how the congestion control API is expected to work.
4+
5+
The CC API can be applied multiple places in the WebRTC model; the immediate
6+
application is in the Encoded Transform API.
7+
8+
9+
The role of a congestion control API is to inform upstream entities about
10+
reasonable restrictions on how much data they can send downstream. The
11+
expectation from the client should be that as long as they stay within the
12+
indicated restriction, the chance of data loss from buffer overflows should be
13+
reasonably low. The given limits are not a guarantee - either that no packets
14+
will be lost, or that packets will be lost if more than the indicated amount is
15+
sent.
16+
17+
## Representing congestion control status
18+
19+
The following structure represents a congestion control status:
20+
```
21+
interface BandwidthInfo {
22+
readonly attribute double allocatedBitrate; // bits per second
23+
readonly attribute double availableBitrate;
24+
readonly attribute boolean writable;
25+
readonly attribute long maxMessageSize; // bytes
26+
};
27+
```
28+
The meaning of the fields is as follows:
29+
30+
* allocatedBitrate: The recommended amount of data for the upstream entity to
31+
send. By staying below this bitrate, it is likely that packet loss will be
32+
low, and that the bandwidth is fairly shared with other users.
33+
* availableBitrate: The total available bitrate estimate for the
34+
downstream link. Exceeding this bitrate will have a high probability of packet
35+
loss.
36+
* writable: Whether or not a single write of a single message of up to
37+
maxMessageSize will succeed. If it is false, downstream buffers are known to
38+
be too full to guarantee success at the time that the information is given.
39+
* maxMessageSize: The size (in bytes) of the largest message that can safely be
40+
written to the downstream interface.
41+
42+
For the bitrates, an unconstrained status is indicated by a value of positive
43+
infinity.
44+
45+
46+
## Receiving information about downstream congestion control
47+
48+
A downstream interface is expected to expose the following mixin:
49+
```
50+
interface mixin CongestionControlledSink {
51+
readonly attribute BandwidthInfo bandwidthInfo;
52+
}
53+
```
54+
It is reasonable to read this attribute whenever a send operation has completed,
55+
or just before a send operation is attempted.
56+
57+
If the bandwidthInfo.writable is false, the write operation SHOULD be aborted.
58+
59+
## Sending information upstream about congestion control state
60+
An upstream interface is expected to expose the following mixin:
61+
```
62+
interface mixin CongestionControlledSource {
63+
undefined sendBandwidthInfo(BandwidthInfo bandwidthInfo);
64+
}
65+
```
66+
This can be called whenever the consumer of the upstream interface deems that
67+
there has been significant change to the congestion control state it wishes the
68+
upstream source to conform to.
69+
70+
Examples of significant changes are:
71+
* The value of "writable" has changed
72+
* The value of "allocatedBitrate" or "availableBitrate" has decreased
73+
* The value of "allocatedBitrate" or "availableBitrate" has increased
74+
significantly
75+
76+
"Significantly" varies by application.
77+
78+
## Code examples
79+
80+
These examples all assume that RTCRtpScriptTransformer is extended with both
81+
mixins above.
82+
83+
### Transform that doubles message sizes if there's room enough
84+
This might, for example, be a RED type redundancy data appender.
85+
86+
```
87+
function relayBandwidth(transformer) {
88+
const newBandwidthInfo = transformer.bandwidthInfo;
89+
newBandwidthInfo.allocatedBitrate =
90+
newBandwidthInfo.allocatedBitrate / 2;
91+
transformer.sendBandwidthinfo(newBandwidthInfo);
92+
}
93+
94+
onrtctransform = function(transformerEvent) {
95+
transformer = transformerEvent.transformer;
96+
const transform = new TransformStream({
97+
async transform(encodedFrame, controller) {
98+
if (encodedFrame.data.byteLength <
99+
transformer.bandwidthInfo.maxMessageSize / 2) {
100+
doDataSizeDoubling(encodedFrame.data);
101+
}
102+
if (transformer.bandwidthInfo.writable) {
103+
controller.enqueue(encodedFrame);
104+
}
105+
// Tell upstream about the new half bitrate
106+
relayBandwidth(transformer);
107+
}
108+
});
109+
relayBandwidth(transformer);
110+
transformer.sendBandwidthInfo(newbandwidthInfo);
111+
transformer.readable.pipeThrough(transform)
112+
.pipeTo(transformer.writable);
113+
}
114+
```
115+
116+
### Transform that allocates half the available bandwith to "important"
117+
118+
```
119+
function relayBandwidth(transformer, important) {
120+
if (important) {
121+
const newBandwidthInfo = transformer.bandwidthInfo;
122+
newBandwidthInfo.allocatedBitrate = newBandwidthInfo.availableBitrate / 2;
123+
transformer.sendBandwidthinfo(newBandwidthInfo);
124+
} else {
125+
// Do a fair division of everyone else's bandwidth
126+
}
127+
}
128+
129+
function isImportant(transformerEvent) {
130+
// decide if it's important; if so return true.
131+
return false;
132+
}
133+
134+
onrtctransform = function(transformerEvent) {
135+
transformer = transformerEvent.transformer;
136+
const transform = new TransformStream({
137+
important: isImportant(transformerEvent),
138+
async transform(encodedFrame, controller) {
139+
if (transformer.bandwidthInfo.writable) {
140+
controller.enqueue(encodedFrame);
141+
}
142+
// Tell upstream about the new half bitrate
143+
relayBandwidth(transformer, important);
144+
}
145+
});
146+
relayBandwidth(transformer, transform.important);
147+
transformer.sendBandwidthInfo(newbandwidthInfo);
148+
transformer.readable.pipeThrough(transform)
149+
.pipeTo(transformer.writable);
150+
}
151+
```
152+
153+

0 commit comments

Comments
 (0)