Improve trickle ICE with early candidate buffering

- Buffer ICE candidates generated before offerId is set
- Flush buffered candidates immediately after offerId is set
- Continue sending candidates as they arrive (true trickle ICE)
- Prevents losing early ICE candidates during setup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-14 19:01:49 +01:00
parent c202e1c627
commit cd78a16c66

View File

@@ -60,6 +60,7 @@ export class RondevuConnection {
private lastIceTimestamp: number = Date.now(); private lastIceTimestamp: number = Date.now();
private eventListeners: Map<keyof RondevuConnectionEvents, Set<Function>> = new Map(); private eventListeners: Map<keyof RondevuConnectionEvents, Set<Function>> = new Map();
private dataChannel?: RTCDataChannel; private dataChannel?: RTCDataChannel;
private pendingIceCandidates: string[] = [];
/** /**
* Current connection state * Current connection state
@@ -101,11 +102,19 @@ export class RondevuConnection {
*/ */
private setupPeerConnection(): void { private setupPeerConnection(): void {
this.pc.onicecandidate = async (event) => { this.pc.onicecandidate = async (event) => {
if (event.candidate && this.offerId) { if (event.candidate) {
try { const candidateString = event.candidate.candidate;
await this.offersApi.addIceCandidates(this.offerId, [event.candidate.candidate]);
} catch (err) { if (this.offerId) {
console.error('Error sending ICE candidate:', err); // offerId is set, send immediately (trickle ICE)
try {
await this.offersApi.addIceCandidates(this.offerId, [candidateString]);
} catch (err) {
console.error('Error sending ICE candidate:', err);
}
} else {
// offerId not set yet, buffer the candidate
this.pendingIceCandidates.push(candidateString);
} }
} }
}; };
@@ -141,6 +150,20 @@ export class RondevuConnection {
}; };
} }
/**
* Flush buffered ICE candidates (trickle ICE support)
*/
private async flushPendingIceCandidates(): Promise<void> {
if (this.pendingIceCandidates.length > 0 && this.offerId) {
try {
await this.offersApi.addIceCandidates(this.offerId, this.pendingIceCandidates);
this.pendingIceCandidates = [];
} catch (err) {
console.error('Error flushing pending ICE candidates:', err);
}
}
}
/** /**
* Create an offer and advertise on topics * Create an offer and advertise on topics
*/ */
@@ -168,6 +191,9 @@ export class RondevuConnection {
this.offerId = offers[0].id; this.offerId = offers[0].id;
// Flush any ICE candidates that were generated during offer creation
await this.flushPendingIceCandidates();
// Start polling for answers // Start polling for answers
this.startAnswerPolling(); this.startAnswerPolling();
@@ -198,6 +224,9 @@ export class RondevuConnection {
// This prevents a race condition where ICE candidates arrive before answer is registered // This prevents a race condition where ICE candidates arrive before answer is registered
this.offerId = offerId; this.offerId = offerId;
// Flush any ICE candidates that were generated during answer creation
await this.flushPendingIceCandidates();
// Start polling for ICE candidates // Start polling for ICE candidates
this.startIcePolling(); this.startIcePolling();
} }