Skip to content

Commit 965f4fb

Browse files
authored
Fix ICE end-of-candidates messages (#2622)
* Fix ICE end-of-candidates messages We were casting a POJO to an RTCIceCandidate for the dummy end-of-candidates candidate, but #2473 started calling .toJSON() on these objects. Store separately whether we've seen the end of candidates rather than adding on a dummy candidate object. A test for this will follow, but a) I want to get this fix out and b) I'm currently rewriting the call test file to add typing. Fixes element-hq/element-call#553 * Remove hacks for testing * Switch if branches
1 parent 9e1b126 commit 965f4fb

File tree

1 file changed

+28
-19
lines changed

1 file changed

+28
-19
lines changed

src/webrtc/call.ts

+28-19
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
343343
// possible
344344
private candidateSendQueue: Array<RTCIceCandidate> = [];
345345
private candidateSendTries = 0;
346-
private sentEndOfCandidates = false;
346+
private candidatesEnded = false;
347347
private feeds: Array<CallFeed> = [];
348348
private usermediaSenders: Array<RTCRtpSender> = [];
349349
private screensharingSenders: Array<RTCRtpSender> = [];
@@ -1597,6 +1597,11 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
15971597
*/
15981598
private gotLocalIceCandidate = (event: RTCPeerConnectionIceEvent): Promise<void> => {
15991599
if (event.candidate) {
1600+
if (this.candidatesEnded) {
1601+
logger.warn("Got candidate after candidates have ended - ignoring!");
1602+
return;
1603+
}
1604+
16001605
logger.debug(
16011606
"Call " + this.callId + " got local ICE " + event.candidate.sdpMid + " candidate: " +
16021607
event.candidate.candidate,
@@ -1606,29 +1611,18 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
16061611

16071612
// As with the offer, note we need to make a copy of this object, not
16081613
// pass the original: that broke in Chrome ~m43.
1609-
if (event.candidate.candidate !== '' || !this.sentEndOfCandidates) {
1614+
if (event.candidate.candidate === '') {
1615+
this.queueCandidate(null);
1616+
} else {
16101617
this.queueCandidate(event.candidate);
1611-
1612-
if (event.candidate.candidate === '') this.sentEndOfCandidates = true;
16131618
}
16141619
}
16151620
};
16161621

16171622
private onIceGatheringStateChange = (event: Event): void => {
16181623
logger.debug(`Call ${this.callId} ice gathering state changed to ${this.peerConn.iceGatheringState}`);
1619-
if (this.peerConn.iceGatheringState === 'complete' && !this.sentEndOfCandidates) {
1620-
// If we didn't get an empty-string candidate to signal the end of candidates,
1621-
// create one ourselves now gathering has finished.
1622-
// We cast because the interface lists all the properties as required but we
1623-
// only want to send 'candidate'
1624-
// XXX: We probably want to send either sdpMid or sdpMLineIndex, as it's not strictly
1625-
// correct to have a candidate that lacks both of these. We'd have to figure out what
1626-
// previous candidates had been sent with and copy them.
1627-
const c = {
1628-
candidate: '',
1629-
} as RTCIceCandidate;
1630-
this.queueCandidate(c);
1631-
this.sentEndOfCandidates = true;
1624+
if (this.peerConn.iceGatheringState === 'complete') {
1625+
this.queueCandidate(null);
16321626
}
16331627
};
16341628

@@ -2247,7 +2241,12 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
22472241
}
22482242
}
22492243

2250-
private queueCandidate(content: RTCIceCandidate): void {
2244+
/**
2245+
* Queue a candidate to be sent
2246+
* @param content The candidate to queue up, or null if candidates have finished being generated
2247+
* and end-of-candidates should be signalled
2248+
*/
2249+
private queueCandidate(content: RTCIceCandidate | null): void {
22512250
// We partially de-trickle candidates by waiting for `delay` before sending them
22522251
// amalgamated, in order to avoid sending too many m.call.candidates events and hitting
22532252
// rate limits in Matrix.
@@ -2257,7 +2256,11 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
22572256
// currently proposes as the way to indicate that candidate gathering is complete.
22582257
// This will hopefully be changed to an explicit rather than implicit notification
22592258
// shortly.
2260-
this.candidateSendQueue.push(content);
2259+
if (content) {
2260+
this.candidateSendQueue.push(content);
2261+
} else {
2262+
this.candidatesEnded = true;
2263+
}
22612264

22622265
// Don't send the ICE candidates yet if the call is in the ringing state: this
22632266
// means we tried to pick (ie. started generating candidates) and then failed to
@@ -2446,6 +2449,12 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
24462449
this.candidateSendQueue = [];
24472450
++this.candidateSendTries;
24482451
const content = { candidates: candidates.map(candidate => candidate.toJSON()) };
2452+
if (this.candidatesEnded) {
2453+
// If there are no more candidates, signal this by adding an empty string candidate
2454+
content.candidates.push({
2455+
candidate: '',
2456+
});
2457+
}
24492458
logger.debug(`Call ${this.callId} attempting to send ${candidates.length} candidates`);
24502459
try {
24512460
await this.sendVoipEvent(EventType.CallCandidates, content);

0 commit comments

Comments
 (0)