2020-11-07 08:49:05 +13:00
|
|
|
<template>
|
|
|
|
<div ref="component" class="component">
|
2020-11-09 10:44:37 +13:00
|
|
|
<div ref="container" class="player-container">
|
2021-02-01 02:06:08 +13:00
|
|
|
<video ref="video" :autoplay="autoplay" :muted="autoplay" />
|
2020-11-07 08:49:05 +13:00
|
|
|
<neko-overlay
|
|
|
|
:webrtc="webrtc"
|
2021-01-10 11:28:56 +13:00
|
|
|
:control="state.control"
|
2020-11-09 07:38:14 +13:00
|
|
|
:screenWidth="state.screen.size.width"
|
|
|
|
:screenHeight="state.screen.size.height"
|
2020-12-08 06:46:29 +13:00
|
|
|
:isControling="controlling && watching"
|
2020-12-07 08:08:25 +13:00
|
|
|
:implicitControl="state.control.implicit_hosting && state.members[state.member_id].profile.can_host"
|
2020-12-02 22:45:23 +13:00
|
|
|
@implicit-control-request="websocket.send('control/request')"
|
|
|
|
@implicit-control-release="websocket.send('control/release')"
|
2021-01-09 08:51:38 +13:00
|
|
|
@drop-files="uploadDrop($event)"
|
2020-11-07 08:49:05 +13:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.component {
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.player-container {
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
video {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
bottom: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
display: flex;
|
|
|
|
background: #000;
|
|
|
|
|
|
|
|
&::-webkit-media-controls {
|
|
|
|
display: none !important;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
|
|
|
|
<script lang="ts">
|
2021-01-14 10:36:09 +13:00
|
|
|
export * as ApiModels from './api/models'
|
|
|
|
export * as StateModels from './types/state'
|
|
|
|
|
2020-11-07 08:49:05 +13:00
|
|
|
import { Vue, Component, Ref, Watch, Prop } from 'vue-property-decorator'
|
|
|
|
import ResizeObserver from 'resize-observer-polyfill'
|
2020-11-08 01:26:07 +13:00
|
|
|
import EventEmitter from 'eventemitter3'
|
2020-11-07 08:49:05 +13:00
|
|
|
|
2020-12-27 11:21:49 +13:00
|
|
|
import { NekoApi, MembersApi, RoomApi } from './internal/api'
|
2020-11-29 09:47:16 +13:00
|
|
|
import { NekoWebSocket } from './internal/websocket'
|
2021-02-08 03:49:10 +13:00
|
|
|
import { NekoWebRTC, WebRTCStats } from './internal/webrtc'
|
2020-11-29 09:47:16 +13:00
|
|
|
import { NekoMessages } from './internal/messages'
|
|
|
|
import { register as VideoRegister } from './internal/video'
|
2020-11-07 08:49:05 +13:00
|
|
|
|
2020-11-29 09:47:16 +13:00
|
|
|
import NekoState from './types/state'
|
2020-11-07 08:49:05 +13:00
|
|
|
import Overlay from './overlay.vue'
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
name: 'neko-canvas',
|
|
|
|
components: {
|
|
|
|
'neko-overlay': Overlay,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
export default class extends Vue {
|
|
|
|
@Ref('component') readonly _component!: HTMLElement
|
|
|
|
@Ref('container') readonly _container!: HTMLElement
|
2020-11-11 07:57:52 +13:00
|
|
|
@Ref('video') readonly _video!: HTMLVideoElement
|
2020-11-07 08:49:05 +13:00
|
|
|
|
2020-11-30 03:34:52 +13:00
|
|
|
api = new NekoApi()
|
2020-11-11 07:57:52 +13:00
|
|
|
websocket = new NekoWebSocket()
|
|
|
|
webrtc = new NekoWebRTC()
|
|
|
|
observer = new ResizeObserver(this.onResize.bind(this))
|
2020-11-07 08:49:05 +13:00
|
|
|
|
2021-02-01 02:06:08 +13:00
|
|
|
@Prop({ type: Boolean })
|
|
|
|
private readonly autoplay!: boolean
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
/////////////////////////////
|
|
|
|
// Public state
|
|
|
|
/////////////////////////////
|
2020-11-08 08:12:13 +13:00
|
|
|
public state = {
|
2020-11-09 07:38:14 +13:00
|
|
|
connection: {
|
2021-01-30 11:14:59 +13:00
|
|
|
authenticated: false,
|
2021-02-01 01:54:47 +13:00
|
|
|
websocket: this.websocket.supported ? 'disconnected' : 'unavailable',
|
2021-02-08 05:28:15 +13:00
|
|
|
webrtc: {
|
|
|
|
status: this.webrtc.supported ? 'disconnected' : 'unavailable',
|
|
|
|
stats: null,
|
|
|
|
video: null,
|
|
|
|
videos: [],
|
|
|
|
},
|
2020-11-09 07:38:14 +13:00
|
|
|
type: 'none',
|
2020-11-07 08:49:05 +13:00
|
|
|
},
|
2020-11-09 07:38:14 +13:00
|
|
|
video: {
|
|
|
|
playable: false,
|
|
|
|
playing: false,
|
|
|
|
volume: 0,
|
2020-11-30 00:06:01 +13:00
|
|
|
muted: false,
|
2020-11-11 07:57:52 +13:00
|
|
|
fullscreen: false,
|
2020-11-08 06:47:02 +13:00
|
|
|
},
|
2020-11-09 07:38:14 +13:00
|
|
|
control: {
|
|
|
|
scroll: {
|
|
|
|
inverse: true,
|
2020-11-09 09:19:46 +13:00
|
|
|
sensitivity: 1,
|
2020-11-09 07:38:14 +13:00
|
|
|
},
|
2021-01-10 11:28:56 +13:00
|
|
|
cursor: null,
|
2020-12-01 10:00:07 +13:00
|
|
|
clipboard: null,
|
2020-12-02 11:59:02 +13:00
|
|
|
host_id: null,
|
2020-12-02 23:24:27 +13:00
|
|
|
implicit_hosting: false,
|
2020-11-09 07:38:14 +13:00
|
|
|
},
|
|
|
|
screen: {
|
|
|
|
size: {
|
|
|
|
width: 1280,
|
|
|
|
height: 720,
|
|
|
|
rate: 30,
|
|
|
|
},
|
|
|
|
configurations: [],
|
|
|
|
},
|
2020-12-02 11:59:02 +13:00
|
|
|
member_id: null,
|
2020-12-02 10:58:05 +13:00
|
|
|
members: {},
|
2020-11-07 10:19:41 +13:00
|
|
|
} as NekoState
|
2020-11-07 08:49:05 +13:00
|
|
|
|
2021-01-30 11:14:59 +13:00
|
|
|
public get authenticated() {
|
|
|
|
return this.state.connection.authenticated
|
|
|
|
}
|
|
|
|
|
2020-11-08 08:12:13 +13:00
|
|
|
public get connected() {
|
2020-12-08 06:46:29 +13:00
|
|
|
return this.state.connection.websocket == 'connected'
|
|
|
|
}
|
|
|
|
|
|
|
|
public get watching() {
|
2021-02-08 05:28:15 +13:00
|
|
|
return this.state.connection.webrtc.status == 'connected'
|
2020-11-08 08:12:13 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 09:46:22 +13:00
|
|
|
public get controlling() {
|
2020-12-02 11:59:02 +13:00
|
|
|
return this.state.control.host_id !== null && this.state.member_id === this.state.control.host_id
|
2020-11-11 09:46:22 +13:00
|
|
|
}
|
|
|
|
|
2020-12-07 08:30:34 +13:00
|
|
|
public get is_admin() {
|
|
|
|
return this.state.member_id != null ? this.state.members[this.state.member_id].profile.is_admin : false
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
/////////////////////////////
|
|
|
|
// Public events
|
|
|
|
/////////////////////////////
|
|
|
|
public events = new NekoMessages(this.websocket, this.state)
|
|
|
|
|
|
|
|
/////////////////////////////
|
|
|
|
// Public methods
|
|
|
|
/////////////////////////////
|
2021-01-30 11:21:14 +13:00
|
|
|
public setUrl(url: string) {
|
2021-01-30 11:14:59 +13:00
|
|
|
const httpURL = url.replace(/^ws/, 'http').replace(/\/$|\/ws\/?$/, '')
|
|
|
|
this.api.setUrl(httpURL)
|
2021-02-09 05:50:12 +13:00
|
|
|
this.websocket.setUrl(httpURL)
|
2021-01-30 11:14:59 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
public async login(id: string, secret: string) {
|
|
|
|
if (this.authenticated) {
|
|
|
|
throw new Error('client already authenticated')
|
2020-11-09 10:02:00 +13:00
|
|
|
}
|
|
|
|
|
2021-02-01 02:25:17 +13:00
|
|
|
await this.api.session.login({ id, secret })
|
|
|
|
Vue.set(this.state.connection, 'authenticated', true)
|
2021-02-09 05:50:12 +13:00
|
|
|
this.websocket.connect()
|
2021-01-30 11:14:59 +13:00
|
|
|
}
|
2020-11-30 03:34:52 +13:00
|
|
|
|
2021-01-30 11:14:59 +13:00
|
|
|
public async logout() {
|
|
|
|
if (!this.authenticated) {
|
|
|
|
throw new Error('client not authenticated')
|
|
|
|
}
|
|
|
|
|
2021-02-09 05:50:12 +13:00
|
|
|
if (this.connected) {
|
|
|
|
this.websocketDisconnect()
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
await this.api.session.logout()
|
|
|
|
} finally {
|
|
|
|
Vue.set(this.state.connection, 'authenticated', false)
|
|
|
|
}
|
2020-11-11 07:57:52 +13:00
|
|
|
}
|
|
|
|
|
2021-01-30 11:14:59 +13:00
|
|
|
public websocketConnect() {
|
|
|
|
if (!this.authenticated) {
|
|
|
|
throw new Error('client not authenticated')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.connected) {
|
|
|
|
throw new Error('client already connected to websocket')
|
|
|
|
}
|
|
|
|
|
2021-02-09 05:50:12 +13:00
|
|
|
this.websocket.connect()
|
2021-01-30 11:14:59 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
public websocketDisconnect() {
|
|
|
|
if (!this.authenticated) {
|
|
|
|
throw new Error('client not authenticated')
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
if (!this.connected) {
|
2021-01-30 11:14:59 +13:00
|
|
|
throw new Error('client not connected to websocket')
|
2020-11-09 10:02:00 +13:00
|
|
|
}
|
2020-11-09 10:48:04 +13:00
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
this.websocket.disconnect()
|
|
|
|
}
|
|
|
|
|
2020-12-08 06:46:29 +13:00
|
|
|
public webrtcConnect() {
|
|
|
|
if (!this.connected) {
|
|
|
|
throw new Error('client not connected to websocket')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.watching) {
|
|
|
|
throw new Error('client already connected to webrtc')
|
|
|
|
}
|
|
|
|
|
|
|
|
this.websocket.send('signal/request')
|
|
|
|
}
|
|
|
|
|
|
|
|
public webrtcDisconnect() {
|
|
|
|
if (!this.connected) {
|
|
|
|
throw new Error('client not connected to websocket')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.watching) {
|
|
|
|
throw new Error('client not connected to webrtc')
|
|
|
|
}
|
|
|
|
|
|
|
|
this.webrtc.disconnect()
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public play() {
|
|
|
|
this._video.play()
|
2020-11-09 10:02:00 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public pause() {
|
|
|
|
this._video.pause()
|
|
|
|
}
|
|
|
|
|
2020-11-30 00:06:01 +13:00
|
|
|
public mute() {
|
|
|
|
this._video.muted = true
|
|
|
|
}
|
|
|
|
|
|
|
|
public unmute() {
|
|
|
|
this._video.muted = false
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public setVolume(value: number) {
|
2020-11-09 10:02:00 +13:00
|
|
|
if (value < 0 || value > 1) {
|
|
|
|
throw new Error('Out of range. Value must be between 0 and 1.')
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
this._video.volume = value
|
2020-11-09 10:02:00 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public requestFullscreen() {
|
|
|
|
this._component.requestFullscreen()
|
2020-11-09 09:19:46 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public exitFullscreen() {
|
|
|
|
document.exitFullscreen()
|
2020-11-09 10:16:47 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public setScrollInverse(value: boolean = true) {
|
|
|
|
Vue.set(this.state.control.scroll, 'inverse', value)
|
2020-11-07 08:49:05 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public setScrollSensitivity(value: number) {
|
|
|
|
Vue.set(this.state.control.scroll, 'sensitivity', value)
|
2020-11-08 06:47:02 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
public setScreenSize(width: number, height: number, rate: number) {
|
2020-12-27 11:59:29 +13:00
|
|
|
//this.api.room.screenConfigurationChange({ screenConfiguration: { width, height, rate } })
|
2020-12-01 10:00:07 +13:00
|
|
|
this.websocket.send('screen/set', { width, height, rate })
|
2020-11-07 08:49:05 +13:00
|
|
|
}
|
|
|
|
|
2021-02-08 05:28:15 +13:00
|
|
|
public setWebRTCVideo(video: string) {
|
|
|
|
if (!this.state.connection.webrtc.videos.includes(video)) {
|
|
|
|
throw new Error('VideoID not found.')
|
|
|
|
}
|
|
|
|
|
|
|
|
this.websocket.send('signal/video', { video: video })
|
|
|
|
}
|
|
|
|
|
2021-01-29 05:11:26 +13:00
|
|
|
public sendUnicast(receiver: string, subject: string, body: any) {
|
2021-01-29 04:08:17 +13:00
|
|
|
this.websocket.send('send/unicast', { receiver, subject, body })
|
|
|
|
}
|
|
|
|
|
2021-01-29 05:11:26 +13:00
|
|
|
public sendBroadcast(subject: string, body: any) {
|
2021-01-29 04:08:17 +13:00
|
|
|
this.websocket.send('send/broadcast', { subject, body })
|
|
|
|
}
|
|
|
|
|
2020-12-27 11:21:49 +13:00
|
|
|
public get room(): RoomApi {
|
|
|
|
return this.api.room
|
2020-12-03 01:52:02 +13:00
|
|
|
}
|
|
|
|
|
2020-12-27 11:21:49 +13:00
|
|
|
public get members(): MembersApi {
|
|
|
|
return this.api.members
|
2020-12-03 01:52:02 +13:00
|
|
|
}
|
|
|
|
|
2021-01-09 09:36:59 +13:00
|
|
|
async uploadDrop({ x, y, files }: { x: number; y: number; files: Array<Blob> }) {
|
|
|
|
try {
|
|
|
|
this.events.emit('upload.drop.started')
|
|
|
|
|
|
|
|
await this.api.room.uploadDrop(x, y, files, {
|
|
|
|
onUploadProgress: (progressEvent: ProgressEvent) => {
|
|
|
|
this.events.emit('upload.drop.progress', progressEvent)
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
this.events.emit('upload.drop.finished', null)
|
|
|
|
} catch (err) {
|
|
|
|
this.events.emit('upload.drop.finished', err)
|
|
|
|
}
|
2021-01-09 08:51:38 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
/////////////////////////////
|
|
|
|
// Component lifecycle
|
|
|
|
/////////////////////////////
|
|
|
|
mounted() {
|
|
|
|
// component size change
|
2020-11-07 08:49:05 +13:00
|
|
|
this.observer.observe(this._component)
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
// fullscreen change
|
2020-11-09 10:16:47 +13:00
|
|
|
this._component.addEventListener('fullscreenchange', () => {
|
2020-11-11 07:57:52 +13:00
|
|
|
Vue.set(this.state.video, 'fullscreen', document.fullscreenElement !== null)
|
2020-11-09 10:16:47 +13:00
|
|
|
this.onResize()
|
|
|
|
})
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
// video events
|
|
|
|
VideoRegister(this._video, this.state.video)
|
2020-11-09 09:19:46 +13:00
|
|
|
|
2020-11-09 10:44:37 +13:00
|
|
|
// websocket
|
2020-11-07 08:49:05 +13:00
|
|
|
this.websocket.on('message', async (event: string, payload: any) => {
|
|
|
|
switch (event) {
|
|
|
|
case 'signal/provide':
|
|
|
|
try {
|
|
|
|
let sdp = await this.webrtc.connect(payload.sdp, payload.lite, payload.ice)
|
2020-11-29 03:28:11 +13:00
|
|
|
this.websocket.send('signal/answer', { sdp })
|
2020-11-07 08:49:05 +13:00
|
|
|
} catch (e) {}
|
|
|
|
break
|
2021-02-03 08:27:23 +13:00
|
|
|
case 'signal/candidate':
|
|
|
|
this.webrtc.setCandidate(payload)
|
|
|
|
break
|
2020-11-07 08:49:05 +13:00
|
|
|
}
|
|
|
|
})
|
|
|
|
this.websocket.on('connecting', () => {
|
2020-11-09 07:38:14 +13:00
|
|
|
Vue.set(this.state.connection, 'websocket', 'connecting')
|
2021-02-01 02:25:17 +13:00
|
|
|
this.events.emit('connection.websocket', 'connecting')
|
2020-11-07 08:49:05 +13:00
|
|
|
})
|
|
|
|
this.websocket.on('connected', () => {
|
2020-11-09 07:38:14 +13:00
|
|
|
Vue.set(this.state.connection, 'websocket', 'connected')
|
2021-02-01 02:25:17 +13:00
|
|
|
this.events.emit('connection.websocket', 'connected')
|
2020-12-08 06:46:29 +13:00
|
|
|
this.webrtcConnect()
|
2020-11-07 08:49:05 +13:00
|
|
|
})
|
|
|
|
this.websocket.on('disconnected', () => {
|
2020-11-09 07:38:14 +13:00
|
|
|
Vue.set(this.state.connection, 'websocket', 'disconnected')
|
2021-02-01 02:25:17 +13:00
|
|
|
this.events.emit('connection.websocket', 'disconnected')
|
2020-11-11 07:57:52 +13:00
|
|
|
|
2020-12-04 08:16:24 +13:00
|
|
|
this.webrtc.disconnect()
|
|
|
|
this.clearState()
|
2021-02-08 04:43:53 +13:00
|
|
|
|
|
|
|
// reconnect WebRTC
|
|
|
|
if (this.authenticated) {
|
|
|
|
setTimeout(() => {
|
|
|
|
try {
|
2021-02-09 05:50:12 +13:00
|
|
|
this.websocket.connect()
|
2021-02-08 04:43:53 +13:00
|
|
|
} catch (e) {}
|
|
|
|
}, 1000)
|
|
|
|
}
|
2020-11-07 08:49:05 +13:00
|
|
|
})
|
|
|
|
|
2020-11-09 10:44:37 +13:00
|
|
|
// webrtc
|
2020-11-07 08:49:05 +13:00
|
|
|
this.webrtc.on('track', (event: RTCTrackEvent) => {
|
|
|
|
const { track, streams } = event
|
2020-11-08 06:47:02 +13:00
|
|
|
if (track.kind === 'audio') return
|
2020-11-07 08:49:05 +13:00
|
|
|
|
2020-11-09 10:44:37 +13:00
|
|
|
// create stream
|
2020-11-11 07:57:52 +13:00
|
|
|
if ('srcObject' in this._video) {
|
|
|
|
this._video.srcObject = streams[0]
|
2020-11-07 08:49:05 +13:00
|
|
|
} else {
|
|
|
|
// @ts-ignore
|
2020-11-11 07:57:52 +13:00
|
|
|
this._video.src = window.URL.createObjectURL(streams[0]) // for older browsers
|
2020-11-07 08:49:05 +13:00
|
|
|
}
|
|
|
|
|
2021-02-01 02:06:08 +13:00
|
|
|
if (this.autoplay) {
|
|
|
|
this._video.play()
|
|
|
|
}
|
2020-11-07 08:49:05 +13:00
|
|
|
})
|
2021-02-03 08:27:23 +13:00
|
|
|
this.webrtc.on('candidate', (candidate: RTCIceCandidateInit) => {
|
|
|
|
this.websocket.send('signal/candidate', candidate)
|
|
|
|
})
|
2021-02-08 03:49:10 +13:00
|
|
|
this.webrtc.on('stats', (stats: WebRTCStats) => {
|
2021-02-08 05:28:15 +13:00
|
|
|
Vue.set(this.state.connection.webrtc, 'stats', stats)
|
2021-02-08 03:49:10 +13:00
|
|
|
})
|
2020-11-07 08:49:05 +13:00
|
|
|
this.webrtc.on('connecting', () => {
|
2021-02-08 05:28:15 +13:00
|
|
|
Vue.set(this.state.connection.webrtc, 'status', 'connecting')
|
2021-02-01 02:25:17 +13:00
|
|
|
this.events.emit('connection.webrtc', 'connecting')
|
2020-11-07 08:49:05 +13:00
|
|
|
})
|
|
|
|
this.webrtc.on('connected', () => {
|
2021-02-08 05:28:15 +13:00
|
|
|
Vue.set(this.state.connection.webrtc, 'status', 'connected')
|
2021-02-01 02:47:12 +13:00
|
|
|
Vue.set(this.state.connection, 'type', 'webrtc')
|
2021-02-01 02:25:17 +13:00
|
|
|
this.events.emit('connection.webrtc', 'connected')
|
2020-11-07 08:49:05 +13:00
|
|
|
})
|
|
|
|
this.webrtc.on('disconnected', () => {
|
2021-02-08 05:28:15 +13:00
|
|
|
Vue.set(this.state.connection.webrtc, 'status', 'disconnected')
|
|
|
|
Vue.set(this.state.connection.webrtc, 'stats', null)
|
|
|
|
Vue.set(this.state.connection.webrtc, 'video', null)
|
|
|
|
Vue.set(this.state.connection.webrtc, 'videos', [])
|
2021-02-01 02:47:12 +13:00
|
|
|
Vue.set(this.state.connection, 'type', 'none')
|
2021-02-01 02:25:17 +13:00
|
|
|
this.events.emit('connection.webrtc', 'disconnected')
|
2020-12-08 06:44:14 +13:00
|
|
|
|
2020-12-08 07:45:07 +13:00
|
|
|
if (!this._video) return
|
|
|
|
|
2020-12-08 06:44:14 +13:00
|
|
|
// destroy stream
|
|
|
|
if ('srcObject' in this._video) {
|
|
|
|
this._video.srcObject = null
|
|
|
|
} else {
|
|
|
|
// @ts-ignore
|
|
|
|
this._video.removeAttribute('src')
|
|
|
|
}
|
2021-02-08 04:43:53 +13:00
|
|
|
|
|
|
|
// reconnect WebRTC
|
|
|
|
if (this.connected) {
|
|
|
|
setTimeout(() => {
|
|
|
|
try {
|
|
|
|
this.webrtcConnect()
|
|
|
|
} catch (e) {}
|
|
|
|
}, 1000)
|
|
|
|
}
|
2020-11-07 08:49:05 +13:00
|
|
|
})
|
2020-11-11 07:57:52 +13:00
|
|
|
|
2021-01-30 11:14:59 +13:00
|
|
|
// check if is user logged in
|
|
|
|
this.api.session.whoami().then(() => {
|
|
|
|
Vue.set(this.state.connection, 'authenticated', true)
|
2021-02-09 05:50:12 +13:00
|
|
|
this.websocket.connect()
|
2021-01-30 11:14:59 +13:00
|
|
|
})
|
2021-01-31 00:37:41 +13:00
|
|
|
|
|
|
|
// unmute on users first interaction
|
2021-02-01 02:06:08 +13:00
|
|
|
if (this.autoplay) {
|
2021-02-01 02:47:12 +13:00
|
|
|
document.addEventListener('click', () => this.unmute(), { once: true })
|
2021-02-01 02:06:08 +13:00
|
|
|
}
|
2020-11-07 08:49:05 +13:00
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
beforeDestroy() {
|
2020-11-08 08:12:13 +13:00
|
|
|
this.observer.disconnect()
|
2020-11-07 08:49:05 +13:00
|
|
|
this.webrtc.disconnect()
|
|
|
|
this.websocket.disconnect()
|
|
|
|
}
|
|
|
|
|
2020-11-11 07:57:52 +13:00
|
|
|
@Watch('state.screen.size')
|
|
|
|
onResize() {
|
2020-11-09 07:38:14 +13:00
|
|
|
const { width, height } = this.state.screen.size
|
2020-11-07 08:49:05 +13:00
|
|
|
const screen_ratio = width / height
|
|
|
|
|
|
|
|
const { offsetWidth, offsetHeight } = this._component
|
|
|
|
const canvas_ratio = offsetWidth / offsetHeight
|
|
|
|
|
2020-11-09 10:44:37 +13:00
|
|
|
// vertical centering
|
2020-11-07 10:19:41 +13:00
|
|
|
if (screen_ratio > canvas_ratio) {
|
2020-11-07 08:49:05 +13:00
|
|
|
const vertical = offsetWidth / screen_ratio
|
|
|
|
this._container.style.width = `${offsetWidth}px`
|
|
|
|
this._container.style.height = `${vertical}px`
|
|
|
|
this._container.style.marginTop = `${(offsetHeight - vertical) / 2}px`
|
|
|
|
this._container.style.marginLeft = `0px`
|
|
|
|
}
|
2020-11-09 10:44:37 +13:00
|
|
|
// horizontal centering
|
2020-11-07 10:19:41 +13:00
|
|
|
else if (screen_ratio < canvas_ratio) {
|
2020-11-07 08:49:05 +13:00
|
|
|
const horizontal = screen_ratio * offsetHeight
|
|
|
|
this._container.style.width = `${horizontal}px`
|
|
|
|
this._container.style.height = `${offsetHeight}px`
|
|
|
|
this._container.style.marginTop = `0px`
|
|
|
|
this._container.style.marginLeft = `${(offsetWidth - horizontal) / 2}px`
|
|
|
|
}
|
2020-11-09 10:44:37 +13:00
|
|
|
// no centering
|
2020-11-07 08:49:05 +13:00
|
|
|
else {
|
|
|
|
this._container.style.width = `${offsetWidth}px`
|
|
|
|
this._container.style.height = `${offsetHeight}px`
|
|
|
|
this._container.style.marginTop = `0px`
|
|
|
|
this._container.style.marginLeft = `0px`
|
|
|
|
}
|
|
|
|
}
|
2020-12-04 08:16:24 +13:00
|
|
|
|
|
|
|
clearState() {
|
2021-01-30 08:41:57 +13:00
|
|
|
Vue.set(this.state.control, 'cursor', null)
|
2020-12-04 08:16:24 +13:00
|
|
|
Vue.set(this.state.control, 'clipboard', null)
|
|
|
|
Vue.set(this.state.control, 'host_id', null)
|
|
|
|
Vue.set(this.state.control, 'implicit_hosting', false)
|
|
|
|
Vue.set(this.state.screen, 'size', { width: 1280, height: 720, rate: 30 })
|
|
|
|
Vue.set(this.state.screen, 'configurations', [])
|
|
|
|
Vue.set(this.state, 'member_id', null)
|
|
|
|
Vue.set(this.state, 'members', {})
|
|
|
|
}
|
2020-11-07 08:49:05 +13:00
|
|
|
}
|
|
|
|
</script>
|