mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
split compnent & spa.
This commit is contained in:
parent
7c9be1fd45
commit
67573f26ef
155
src/Neko.vue
155
src/Neko.vue
@ -1,155 +0,0 @@
|
||||
<template>
|
||||
<div ref="component" class="video">
|
||||
<button @click="connect()">Connect</button>
|
||||
<button @click="disconnect()">Disonnect</button><br />
|
||||
websocket_state: {{ websocket_state }}<br />
|
||||
webrtc_state: {{ webrtc_state }}<br />
|
||||
|
||||
<div ref="container" class="player-container">
|
||||
<video ref="video" />
|
||||
<neko-overlay
|
||||
v-if="websocket_state == 'connected' && webrtc_state == 'connected'"
|
||||
:webrtc="webrtc"
|
||||
:screenWidth="1280"
|
||||
:screenHeight="720"
|
||||
:scrollSensitivity="5"
|
||||
:scrollInvert="true"
|
||||
:isControling="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.player-container {
|
||||
position: relative;
|
||||
width: 1280px;
|
||||
height: 720px;
|
||||
|
||||
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">
|
||||
import ResizeObserver from 'resize-observer-polyfill'
|
||||
import { Vue, Component, Ref, Watch } from 'vue-property-decorator'
|
||||
import { NekoWebSocket } from './internal/websocket'
|
||||
import { NekoWebRTC } from './internal/webrtc'
|
||||
|
||||
import Overlay from '~/components/overlay.vue'
|
||||
|
||||
@Component({
|
||||
name: 'neko',
|
||||
components: {
|
||||
'neko-overlay': Overlay,
|
||||
},
|
||||
})
|
||||
export default class extends Vue {
|
||||
@Ref('component') readonly _component!: HTMLElement
|
||||
@Ref('container') readonly _container!: HTMLElement
|
||||
@Ref('video') readonly _video!: HTMLVideoElement
|
||||
|
||||
private observer = new ResizeObserver(this.onResize.bind(this))
|
||||
|
||||
websocket: NekoWebSocket | null = null
|
||||
webrtc: NekoWebRTC | null = null
|
||||
|
||||
private websocket_state = 'disconnected'
|
||||
private webrtc_state = 'disconnected'
|
||||
|
||||
public connect() {
|
||||
try {
|
||||
this.websocket?.connect('ws://192.168.1.20:3000/', 'admin')
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
this.websocket?.disconnect()
|
||||
}
|
||||
|
||||
mounted() {
|
||||
// Update canvas on resize
|
||||
this._container.addEventListener('resize', this.onResize)
|
||||
this.observer.observe(this._component)
|
||||
|
||||
// WebSocket
|
||||
this.websocket = new NekoWebSocket()
|
||||
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)
|
||||
this.websocket?.send('signal/answer', { sdp, displayname: 'test' })
|
||||
} catch (e) {}
|
||||
break
|
||||
case 'screen/resolution':
|
||||
payload.width
|
||||
payload.height
|
||||
payload.rate
|
||||
break
|
||||
default:
|
||||
console.log(event, payload)
|
||||
}
|
||||
})
|
||||
this.websocket?.on('connecting', () => {
|
||||
this.websocket_state = 'connecting'
|
||||
})
|
||||
this.websocket?.on('connected', () => {
|
||||
this.websocket_state = 'connected'
|
||||
})
|
||||
this.websocket?.on('disconnected', () => {
|
||||
this.websocket_state = 'disconnected'
|
||||
this.webrtc?.disconnect()
|
||||
})
|
||||
|
||||
// WebRTC
|
||||
this.webrtc = new NekoWebRTC()
|
||||
this.webrtc?.on('track', (event: RTCTrackEvent) => {
|
||||
const { track, streams } = event
|
||||
if (track.kind === 'audio') {
|
||||
return
|
||||
}
|
||||
|
||||
// Create stream
|
||||
if ('srcObject' in this._video) {
|
||||
this._video.srcObject = streams[0]
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._video.src = window.URL.createObjectURL(streams[0]) // for older browsers
|
||||
}
|
||||
|
||||
this._video.play()
|
||||
})
|
||||
this.webrtc?.on('connecting', () => {
|
||||
this.webrtc_state = 'connecting'
|
||||
})
|
||||
this.webrtc?.on('connected', () => {
|
||||
this.webrtc_state = 'connected'
|
||||
})
|
||||
this.webrtc?.on('disconnected', () => {
|
||||
this.webrtc_state = 'disconnected'
|
||||
})
|
||||
}
|
||||
|
||||
destroyed() {
|
||||
this.webrtc?.disconnect()
|
||||
this.websocket?.disconnect()
|
||||
}
|
||||
|
||||
public onResize() {
|
||||
console.log('Resize event triggered.')
|
||||
}
|
||||
}
|
||||
</script>
|
54
src/app.vue
Normal file
54
src/app.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<button @click="connect()">Connect</button>
|
||||
<button @click="disconnect()">Disonnect</button>
|
||||
<button @click="neko.control.request()">request control</button>
|
||||
<button @click="neko.control.release()">release control</button>
|
||||
<button @click="neko.video.pause()">stop</button><br />
|
||||
W: <input type="text" v-model="width" /><br />
|
||||
H: <input type="text" v-model="height" /><br />
|
||||
<button @click="resize()">Resize</button>
|
||||
<br />
|
||||
<!--websocket_state: {{ websocket_state }}<br />
|
||||
webrtc_state: {{ webrtc_state }}<br />-->
|
||||
|
||||
<div ref="container" style="width:1280px;height:720px;border: 2px solid red;">
|
||||
<neko-canvas ref="neko" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component, Ref, Watch } from 'vue-property-decorator'
|
||||
|
||||
import Neko from '~/components/canvas.vue'
|
||||
|
||||
@Component({
|
||||
name: 'neko',
|
||||
components: {
|
||||
'neko-canvas': Neko,
|
||||
},
|
||||
})
|
||||
export default class extends Vue {
|
||||
@Ref('container') readonly container!: HTMLElement
|
||||
@Ref('neko') readonly neko!: Neko
|
||||
|
||||
width = '720px'
|
||||
height = '1280px'
|
||||
|
||||
connect() {
|
||||
this.neko.connect('ws://192.168.1.20:3000/', 'neko')
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.neko.disconnect()
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.container.style.width = this.width
|
||||
this.container.style.height = this.height
|
||||
}
|
||||
}
|
||||
</script>
|
202
src/components/canvas.vue
Normal file
202
src/components/canvas.vue
Normal file
@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div ref="component" class="component">
|
||||
<div ref="container" class="player-container">
|
||||
<video ref="video" />
|
||||
<neko-overlay
|
||||
v-if="websocket_state == 'connected' && webrtc_state == 'connected'"
|
||||
:webrtc="webrtc"
|
||||
:screenWidth="state.screen_size.width"
|
||||
:screenHeight="state.screen_size.height"
|
||||
:scrollSensitivity="5"
|
||||
:scrollInvert="true"
|
||||
:isControling="true"
|
||||
/>
|
||||
</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">
|
||||
import { Vue, Component, Ref, Watch, Prop } from 'vue-property-decorator'
|
||||
import ResizeObserver from 'resize-observer-polyfill'
|
||||
|
||||
import { NekoWebSocket } from '~/internal/websocket'
|
||||
import { NekoWebRTC } from '~/internal/webrtc'
|
||||
|
||||
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
|
||||
@Ref('video') public readonly video!: HTMLVideoElement
|
||||
|
||||
private websocket = new NekoWebSocket()
|
||||
private webrtc = new NekoWebRTC()
|
||||
private observer = new ResizeObserver(this.onResize.bind(this))
|
||||
|
||||
private state = {
|
||||
screen_size: {
|
||||
width: Number,
|
||||
height: Number,
|
||||
rate: Number,
|
||||
},
|
||||
}
|
||||
|
||||
private websocket_state = 'disconnected'
|
||||
private webrtc_state = 'disconnected'
|
||||
|
||||
public control = {
|
||||
request: () => {
|
||||
this.websocket.send('control/request')
|
||||
},
|
||||
release: () => {
|
||||
this.websocket.send('control/release')
|
||||
},
|
||||
}
|
||||
|
||||
public connect(url: string, password: string) {
|
||||
if (this.websocket.connected) {
|
||||
throw new Error('client already connected')
|
||||
}
|
||||
|
||||
this.websocket.connect(url, password)
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
if (!this.websocket.connected) {
|
||||
throw new Error('client not connected')
|
||||
}
|
||||
|
||||
this.websocket.disconnect()
|
||||
}
|
||||
|
||||
private mounted() {
|
||||
// Update canvas on resize
|
||||
this.observer.observe(this._component)
|
||||
|
||||
// WebSocket
|
||||
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)
|
||||
this.websocket.send('signal/answer', { sdp, displayname: 'test' })
|
||||
} catch (e) {}
|
||||
break
|
||||
case 'screen/resolution':
|
||||
Vue.set(this.state, 'screen_size', payload)
|
||||
this.onResize()
|
||||
break
|
||||
default:
|
||||
console.log(event, payload)
|
||||
}
|
||||
})
|
||||
this.websocket.on('connecting', () => {
|
||||
this.websocket_state = 'connecting'
|
||||
})
|
||||
this.websocket.on('connected', () => {
|
||||
this.websocket_state = 'connected'
|
||||
})
|
||||
this.websocket.on('disconnected', () => {
|
||||
this.websocket_state = 'disconnected'
|
||||
this.webrtc.disconnect()
|
||||
})
|
||||
|
||||
// WebRTC
|
||||
this.webrtc.on('track', (event: RTCTrackEvent) => {
|
||||
const { track, streams } = event
|
||||
if (track.kind === 'audio') {
|
||||
return
|
||||
}
|
||||
|
||||
// Create stream
|
||||
if ('srcObject' in this.video) {
|
||||
this.video.srcObject = streams[0]
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this.video.src = window.URL.createObjectURL(streams[0]) // for older browsers
|
||||
}
|
||||
|
||||
this.video.play()
|
||||
})
|
||||
this.webrtc.on('connecting', () => {
|
||||
this.webrtc_state = 'connecting'
|
||||
})
|
||||
this.webrtc.on('connected', () => {
|
||||
this.webrtc_state = 'connected'
|
||||
})
|
||||
this.webrtc.on('disconnected', () => {
|
||||
this.webrtc_state = 'disconnected'
|
||||
})
|
||||
}
|
||||
|
||||
private beforeDestroy() {
|
||||
this.webrtc.disconnect()
|
||||
this.websocket.disconnect()
|
||||
}
|
||||
|
||||
private onResize() {
|
||||
console.log('Resize event triggered.')
|
||||
|
||||
const { width, height } = this.state.screen_size
|
||||
const screen_ratio = width / height
|
||||
|
||||
const { offsetWidth, offsetHeight } = this._component
|
||||
const canvas_ratio = offsetWidth / offsetHeight
|
||||
|
||||
// Vertical centering
|
||||
if(screen_ratio > canvas_ratio) {
|
||||
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`
|
||||
}
|
||||
// Horizontal centering
|
||||
else if(screen_ratio < canvas_ratio) {
|
||||
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`
|
||||
}
|
||||
// No centering
|
||||
else {
|
||||
this._container.style.width = `${offsetWidth}px`
|
||||
this._container.style.height = `${offsetHeight}px`
|
||||
this._container.style.marginTop = `0px`
|
||||
this._container.style.marginLeft = `0px`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
// Import vue component
|
||||
import Neko from './Neko.vue';
|
||||
import Neko from './components/canvas.vue';
|
||||
|
||||
// Declare install function executed by Vue.use()
|
||||
export function install(Vue) {
|
||||
|
Loading…
Reference in New Issue
Block a user