mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
webrtc & websocket simple implementation.
This commit is contained in:
parent
3bff243dcb
commit
e99047963c
43
src/Neko.vue
43
src/Neko.vue
@ -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>
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user