Implementing reconnection logic.

This commit is contained in:
Filipi Fuchter
2025-03-12 18:23:12 -03:00
parent 9cad8bfcc6
commit d33a4b3a11
2 changed files with 66 additions and 25 deletions

View File

@@ -31,6 +31,10 @@ export class SmallWebRTCTransport {
private videoCodec: string | null = null; private videoCodec: string | null = null;
private pc_id: string | null = null; private pc_id: string | null = null;
private reconnectionAttempts = 0;
private maxReconnectionAttempts = 3;
private isReconnecting = false;
constructor(callbacks: SmallWebRTCTransportCallbacks) { constructor(callbacks: SmallWebRTCTransportCallbacks) {
this._callbacks = callbacks this._callbacks = callbacks
} }
@@ -52,10 +56,10 @@ export class SmallWebRTCTransport {
}); });
this.log(`iceGatheringState: ${pc.iceGatheringState}`) this.log(`iceGatheringState: ${pc.iceGatheringState}`)
pc.addEventListener('iceconnectionstatechange', () => { pc.addEventListener("iceconnectionstatechange", () => this.handleICEConnectionStateChange());
let connectionState = this.pc!.iceConnectionState
this.log(`iceConnectionState: ${connectionState}`) pc.addEventListener("connectionstatechange", () => this.handleConnectionStateChange());
});
this.log(`iceConnectionState: ${pc.iceConnectionState}`) this.log(`iceConnectionState: ${pc.iceConnectionState}`)
pc.addEventListener('signalingstatechange', () => { pc.addEventListener('signalingstatechange', () => {
@@ -68,27 +72,64 @@ export class SmallWebRTCTransport {
this._callbacks.onTrackStarted(evt.track) this._callbacks.onTrackStarted(evt.track)
}); });
pc.onconnectionstatechange = () => {
let connectionState = this.pc?.connectionState
this.log(`connectionState: ${connectionState}`)
if (connectionState == 'connected') {
this._callbacks.onConnected()
} else if (connectionState == 'disconnected') {
this.handleDisconnected()
}
}
return pc; return pc;
} }
private async negotiate(): Promise<void> { private handleICEConnectionStateChange(): void {
if (!this.pc) return;
this.log(`ICE Connection State: ${this.pc.iceConnectionState}`);
if (this.pc.iceConnectionState === "failed") {
this.log("ICE connection failed, attempting restart.");
void this.attemptReconnection(true);
} else if (this.pc.iceConnectionState === "disconnected") {
// Waiting before trying to reconnect to see if it handles it automatically
setTimeout(() => {
if (this.pc?.iceConnectionState === "disconnected") {
this.log("Still disconnected, attempting reconnection.");
void this.attemptReconnection(true);
}
}, 5000);
}
}
private handleConnectionStateChange(): void {
if (!this.pc) return;
this.log(`Connection State: ${this.pc.connectionState}`);
if (this.pc.connectionState === "connected") {
this.reconnectionAttempts = 0;
this.isReconnecting = false;
this._callbacks.onConnected();
} else if (this.pc.connectionState === "failed") {
void this.attemptReconnection(true);
}
}
private async attemptReconnection(iceRestart: boolean = false): Promise<void> {
if (this.isReconnecting) {
this.log("Reconnection already in progress, skipping.");
return;
}
if (this.reconnectionAttempts >= this.maxReconnectionAttempts) {
this.log("Max reconnection attempts reached. Stopping transport.");
this.stop();
return;
}
this.isReconnecting = true;
this.reconnectionAttempts++;
this.log(`Reconnection attempt ${this.reconnectionAttempts}...`);
await this.negotiate(iceRestart);
}
private async negotiate(iceRestart: boolean = false): Promise<void> {
if (!this.pc) { if (!this.pc) {
return Promise.reject('Peer connection is not initialized'); return Promise.reject('Peer connection is not initialized');
} }
try { try {
// Create offer // Create offer
const offer = await this.pc.createOffer(); const offer = await this.pc.createOffer({iceRestart});
await this.pc.setLocalDescription(offer); await this.pc.setLocalDescription(offer);
// Wait for ICE gathering to complete // Wait for ICE gathering to complete
@@ -143,7 +184,9 @@ export class SmallWebRTCTransport {
this.log(`Received answer for peer connection id ${answer.pc_id}`) this.log(`Received answer for peer connection id ${answer.pc_id}`)
await this.pc!.setRemoteDescription(answer); await this.pc!.setRemoteDescription(answer);
} catch (e) { } catch (e) {
alert(e); this.log(`Reconnection attempt ${this.reconnectionAttempts} failed: ${e}`);
this.isReconnecting = false
setTimeout(() => this.attemptReconnection(true), 2000);
} }
} }
@@ -167,11 +210,6 @@ export class SmallWebRTCTransport {
return this.pc!.getTransceivers()[1]; return this.pc!.getTransceivers()[1];
} }
private handleDisconnected() {
this.pc_id = null
this._callbacks.onDisconnected()
}
async start(audioDevice: string | undefined, audioCodec: string, videoCodec: string, videoDevice: string | undefined): Promise<void> { async start(audioDevice: string | undefined, audioCodec: string, videoCodec: string, videoDevice: string | undefined): Promise<void> {
this.pc = this.createPeerConnection(); this.pc = this.createPeerConnection();
this.addInitialTransceivers(); this.addInitialTransceivers();
@@ -227,7 +265,7 @@ export class SmallWebRTCTransport {
// Handle different signalling message types // Handle different signalling message types
switch (signallingMessage.message) { switch (signallingMessage.message) {
case SignallingMessage.RENEGOTIATE: case SignallingMessage.RENEGOTIATE:
await this.negotiate() void this.attemptReconnection(false)
break; break;
default: default:
@@ -304,7 +342,10 @@ export class SmallWebRTCTransport {
this.pc.close(); this.pc.close();
// For some reason after we close the peer connection, it is not triggering the listeners // For some reason after we close the peer connection, it is not triggering the listeners
this.handleDisconnected() this.pc_id = null
this.reconnectionAttempts = 0
this.isReconnecting = false;
this._callbacks.onDisconnected()
} }
private async getAllDevices() { private async getAllDevices() {

View File

@@ -189,7 +189,7 @@ class SmallWebRTCClient:
except asyncio.TimeoutError: except asyncio.TimeoutError:
if self._webrtcConnection.is_connected(): if self._webrtcConnection.is_connected():
logger.warning("Timeout: No video frame received within the specified time.") logger.warning("Timeout: No video frame received within the specified time.")
self._webrtcConnection.ask_to_renegotiate() # self._webrtcConnection.ask_to_renegotiate()
frame = None frame = None
if frame is None or not isinstance(frame, VideoFrame): if frame is None or not isinstance(frame, VideoFrame):