Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
118 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# Custom Congestion Control Use Case | ||
|
||
## Use case description | ||
|
||
In this use case, congestion control can be done by the application, but by doing custom bandwidth estimation and custom pacing and probing. | ||
|
||
## Goals | ||
|
||
Enable applications to do custom bandwidth estimation by enabling them to: | ||
- Have access to information about when RTP packets are sent and how large they are. | ||
- Have access to information about when congestion control feedback (ack messages) are received, which per-packet information about when they were received. | ||
- Have access to information used by L4S. | ||
- Know when a packet sent by the application is not sent, and why. | ||
- Efficiently control when packets are sent, in order to do custom pacing and probing. | ||
|
||
## Key use-cases | ||
|
||
|
||
## API Outline | ||
|
||
|
||
```javascript | ||
partial interface RtpSendStream { | ||
Promise<RtpSendResult> sendRtp(RtpPacketInit packet, RtpSendOptions options); | ||
} | ||
|
||
dictionary RtpSendOptions { | ||
DOMHighResTimeStamp sendTime; | ||
} | ||
|
||
interface RtpSendResult { | ||
readonly attribute RtpSent sent?; | ||
readonly attribute RtpUnsentReason unsent?; | ||
} | ||
|
||
|
||
interface RtpSent { | ||
readonly attribute DOMHighResTimeStamp time; | ||
|
||
// Can be correlated with acks | ||
readonly attribute unsigned long long ackId?; | ||
readonly attribute unsigned long long size; | ||
} | ||
|
||
enum RtpUnsentReason { | ||
"overuse", | ||
"transport-unavailable", | ||
}; | ||
|
||
partial interface RtpTransport { | ||
attribute EventHandler onrtpsent; // RtpSent | ||
attribute EventHandler onrtpacksreceived; // RtpAcks | ||
} | ||
|
||
|
||
// RFC 8888 or Transport-cc feedback | ||
interface RtpAcks { | ||
readonly attribute sequence<RtpAck> acks; | ||
readonly attribute unsigned long long remoteSendTimestamp; | ||
readonly attribute DOMHighResTimeStamp receivedTime; | ||
readonly attribute ExplicitCongestionNotification explicitCongestionNotification; // AKA "ECN" | ||
} | ||
|
||
interface RtpAck { | ||
// Correlated with RtpSent.ackId | ||
readonly attribute unsigned long long ackId; | ||
readonly attribute unsigned long long remoteReceiveTimestamp; | ||
} | ||
|
||
// See RFC 3991 and RFC 3168 | ||
enum ExplicitCongestionNotification { | ||
"unset", // AKA "Not-ECT"; Bits: 00 | ||
"scalable-congestion-not-experienced", // AKA "ECT(1)" or "Scalable" or "L4S" ; Bits: 01 | ||
"classic-congestion-not-experienced", // AKA "ECT(0)" or "Classic" or "not L4S"; Bits: 10 | ||
"congestion-experienced" // AKA "CE" or "ECN-marked" or "marked"; Bits: 11 | ||
} | ||
``` | ||
## Proposed solutions | ||
## Example: Custom BWE | ||
```javascript | ||
const [pc, rtpTransport] = setupPeerConnectionWithRtpTransport(); // Custom | ||
const estimator = createBandwidthEstimator(); // Custom | ||
rtpTransport.onrtpsent = (rtpSent) => { | ||
if (rtpSent.ackId) { | ||
estimator.rememberRtpSent(rtpSent); | ||
} | ||
} | ||
rtpTransport.onrtpacksreceived = (rtpAcks) => { | ||
for (const rtpAck in rtpAcks.acks) { | ||
const bwe = estimator.processReceivedAcks(rtpAck); | ||
doBitrateAllocationAndUpdateEncoders(bwe); // Custom | ||
} | ||
} | ||
|
||
``` | ||
## Example: Custom Pacing and Probing | ||
```javascript | ||
const [pc, rtpSender1, rtpSender2] = setupPeerConnectionWithRtpSenders(); // Custom | ||
const pacer = createPacer(); // Custom | ||
while (true) { | ||
// Packets are queued by using pacer.sendRtp(rtpSender, rtpPacket) instead of rtpSender.sendRtp(rtpPacket) | ||
const [rtpSender, packet, sendTime] = await pacer.dequeue(); // Custom | ||
const rtpSent = rtpSender.sendRtp(packet, {sendTime: sendTime}); | ||
(async () => { | ||
pacer.handleSent(await rtpSent); | ||
})(); | ||
} | ||
``` | ||
## Alternative designs considered | ||