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 vue component
|
||||||
import Neko from './Neko.vue';
|
import Neko from './components/canvas.vue';
|
||||||
|
|
||||||
// Declare install function executed by Vue.use()
|
// Declare install function executed by Vue.use()
|
||||||
export function install(Vue) {
|
export function install(Vue) {
|
||||||
|
Loading…
Reference in New Issue
Block a user