Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add use case 3 for custom NACK/RTX #59

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion api-outline.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ dictionary RTCRtpHeaderExtensionInit {
required AllowSharedBufferSource value;
}

interface RTCRtcpPacket {
readonly attribute sequence<RTCRtcpNack> nacks;
}

```
### RTCPeerConnection, RTCRtpSendStream, RTCRtpReceiveStream Extensions

Expand All @@ -60,6 +64,11 @@ dictionary RTCConfiguration {
// and I will send them."
// TODO: Think of a better name
bool customPacer;
// Means "continue to encode and packetize RTCP NACK, but don't send them.
// Instead give them to me via onpacketizedrtcpavailable/readPacketizedRtcp
// and I will send them."
// TODO: Think of a better name
bool customNack;
}

partial interface RTCRtpSender {
Expand All @@ -81,6 +90,8 @@ interface RTCRtpTransport {
attribute EventHandler onrtpacksreceived; // RtpAcks
attribute EventHandler onpacketizedrtpavailable; // No payload. Call readPacketizedRtp
sequence<RTCRtpPacket> readPacketizedRtp(maxNumberOfPackets);
attribute EventHandler onpacketizedrtcpavailable; // No payload. Call readPacketizedRtcp
sequence<RTCRtcpPacket> readPacketizedRtcp(maxNumberOfPackets);

readonly attribute unsigned long bandwidthEstimate; // bps
readonly attribute unsigned long allocatedBandwidth; // bps
Expand Down Expand Up @@ -124,6 +135,9 @@ interface RTCRtpSendStream {
attribute EventHandler onpacketizedrtp;
sequence<RTCRtpPacket> readPacketizedRtp(long maxNumberOfPackets);

attribute EventHandler onreceivedrtcpnacks;
sequence<RTCRtcpNack> readReceivedRtcpNacks(long maxNumberOfPackets);

// https://github.com/w3c/webrtc-rtptransport/issues/32
void sendRtp(RTCRtpPacket packet);
Promise<RTCRtpSendResult> sendRtp(RTCRtpPacketInit packet, RTCRtpSendOptions options);
Expand Down Expand Up @@ -152,6 +166,13 @@ enum RTCRtpUnsentReason {

dictionary RTCRtpSendOptions {
DOMHighResTimeStamp sendTime;
// Serializes the first two bytes of the RTP payload as an RTX payload
// and sends using the RTX ssrc.
bool asRtx;
}

interface RTCRtcpNack {
readonly attribute sequence<unsigned short> sequenceNumbers;
}

[Exposed=(Window,Worker), Transferable]
Expand All @@ -164,6 +185,8 @@ interface RTCRtpReceiveStream {
attribute EventHandler onreceivedrtp;
sequence<RTCRtpPacket> readReceivedRtp(long maxNumberOfPackets);

void receiveRtp(RTCRtpPacket packet)
void receiveRtp(RTCRtpPacket packet);

void sendNack(sequence<unsigned short>);
}
```
4 changes: 2 additions & 2 deletions explainer-use-case-1.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ setInterval(() => {
}, 1000);
```

## Example 13: Receive with BYOB
### Example 13: Receive with BYOB
```javascript
const [pc, videoRtpReceiver] = await setupPeerConnectionWithRtpReceiver(); // Custom
const videoRtpReceiveStream = await videoRtpReceiver.replaceReceiveStreams()[0]; // Custom
Expand All @@ -274,7 +274,7 @@ videoRtpReceiveStream.onrtpreceived = () => {
};
```

## Example 14: Packetize with BYOB
### Example 14: Packetize with BYOB
```javascript
const [pc, videoRtpSender] = await setupPeerConnectionWithRtpSender(); // Custom
const videoRtpSendStream = await videoRtpSender.replaceSendStreams()[0];
Expand Down
6 changes: 3 additions & 3 deletions explainer-use-case-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Applications can do custom bandwidth estimation via:

## Examples

## Example 1: Custom BWE
### Example 1: Custom BWE

```javascript
const [pc, rtpTransport] = setupPeerConnectionWithRtpTransport(); // Custom
Expand All @@ -40,7 +40,7 @@ rtpTransport.onrtpacksreceived = (rtpAcks) => {

```

## Example 2: Custom Pacing and Probing
### Example 2: Custom Pacing and Probing

```javascript
const [pc, rtpTransport] = setupPeerConnectionWithRtpTransport({customPacer: true}); // Custom
Expand All @@ -59,7 +59,7 @@ while (true) {
}
```

## Example 3: Batched pacing
### Example 3: Batched pacing
Making use of the synchronous readPacketizedRtp method to only read packets in batches
at a controlled frequency.

Expand Down
160 changes: 160 additions & 0 deletions explainer-use-case-3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Custom RTX Use Case

## Motivation

Advanced web applications wish to control NACK and RTX behavior, such as being able to control how large the RTX packet cache is, or how long packets stay in the cache.

## Examples

### Example 1a: Custom RTX packet cache (per-stream and parsed)

```javascript
const [pc, rtpTransport, rtpSender] = setupPeerConnectionWithRtpTransport(); // Custom
const rtxPacketCache = createRtxPacketCache(); // Custom
const rtpSendStream = await rtpSender.replaceSendStreams()[0];
rtpSendStream.onpacketizedrtp = () => {
const rtpPackets = rtpSendStream.readPacketizedRtp();
for (const rtpPacket of rtpPackets) {
rtxPacketCache.cacheRtpPacket(rtpPacket);
// CON: Need sendRtp
rtpSendStream.sendRtp(rtpPacket);
}
};
// TODO: How do we disable normal NACK processing?
rtpSendStream.onreceivedrtcpnacks = () => {
// NEW: readReceivedRtcpNacks and Nack.sequenceNumbers
const nacks = rtpSendStream.readReceivedRtcpNacks();
for (const nack of nacks) {
for (const seqnum of nack.sequenceNumbers) {
const cachedRtpPacket = rtxPacketCache.getCachedRtpPacketBySequenceNumber(seqnum);
if (cachedRtpPacket) {
// NEW: asRtx
rtpSendStream.sendRtp(cachedRtpPacket, {asRtx: true});
}
}
}
}
```

### Example 1b: Custom RTX packet cache (cross-RtpTransport and parsed)

```javascript
const [pc, rtpTransport] = setupPeerConnectionWithRtpTransport(); // Custom
const rtxPacketCache = createRtxPacketCache(); // Custom
rtpTransport.onrtpsent = () => {
// NEW: RtpTransport.readSentRtp (make RtpTransport.onrtpsent batch-style)
const sentRtps = rtpTransport.readSentRtp();
for (const sentRtp of sentRtps) {
// NEW: SentRtp.packet (make the existing object include the whole RtpPacket, not just timestamps)
rtxPacketCache.cacheRtpPacket(sentRtp.packet);
// PRO: No sendRtp required
}
};
// TODO: How do we disable normal NACK processing?
// NEW: RtpTransport.onreceivedrtcp (a whole new thing)
rtpTransport.onreceivedrtcp = () => {
// NEW: RtpTransport.readReceivedRtcp (batched part of RtpTransport.onrecievedrtcp)
// NEW: RtcpPacket.nacks and Nack.sequenceNumbers and Nack.ssrc
const rtcp = rtpTransport.readReceivedRtcp();
for (const nack of rtcp.nacks) {
for (const seqnum of nack.sequenceNumbers) {
const cachedRtpPacket = rtxPacketCache.getCachedRtpPacket(nack.ssrc, seqnum);
if (cachedRtpPacket) {
// NEW: asRtx
rtpSendStream.sendRtp(cachedRtpPacket, {asRtx: true});
}
}
}
}
```

### Example 1c: Custom RTX packet cache (cross-RtpTransport and unparsed)

```javascript
const [pc, rtpTransport] = setupPeerConnectionWithRtpTransport(); // Custom
const rtxPacketCache = createRtxPacketCache(); // Custom
// NEW: RtpTransport.readSentRtp (make RtpTransport.onrtpsent batch-style)
const sentRtps = rtpTransport.readSentRtp();
for (const sentRtp of sentRtps) {
// NEW: SentRtp.packet (make the existing object include the whole RtpPacket, not just timestamps)
rtxPacketCache.cacheRtpPacket(sentRtp.packet);
// PRO: No sendRtp required
}
};
// TODO: How do we disable normal NACK processing?
// NEW: RtpTransport.onreceivedrtcp (a whole new thing)
rtpTransport.onreceivedrtcp = () => {
// NEW: RtpTransport.readReceivedRtcp (batched part of RtpTransport.onrecievedrtcp)
const rtcp = rtpTransport.readReceivedRtcp();
// PRO: No need for browser to parse NACKs
// CON: Need to parse NACKs yourself
const nacks = parseNacks(rtcp);
for (const nack of nacks) {
for (const seqnum of nack.sequenceNumbers) {
const cachedRtpPacket = rtxPacketCache.getCachedRtpPacket(nack.ssrc, seqnum);
if (cachedRtpPacket) {
// NEW: asRtx
rtpSendStream.sendRtp(cachedRtpPacket, {asRtx: true});
}
}
}
}
```

### Example 2a: Custom NACK (per-stream and "parsed")

```javascript
const [pc, rtpTransport, rtpReceiver] = setupPeerConnectionWithRtpTransport(); // Custom using "customNack: true"
const nackCalculator = createNackCalculator(); // Custom
const rtpReceiveStream = await rtpReceiver.replaceReceiveStreams()[0];
rtpReceiveStream.onrtpreceived = () => {
const rtpPackets = rtpReceiveStream.readReceivedRtp();
const nackedSequenceNumbers = nackCalaculator.calculateNackedSequenceNumbers(rtpPackets);
if (nackedSequenceNumbers) {
rtpRecieveStream.sendNack(nackedSequenceNumbers);
}
}
```

### Example 2b: Custom NACK (per-transport and "parsed")

```javascript
const [pc, rtpTransport] = setupPeerConnectionWithRtpTransport(); // Custom using "customNack: true"
const nackCalculator = createNackCalculator(); // Custom
// NEW: RtpTransport.onrtpreceived (transport-wide version of RtpReceiveStream.onrtpreceived)
// CON: Duplicate onrtpreceived (transport-wide and per-stream)
rtpTransport.onrtpreceived = () => {
// NEW: RtpTransport.readReceivedRtp (transport-wide version of RtpReceiveStream.readReceivedRtp)
const rtpPackets = rtpTransport.readReceivedRtp();
const nacks = nackCalaculator.calculateNacks(rtpPackets);
for (const nack of nacks) {
// NEW: RtpTransport.sendNack (or maybe sendRtcp)
rtpTransport.sendNack(nack.ssrc, nack.sequenceNumbers);
}
}
```

### Example 2c: Custom NACK (per-transport and "unparsed")

```javascript
const [pc, rtpTransport] = setupPeerConnectionWithRtpTransport(); // Custom using "customNack: true"
const nackCalculator = createNackCalculator(); // Custom
// NEW: RtpTransport.onrtpreceived (transport-wide version of RtpReceiveStream.onrtpreceived)
// CON: Duplicate onrtpreceived (transport-wide and per-stream)
rtpTransport.onrtpreceived = () => {
// NEW: RtpTransport.readReceivedRtp (transport-wide version of RtpReceiveStream.readReceivedRtp)
const rtpPackets = rtpTransport.readReceivedRtp();
const nacks = nackCalaculator.calculateNacks(rtpPackets);
for (const nack of nacks) {
// PRO: Browser doesn't have to construct NACK
// CON: User does have to construct NACK
const rtcp = constructNackPacket(nack.ssrc, nack.sequenceNumbers);
rtpTransport.sendRtcp(rtcp);
}
}
```


### Example: Custom RTX payload

TODO: Use onpacketizedrtcpavailable/readPacketizedRtcp?
Loading