From 14bda1a028b87b0a68da780b34d6c8fd8b5d5c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sun, 27 Jun 2021 22:07:42 +0200 Subject: [PATCH] add ice restarts to reconnection logic. --- src/component/internal/connection.ts | 9 +++++++-- src/component/internal/messages.ts | 12 ++++++++++++ src/component/internal/webrtc.ts | 20 +++++++++++++++----- src/component/types/events.ts | 1 + 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/component/internal/connection.ts b/src/component/internal/connection.ts index e6cde504..924006f5 100644 --- a/src/component/internal/connection.ts +++ b/src/component/internal/connection.ts @@ -86,18 +86,23 @@ export class NekoConnection extends EventEmitter { // try to downgrade quality if it happend many times if (++webrtcCongestion >= WEBRTC_RECONN_FAILED_ATTEMPTS) { + webrtcCongestion = 0 + const quality = this._webrtcQualityDowngrade(this._state.webrtc.video) // downgrade if lower video quality exists if (quality && this.webrtc.connected) { this.setVideo(quality) - webrtcCongestion = 0 + } + + // try to perform ice restart, if available + if (this.webrtc.open) { + this.websocket.send(EVENT.SIGNAL_RESTART) return } // try to reconnect this._webrtcReconnect() - webrtcCongestion = 0 } }) } diff --git a/src/component/internal/messages.ts b/src/component/internal/messages.ts index 8d52dc1c..3d24fab1 100644 --- a/src/component/internal/messages.ts +++ b/src/component/internal/messages.ts @@ -125,6 +125,18 @@ export class NekoMessages extends EventEmitter { Vue.set(this._state.connection.webrtc, 'video', video) } + protected async [EVENT.SIGNAL_RESTART]({ event, sdp }: message.SignalAnswer) { + this._log.debug('EVENT.SIGNAL_RESTART') + this.emit('connection.webrtc.sdp', 'remote', sdp) + + const localSdp = await this._connection.webrtc.offer(sdp) + this._connection.websocket.send(EVENT.SIGNAL_ANSWER, { + sdp: localSdp, + }) + + this.emit('connection.webrtc.sdp', 'local', localSdp) + } + protected [EVENT.SIGNAL_CANDIDATE]({ event, ...candidate }: message.SignalCandidate) { this._log.debug('EVENT.SIGNAL_CANDIDATE') this._connection.webrtc.setCandidate(candidate) diff --git a/src/component/internal/webrtc.ts b/src/component/internal/webrtc.ts index 28d0dd0c..49dd49d9 100644 --- a/src/component/internal/webrtc.ts +++ b/src/component/internal/webrtc.ts @@ -54,15 +54,16 @@ export class NekoWebRTC extends EventEmitter { return typeof RTCPeerConnection !== 'undefined' && typeof RTCPeerConnection.prototype.addTransceiver !== 'undefined' } - get connected() { + get open() { return ( - typeof this._peer !== 'undefined' && - ['connected', 'checking', 'completed'].includes(this._state) && - typeof this._channel !== 'undefined' && - this._channel.readyState == 'open' + typeof this._peer !== 'undefined' && typeof this._channel !== 'undefined' && this._channel.readyState == 'open' ) } + get connected() { + return this.open && ['connected', 'checking', 'completed'].includes(this._state) + } + public async setCandidate(candidate: RTCIceCandidateInit) { if (!this._peer) { this._candidates.push(candidate) @@ -137,6 +138,15 @@ export class NekoWebRTC extends EventEmitter { this._peer.ondatachannel = this.onDataChannel.bind(this) this._peer.addTransceiver('audio', { direction: 'recvonly' }) this._peer.addTransceiver('video', { direction: 'recvonly' }) + + return await this.offer(sdp) + } + + public async offer(sdp: string) { + if (!this._peer) { + throw new Error('attempting to set offer for nonexistent peer') + } + this._peer.setRemoteDescription({ type: 'offer', sdp }) if (this._candidates.length > 0) { diff --git a/src/component/types/events.ts b/src/component/types/events.ts index 113c0a17..f58cec73 100644 --- a/src/component/types/events.ts +++ b/src/component/types/events.ts @@ -3,6 +3,7 @@ export const SYSTEM_ADMIN = 'system/admin' export const SYSTEM_DISCONNECT = 'system/disconnect' export const SIGNAL_REQUEST = 'signal/request' +export const SIGNAL_RESTART = 'signal/restart' export const SIGNAL_ANSWER = 'signal/answer' export const SIGNAL_PROVIDE = 'signal/provide' export const SIGNAL_CANDIDATE = 'signal/candidate'