webrtc & websocket simple implementation.

This commit is contained in:
Miroslav Šedivý 2020-11-05 18:06:55 +01:00
parent 3bff243dcb
commit e99047963c
3 changed files with 53 additions and 30 deletions

View File

@ -1,18 +1,55 @@
<template> <template>
<div> <div>
<h1>Hello world, Neko.</h1> <h1>Hello world, Neko.</h1>
<button @click="visible = !visible">Toggle</button> <button @click="connect()">Connect</button>
<div v-if="visible">Is this visible?</div> <button @click="disconnect()">Disonnect</button>
<button @click="send()">Send</button>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Vue, Component, Ref } from 'vue-property-decorator' import { Vue, Component, Ref } from 'vue-property-decorator'
import { NekoWebSocket } from './internal/websocket'
import { NekoWebRTC } from './internal/webrtc'
@Component({ @Component({
name: 'neko', name: 'neko',
}) })
export default class extends Vue { export default class extends Vue {
public visible: boolean = false protected _websocket?: NekoWebSocket
protected _webrtc?: NekoWebRTC
public connect() {
try {
this._websocket?.connect('ws://192.168.1.20:3000/', 'admin')
} catch (e) {}
}
public disconnect() {
this._websocket?.disconnect()
}
public send() {
this._websocket?.send('test', 'abc')
}
mounted() {
this._websocket = new NekoWebSocket()
this._webrtc = new NekoWebRTC()
this._websocket?.on('message', async (event, payload) => {
if (event == 'signal/provide') {
try {
let sdp = await this._webrtc?.connect(payload.sdp, payload.lite, payload.ice)
this._websocket?.send('signal/answer', { sdp, displayname: 'test' })
} catch (e) {}
} else {
console.log(event, payload)
}
})
this._websocket?.on('disconnected', () => {
this._webrtc?.disconnect()
})
}
} }
</script> </script>

View File

@ -15,7 +15,7 @@ export interface NekoWebRTCEvents {
track: (event: RTCTrackEvent) => void track: (event: RTCTrackEvent) => void
} }
export abstract class NekoWebRTC extends EventEmitter<NekoWebRTCEvents> { export class NekoWebRTC extends EventEmitter<NekoWebRTCEvents> {
private _peer?: RTCPeerConnection private _peer?: RTCPeerConnection
private _channel?: RTCDataChannel private _channel?: RTCDataChannel
private _state: RTCIceConnectionState = 'disconnected' private _state: RTCIceConnectionState = 'disconnected'
@ -23,7 +23,7 @@ export abstract class NekoWebRTC extends EventEmitter<NekoWebRTCEvents> {
constructor() { constructor() {
super() super()
this._log = new Logger('webrtc') this._log = new Logger('webrtc')
} }
@ -37,11 +37,11 @@ export abstract class NekoWebRTC extends EventEmitter<NekoWebRTCEvents> {
public async connect(sdp: string, lite: boolean, servers: string[]): Promise<string> { public async connect(sdp: string, lite: boolean, servers: string[]): Promise<string> {
this._log.debug(`creating peer`) this._log.debug(`creating peer`)
if (!this.supported) { if (!this.supported) {
throw new Error('browser does not support webrtc') throw new Error('browser does not support webrtc')
} }
if (this.connected) { if (this.connected) {
throw new Error('attempting to create peer while connected') throw new Error('attempting to create peer while connected')
} }
@ -87,7 +87,7 @@ export abstract class NekoWebRTC extends EventEmitter<NekoWebRTCEvents> {
this._peer.addTransceiver('video', { direction: 'recvonly' }) this._peer.addTransceiver('video', { direction: 'recvonly' })
this._channel = this._peer.createDataChannel('data') this._channel = this._peer.createDataChannel('data')
this._channel.onerror = this.onError.bind(this) this._channel.onerror = this.onDisconnected.bind(this, new Error('peer data channel error'))
this._channel.onmessage = this.onData.bind(this) this._channel.onmessage = this.onData.bind(this)
this._channel.onclose = this.onDisconnected.bind(this, new Error('peer data channel closed')) this._channel.onclose = this.onDisconnected.bind(this, new Error('peer data channel closed'))
@ -175,17 +175,13 @@ export abstract class NekoWebRTC extends EventEmitter<NekoWebRTCEvents> {
private onTrack(event: RTCTrackEvent) { private onTrack(event: RTCTrackEvent) {
this._log.debug(`received ${event.track.kind} track from peer: ${event.track.id}`, event) this._log.debug(`received ${event.track.kind} track from peer: ${event.track.id}`, event)
const stream = event.streams[0] const stream = event.streams[0]
if (!stream) { if (!stream) {
this._log.warn(`no stream provided for track ${event.track.id}(${event.track.label})`) this._log.warn(`no stream provided for track ${event.track.id}(${event.track.label})`)
return return
} }
this.emit('track', event)
}
private onError(event: Event) { this.emit('track', event)
this._log.error((event as ErrorEvent).error)
} }
private onConnected() { private onConnected() {
@ -201,7 +197,7 @@ export abstract class NekoWebRTC extends EventEmitter<NekoWebRTCEvents> {
private onDisconnected(reason?: Error) { private onDisconnected(reason?: Error) {
this.disconnect() this.disconnect()
this._log.debug(`disconnected:`, reason) this._log.debug(`disconnected:`, reason?.message)
this.emit('disconnected', reason) this.emit('disconnected', reason)
} }
} }

View File

@ -7,6 +7,7 @@ export interface NekoWebSocketEvents {
connecting: () => void connecting: () => void
connected: () => void connected: () => void
disconnected: (error?: Error) => void disconnected: (error?: Error) => void
message: (event: string, payload: any) => void
} }
export class NekoWebSocket extends EventEmitter<NekoWebSocketEvents> { export class NekoWebSocket extends EventEmitter<NekoWebSocketEvents> {
@ -36,7 +37,7 @@ export class NekoWebSocket extends EventEmitter<NekoWebSocketEvents> {
this._ws.onopen = this.onConnected.bind(this) this._ws.onopen = this.onConnected.bind(this)
this._ws.onclose = this.onDisconnected.bind(this, new Error('websocket closed')) this._ws.onclose = this.onDisconnected.bind(this, new Error('websocket closed'))
this._ws.onerror = this.onError.bind(this) this._ws.onerror = this.onDisconnected.bind(this, new Error('websocket error'))
this._ws.onmessage = this.onMessage.bind(this) this._ws.onmessage = this.onMessage.bind(this)
this._timeout = setTimeout(this.onTimeout.bind(this), timeout) this._timeout = setTimeout(this.onTimeout.bind(this), timeout)
@ -68,20 +69,9 @@ export class NekoWebSocket extends EventEmitter<NekoWebSocketEvents> {
private onMessage(e: MessageEvent) { private onMessage(e: MessageEvent) {
const { event, ...payload } = JSON.parse(e.data) const { event, ...payload } = JSON.parse(e.data)
this._log.debug(`received websocket event ${event} ${payload ? `with payload: ` : ''}`, payload) this._log.debug(`received websocket event ${event} ${payload ? `with payload: ` : ''}`, payload)
this.emit('message', event, payload)
// @ts-ignore
if (typeof this[event] === 'function') {
// @ts-ignore
// TODO: REFACTOR
this[event](payload)
} else {
this._log.warn(`unhandled websocket event '${event}':`, payload)
}
}
private onError(event: Event) {
this._log.error((event as ErrorEvent).error)
} }
private onConnected() { private onConnected() {
@ -104,7 +94,7 @@ export class NekoWebSocket extends EventEmitter<NekoWebSocketEvents> {
} }
private onDisconnected(reason?: Error) { private onDisconnected(reason?: Error) {
this._log.debug(`disconnected:`, reason) this._log.debug(`disconnected:`, reason?.message)
this.disconnect() this.disconnect()
this.emit('disconnected', reason) this.emit('disconnected', reason)