move websocket to connection.

This commit is contained in:
Miroslav Šedivý 2021-06-18 00:31:03 +02:00
parent e958690e10
commit e0de57fc70
3 changed files with 86 additions and 118 deletions

View File

@ -10,61 +10,40 @@ export interface NekoConnectionEvents {
}
export class NekoConnection extends EventEmitter<NekoConnectionEvents> {
staysConnected = false
private _url: string
private _token: string
websocket = new NekoWebSocket()
webrtc = new NekoWebRTC()
public websocket = new NekoWebSocket()
public webrtc = new NekoWebRTC()
constructor() {
super()
//
// websocket events
//
this._url = ''
this._token = ''
}
this.websocket.on('message', async (event: string, payload: any) => {
})
this.websocket.on('connecting', () => {
})
this.websocket.on('connected', () => {
})
this.websocket.on('disconnected', () => {
})
//
// webrtc events
//
this.webrtc.on('track', (event: RTCTrackEvent) => {
})
this.webrtc.on('candidate', (candidate: RTCIceCandidateInit) => {
})
this.webrtc.on('stats', (stats: WebRTCStats) => {
})
this.webrtc.on('connecting', () => {
})
this.webrtc.on('connected', () => {
})
this.webrtc.on('disconnected', () => {
})
public setUrl(url: string) {
this._url = url.replace(/^http/, 'ws').replace(/\/+$/, '') + '/api/ws'
}
public setToken(token: string) {
this._token = token
}
public async connect(): Promise<void> {
this.staysConnected = true
let url = this._url
if (this._token) {
url += '?token=' + encodeURIComponent(this._token)
}
public async disconnect(): Promise<void> {
this.staysConnected = false
await this.websocket.connect(url)
// TODO: connect to WebRTC
//this.websocket.send(EVENT.SIGNAL_REQUEST, { video: video })
}
public disconnect() {
this.websocket.disconnect()
}
}

View File

@ -4,7 +4,7 @@ import * as message from '../types/messages'
import EventEmitter from 'eventemitter3'
import { Logger } from '../utils/logger'
import { NekoWebSocket } from './websocket'
import { NekoConnection } from './connection'
import NekoState from '../types/state'
export interface NekoEvents {
@ -42,18 +42,18 @@ export interface NekoEvents {
}
export class NekoMessages extends EventEmitter<NekoEvents> {
private _websocket: NekoWebSocket
private _connection: NekoConnection
private _state: NekoState
private _log: Logger
constructor(websocket: NekoWebSocket, state: NekoState) {
constructor(connection: NekoConnection, state: NekoState) {
super()
this._log = new Logger('messages')
this._state = state
this._websocket = websocket
this._connection = connection
this._websocket.on('message', async (event: string, payload: any) => {
this._connection.websocket.on('message', async (event: string, payload: any) => {
// @ts-ignore
if (typeof this[event] === 'function') {
// @ts-ignore
@ -62,6 +62,11 @@ export class NekoMessages extends EventEmitter<NekoEvents> {
this._log.warn(`unhandled websocket event '${event}':`, payload)
}
})
this._connection.webrtc.on('candidate', (candidate: RTCIceCandidateInit) => {
this._connection.websocket.send(EVENT.SIGNAL_CANDIDATE, candidate)
this.emit('connection.webrtc.sdp.candidate', 'local', candidate)
})
}
/////////////////////////////
@ -101,7 +106,7 @@ export class NekoMessages extends EventEmitter<NekoEvents> {
protected [EVENT.SYSTEM_DISCONNECT]({ message }: message.SystemDisconnect) {
this._log.debug('EVENT.SYSTEM_DISCONNECT')
this._websocket.disconnect(new Error(message))
this._connection.disconnect()
this.emit('connection.disconnect', message)
}
@ -109,20 +114,26 @@ export class NekoMessages extends EventEmitter<NekoEvents> {
// Signal Events
/////////////////////////////
protected [EVENT.SIGNAL_PROVIDE]({ event, sdp, video }: message.SignalProvide) {
protected async [EVENT.SIGNAL_PROVIDE]({ sdp: remoteSdp, video, iceservers }: message.SignalProvide) {
this._log.debug('EVENT.SIGNAL_PROVIDE')
this.emit('connection.webrtc.sdp', 'remote', remoteSdp)
const localSdp = await this._connection.webrtc.connect(remoteSdp, iceservers)
this._connection.websocket.send(EVENT.SIGNAL_ANSWER, {
sdp: localSdp,
})
this.emit('connection.webrtc.sdp', 'local', localSdp)
Vue.set(this._state.connection.webrtc, 'video', video)
// TODO: Handle.
this.emit('connection.webrtc.sdp', 'remote', sdp)
}
protected [EVENT.SIGNAL_CANDIDATE]({ event, ...candidate }: message.SignalCandidate) {
this._log.debug('EVENT.SIGNAL_CANDIDATE')
// TODO: Handle.
this._connection.webrtc.setCandidate(candidate)
this.emit('connection.webrtc.sdp.candidate', 'remote', candidate)
}
protected [EVENT.SIGNAL_VIDEO]({ event, video }: message.SignalVideo) {
protected [EVENT.SIGNAL_VIDEO]({ video }: message.SignalVideo) {
this._log.debug('EVENT.SIGNAL_VIDEO')
Vue.set(this._state.connection.webrtc, 'video', video)
}

View File

@ -3,7 +3,7 @@
<div ref="container" class="neko-container">
<video ref="video" :autoplay="autoplay" :muted="autoplay" playsinline />
<neko-overlay
:webrtc="webrtc"
:webrtc="connection.webrtc"
:scroll="state.control.scroll"
:screenSize="state.screen.size"
:canvasSize="canvasSize"
@ -14,8 +14,8 @@
: ''
"
:implicitControl="state.control.implicit_hosting && state.sessions[state.session_id].profile.can_host"
@implicit-control-request="websocket.send('control/request')"
@implicit-control-release="websocket.send('control/release')"
@implicit-control-request="connection.websocket.send('control/request')"
@implicit-control-release="connection.websocket.send('control/release')"
@update-kbd-modifiers="updateKeyboardModifiers($event)"
@drop-files="uploadDrop($event)"
/>
@ -57,8 +57,8 @@
import ResizeObserver from 'resize-observer-polyfill'
import { NekoApi, MembersApi, RoomApi } from './internal/api'
import { NekoWebSocket } from './internal/websocket'
import { NekoWebRTC, WebRTCStats } from './internal/webrtc'
import { NekoConnection } from './internal/connection'
import { WebRTCStats } from './internal/webrtc'
import { NekoMessages } from './internal/messages'
import { register as VideoRegister } from './internal/video'
@ -77,8 +77,6 @@
@Ref('video') readonly _video!: HTMLVideoElement
api = new NekoApi()
websocket = new NekoWebSocket()
webrtc = new NekoWebRTC()
observer = new ResizeObserver(this.onResize.bind(this))
canvasSize: { width: number; height: number } = {
width: 0,
@ -97,15 +95,20 @@
@Prop({ type: Boolean })
private readonly autoplay!: boolean
/////////////////////////////
// Public connection manager
/////////////////////////////
public connection = new NekoConnection()
/////////////////////////////
// Public state
/////////////////////////////
public state = {
connection: {
authenticated: false,
websocket: this.websocket.supported ? 'disconnected' : 'unavailable',
websocket: this.connection.websocket.supported ? 'disconnected' : 'unavailable',
webrtc: {
status: this.webrtc.supported ? 'disconnected' : 'unavailable',
status: this.connection.webrtc.supported ? 'disconnected' : 'unavailable',
stats: null,
video: null,
videos: [],
@ -167,7 +170,7 @@
/////////////////////////////
// Public events
/////////////////////////////
public events = new NekoMessages(this.websocket, this.state)
public events = new NekoMessages(this.connection, this.state)
/////////////////////////////
// Public methods
@ -180,14 +183,10 @@
const httpURL = url.replace(/^ws/, 'http').replace(/\/$|\/ws\/?$/, '')
this.api.setUrl(httpURL)
this.websocket.setUrl(httpURL)
this.connection.setUrl(httpURL)
if (this.connected) {
this.websocket.disconnect(new Error('url changed'))
}
if (this.watching) {
this.webrtc.disconnect()
this.connection.disconnect()
}
if (this.authenticated) {
@ -199,14 +198,14 @@
const token = localStorage.getItem('neko_session')
if (token) {
this.api.setToken(token)
this.websocket.setToken(token)
this.connection.setToken(token)
}
await this.api.session.whoami()
Vue.set(this.state.connection, 'authenticated', true)
if (this.autoconnect) {
await this.websocket.connect()
await this.connection.connect()
}
}
}
@ -219,7 +218,7 @@
const res = await this.api.session.login({ username, password })
if (res.data.token) {
this.api.setToken(res.data.token)
this.websocket.setToken(res.data.token)
this.connection.setToken(res.data.token)
if (this.autologin) {
localStorage.setItem('neko_session', res.data.token)
@ -229,7 +228,7 @@
Vue.set(this.state.connection, 'authenticated', true)
if (this.autoconnect) {
await this.websocket.connect()
await this.connection.connect()
}
}
@ -239,14 +238,14 @@
}
if (this.connected) {
this.websocket.disconnect(new Error('logged out'))
this.connection.disconnect()
}
try {
await this.api.session.logout()
} finally {
this.api.setToken('')
this.websocket.setToken('')
this.connection.setToken('')
if (this.autologin) {
localStorage.removeItem('neko_session')
@ -266,7 +265,7 @@
throw new Error('client already connected to websocket')
}
await this.websocket.connect()
await this.connection.connect()
}
// TODO: Refactor.
@ -275,7 +274,7 @@
throw new Error('client not connected to websocket')
}
this.websocket.disconnect(new Error('manual action'))
this.connection.disconnect()
}
// TODO: Refactor.
@ -292,7 +291,7 @@
throw new Error('video id not found')
}
this.websocket.send(EVENT.SIGNAL_REQUEST, { video: video })
this.connection.websocket.send(EVENT.SIGNAL_REQUEST, { video: video })
}
// TODO: Refactor.
@ -301,7 +300,7 @@
throw new Error('client not connected to webrtc')
}
this.webrtc.disconnect()
this.connection.webrtc.disconnect()
}
public play() {
@ -342,7 +341,7 @@
// TODO: Remove? Use REST API only?
public setScreenSize(width: number, height: number, rate: number) {
this.websocket.send(EVENT.SCREEN_SET, { width, height, rate })
this.connection.websocket.send(EVENT.SCREEN_SET, { width, height, rate })
}
public setWebRTCVideo(video: string) {
@ -350,7 +349,7 @@
throw new Error('video id not found')
}
this.websocket.send(EVENT.SIGNAL_VIDEO, { video: video })
this.connection.websocket.send(EVENT.SIGNAL_VIDEO, { video: video })
}
public setWebRTCAuto(auto: boolean = true) {
@ -358,11 +357,11 @@
}
public sendUnicast(receiver: string, subject: string, body: any) {
this.websocket.send(EVENT.SEND_UNICAST, { receiver, subject, body })
this.connection.websocket.send(EVENT.SEND_UNICAST, { receiver, subject, body })
}
public sendBroadcast(subject: string, body: any) {
this.websocket.send(EVENT.SEND_BROADCAST, { subject, body })
this.connection.websocket.send(EVENT.SEND_BROADCAST, { subject, body })
}
public get room(): RoomApi {
@ -400,28 +399,11 @@
VideoRegister(this._video, this.state.video)
// websocket
this.websocket.on('message', async (event: string, payload: any) => {
switch (event) {
case EVENT.SIGNAL_PROVIDE:
try {
let sdp = await this.webrtc.connect(payload.sdp, payload.iceservers)
this.websocket.send(EVENT.SIGNAL_ANSWER, { sdp })
this.events.emit('connection.webrtc.sdp', 'local', sdp)
} catch (e) {}
break
case EVENT.SIGNAL_CANDIDATE:
this.webrtc.setCandidate(payload)
break
case EVENT.SYSTEM_DISCONNECT:
this.websocket.disconnect(new Error('disconnected by server'))
break
}
})
this.websocket.on('connecting', () => {
this.connection.websocket.on('connecting', () => {
Vue.set(this.state.connection, 'websocket', 'connecting')
this.events.emit('connection.websocket', 'connecting')
})
this.websocket.on('connected', () => {
this.connection.websocket.on('connected', () => {
Vue.set(this.state.connection, 'websocket', 'connected')
this.events.emit('connection.websocket', 'connected')
@ -430,7 +412,7 @@
this.webrtcConnect()
}
})
this.websocket.on('disconnected', () => {
this.connection.websocket.on('disconnected', () => {
Vue.set(this.state.connection, 'websocket', 'disconnected')
this.events.emit('connection.websocket', 'disconnected')
@ -438,7 +420,7 @@
})
// webrtc
this.webrtc.on('track', (event: RTCTrackEvent) => {
this.connection.webrtc.on('track', (event: RTCTrackEvent) => {
const { track, streams } = event
if (track.kind === 'audio') return
@ -454,13 +436,9 @@
this._video.play()
}
})
this.webrtc.on('candidate', (candidate: RTCIceCandidateInit) => {
this.websocket.send(EVENT.SIGNAL_CANDIDATE, candidate)
this.events.emit('connection.webrtc.sdp.candidate', 'local', candidate)
})
let webrtcCongestion: number = 0
this.webrtc.on('stats', (stats: WebRTCStats) => {
this.connection.webrtc.on('stats', (stats: WebRTCStats) => {
Vue.set(this.state.connection.webrtc, 'stats', stats)
// if automatic quality adjusting is turned off
@ -493,18 +471,18 @@
webrtcCongestion = 0
}
})
this.webrtc.on('connecting', () => {
this.connection.webrtc.on('connecting', () => {
Vue.set(this.state.connection.webrtc, 'status', 'connecting')
this.events.emit('connection.webrtc', 'connecting')
})
this.webrtc.on('connected', () => {
this.connection.webrtc.on('connected', () => {
Vue.set(this.state.connection.webrtc, 'status', 'connected')
Vue.set(this.state.connection, 'type', 'webrtc')
this.events.emit('connection.webrtc', 'connected')
})
let webrtcReconnect: any = null
this.webrtc.on('disconnected', () => {
this.connection.webrtc.on('disconnected', () => {
const lastVideo = this.state.connection.webrtc.video ?? undefined
this.events.emit('connection.webrtc', 'disconnected')
@ -548,8 +526,8 @@
beforeDestroy() {
this.observer.disconnect()
this.webrtc.disconnect()
this.websocket.disconnect()
this.connection.webrtc.disconnect()
this.connection.websocket.disconnect()
// remove users first interaction event
document.removeEventListener('click', this.unmute)
@ -559,12 +537,12 @@
@Watch('state.control.keyboard')
updateKeyboard() {
if (this.controlling && this.state.control.keyboard.layout) {
this.websocket.send(EVENT.KEYBOARD_MAP, this.state.control.keyboard)
this.connection.websocket.send(EVENT.KEYBOARD_MAP, this.state.control.keyboard)
}
}
updateKeyboardModifiers(modifiers: { capslock: boolean; numlock: boolean }) {
this.websocket.send(EVENT.KEYBOARD_MODIFIERS, modifiers)
this.connection.websocket.send(EVENT.KEYBOARD_MODIFIERS, modifiers)
}
@Watch('state.screen.size')