fix: stop webcam when webcam panel is collapse (#839)
This commit is contained in:
parent
bbf2832238
commit
6ffb771eeb
@ -1,6 +1,7 @@
|
||||
<style>
|
||||
.webcamImage {
|
||||
width: 100%;
|
||||
background: lightgray;
|
||||
}
|
||||
|
||||
.webcamFpsOutput {
|
||||
@ -16,7 +17,12 @@
|
||||
|
||||
<template>
|
||||
<div style="position: relative">
|
||||
<img ref="image" class="webcamImage" :style="webcamStyle" />
|
||||
<img
|
||||
ref="image"
|
||||
v-observe-visibility="visibilityChanged"
|
||||
class="webcamImage"
|
||||
:style="webcamStyle"
|
||||
@load="onload" />
|
||||
<span v-if="showFps" class="webcamFpsOutput">{{ $t('Panels.WebcamPanel.FPS') }}: {{ fpsOutput }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -32,6 +38,8 @@ const TYPE_JPEG = 'image/jpeg'
|
||||
@Component
|
||||
export default class Mjpegstreamer extends Mixins(BaseMixin) {
|
||||
private currentFPS = 0
|
||||
private streamState = false
|
||||
private aspectRatio: null | number = null
|
||||
private timerFPS: number | null = null
|
||||
private timerRestart: number | null = null
|
||||
private stream: ReadableStream | null = null
|
||||
@ -58,12 +66,19 @@ export default class Mjpegstreamer extends Mixins(BaseMixin) {
|
||||
}
|
||||
|
||||
get webcamStyle() {
|
||||
const output = {
|
||||
transform: 'none',
|
||||
aspectRatio: 16 / 9,
|
||||
}
|
||||
|
||||
let transforms = ''
|
||||
if ('flipX' in this.camSettings && this.camSettings.flipX) transforms += ' scaleX(-1)'
|
||||
if ('flipX' in this.camSettings && this.camSettings.flipY) transforms += ' scaleY(-1)'
|
||||
if (transforms.trimLeft().length) return { transform: transforms.trimLeft() }
|
||||
if (transforms.trimStart().length) output.transform = transforms.trimStart()
|
||||
|
||||
return ''
|
||||
if (this.aspectRatio) output.aspectRatio = this.aspectRatio
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
get fpsOutput() {
|
||||
@ -71,6 +86,8 @@ export default class Mjpegstreamer extends Mixins(BaseMixin) {
|
||||
}
|
||||
|
||||
startStream() {
|
||||
this.streamState = true
|
||||
|
||||
const SOI = new Uint8Array(2)
|
||||
SOI[0] = 0xff
|
||||
SOI[1] = 0xd8
|
||||
@ -91,7 +108,7 @@ export default class Mjpegstreamer extends Mixins(BaseMixin) {
|
||||
const { signal } = this.controller
|
||||
|
||||
//readable stream credit to from https://github.com/aruntj/mjpeg-readable-stream
|
||||
fetch(this.url, { signal })
|
||||
fetch(this.url, { signal, mode: 'cors' })
|
||||
.then((response) => response.body)
|
||||
.then((rb) => {
|
||||
const reader = rb?.getReader()
|
||||
@ -116,10 +133,8 @@ export default class Mjpegstreamer extends Mixins(BaseMixin) {
|
||||
|
||||
this.stream = new ReadableStream({
|
||||
start(controller) {
|
||||
return pump()
|
||||
|
||||
// The following function handles each data chunk
|
||||
function pump(): any {
|
||||
const pump = (): any => {
|
||||
// "done" is a Boolean and value a "Uint8Array"
|
||||
return reader?.read().then(({ done, value }) => {
|
||||
// If there is no more data to read
|
||||
@ -165,22 +180,25 @@ export default class Mjpegstreamer extends Mixins(BaseMixin) {
|
||||
return pump()
|
||||
})
|
||||
}
|
||||
|
||||
return pump()
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
mounted() {
|
||||
document.addEventListener('visibilitychange', this.visibilityChanged)
|
||||
document.addEventListener('visibilitychange', this.documentVisibilityChanged)
|
||||
this.startStream()
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('visibilitychange', this.visibilityChanged)
|
||||
document.removeEventListener('visibilitychange', this.documentVisibilityChanged)
|
||||
this.stopStream()
|
||||
}
|
||||
|
||||
stopStream() {
|
||||
this.streamState = false
|
||||
URL.revokeObjectURL(this.url)
|
||||
if (this.timerFPS) clearTimeout(this.timerFPS)
|
||||
if (this.timerRestart) clearTimeout(this.timerRestart)
|
||||
@ -195,18 +213,32 @@ export default class Mjpegstreamer extends Mixins(BaseMixin) {
|
||||
|
||||
@Watch('url')
|
||||
urlChanged() {
|
||||
this.aspectRatio = null
|
||||
this.restartStream()
|
||||
}
|
||||
|
||||
visibilityChanged() {
|
||||
// this function check if you changed the browser tab
|
||||
documentVisibilityChanged() {
|
||||
const visibility = document.visibilityState
|
||||
this.visibilityChanged(visibility === 'visible')
|
||||
}
|
||||
|
||||
if (visibility === 'visible') {
|
||||
// this function is to stop the stream, on scroll or on collapse the webcam panel
|
||||
visibilityChanged(newVal: boolean) {
|
||||
if (newVal && this.streamState) return
|
||||
|
||||
if (newVal) {
|
||||
this.startStream()
|
||||
return
|
||||
}
|
||||
|
||||
this.stopStream()
|
||||
}
|
||||
|
||||
onload() {
|
||||
if (this.aspectRatio === null && this.$refs.image) {
|
||||
this.aspectRatio = this.$refs.image.naturalWidth / this.$refs.image.naturalHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -17,6 +17,7 @@
|
||||
</div>
|
||||
<canvas
|
||||
ref="mjpegstreamerAdaptive"
|
||||
v-observe-visibility="visibilityChanged"
|
||||
width="600"
|
||||
height="400"
|
||||
:style="webcamStyle"
|
||||
@ -46,6 +47,7 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
|
||||
private time_smoothing = 0.6
|
||||
private request_time_smoothing = 0.1
|
||||
private currentFPS = 0
|
||||
private aspectRatio: null | number = null
|
||||
|
||||
declare $refs: {
|
||||
mjpegstreamerAdaptive: any
|
||||
@ -56,12 +58,19 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
|
||||
@Prop({ default: true }) declare showFps: boolean
|
||||
|
||||
get webcamStyle() {
|
||||
const output = {
|
||||
transform: 'none',
|
||||
aspectRatio: 16 / 9,
|
||||
}
|
||||
|
||||
let transforms = ''
|
||||
if ('flipX' in this.camSettings && this.camSettings.flipX) transforms += ' scaleX(-1)'
|
||||
if ('flipX' in this.camSettings && this.camSettings.flipY) transforms += ' scaleY(-1)'
|
||||
if (transforms.trimLeft().length) return { transform: transforms.trimLeft() }
|
||||
if (transforms.trimStart().length) output.transform = transforms.trimStart()
|
||||
|
||||
return ''
|
||||
if (this.aspectRatio) output.aspectRatio = this.aspectRatio
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
get fpsOutput() {
|
||||
@ -112,6 +121,9 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
|
||||
|
||||
canvas.width = canvas.clientWidth
|
||||
canvas.height = canvas.clientWidth * (frame.height / frame.width)
|
||||
if (this.aspectRatio === null) {
|
||||
this.aspectRatio = frame.width / frame.height
|
||||
}
|
||||
|
||||
ctx?.drawImage(frame, 0, 0, frame.width, frame.height, 0, 0, canvas.width, canvas.height)
|
||||
this.isLoaded = true
|
||||
@ -132,18 +144,21 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
|
||||
}
|
||||
|
||||
mounted() {
|
||||
document.addEventListener('visibilitychange', this.visibilityChanged)
|
||||
document.addEventListener('visibilitychange', this.documentVisibilityChanged)
|
||||
this.refreshFrame()
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('visibilitychange', this.visibilityChanged)
|
||||
document.removeEventListener('visibilitychange', this.documentVisibilityChanged)
|
||||
}
|
||||
|
||||
visibilityChanged() {
|
||||
documentVisibilityChanged() {
|
||||
const visibility = document.visibilityState
|
||||
this.visibilityChanged(visibility === 'visible')
|
||||
}
|
||||
|
||||
if (visibility === 'visible') {
|
||||
visibilityChanged(newVal: boolean) {
|
||||
if (newVal) {
|
||||
this.isVisible = true
|
||||
this.refreshFrame()
|
||||
return
|
||||
|
@ -1,11 +1,19 @@
|
||||
<style scoped>
|
||||
.webcamImage {
|
||||
width: 100%;
|
||||
background: lightgray;
|
||||
}
|
||||
</style>
|
||||
|
||||
<template>
|
||||
<img ref="webcamUv4lMjpegImage" :src="url" :alt="camSettings.name" :style="webcamStyle" class="webcamImage" />
|
||||
<img
|
||||
ref="webcamUv4lMjpegImage"
|
||||
v-observe-visibility="visibilityChanged"
|
||||
:src="url"
|
||||
:alt="camSettings.name"
|
||||
:style="webcamStyle"
|
||||
class="webcamImage"
|
||||
@load="onload" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -15,6 +23,7 @@ import { GuiWebcamStateWebcam } from '@/store/gui/webcams/types'
|
||||
|
||||
@Component
|
||||
export default class Uv4lMjpeg extends Mixins(BaseMixin) {
|
||||
private aspectRatio: null | number = null
|
||||
@Prop({ required: true }) declare readonly camSettings: GuiWebcamStateWebcam
|
||||
@Prop({ default: null }) declare readonly printerUrl: string | null
|
||||
|
||||
@ -30,20 +39,27 @@ export default class Uv4lMjpeg extends Mixins(BaseMixin) {
|
||||
}
|
||||
|
||||
get webcamStyle() {
|
||||
const output = {
|
||||
transform: 'none',
|
||||
aspectRatio: 16 / 9,
|
||||
}
|
||||
|
||||
let transforms = ''
|
||||
if ('flipX' in this.camSettings && this.camSettings.flipX) transforms += ' scaleX(-1)'
|
||||
if ('flipX' in this.camSettings && this.camSettings.flipY) transforms += ' scaleY(-1)'
|
||||
if (transforms.trimLeft().length) return { transform: transforms.trimLeft() }
|
||||
if (transforms.trimStart().length) output.transform = transforms.trimStart()
|
||||
|
||||
return ''
|
||||
if (this.aspectRatio) output.aspectRatio = this.aspectRatio
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
mounted() {
|
||||
document.addEventListener('visibilitychange', this.visibilityChanged)
|
||||
document.addEventListener('visibilitychange', this.documentVisibilityChanged)
|
||||
}
|
||||
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('visibilitychange', this.visibilityChanged)
|
||||
document.removeEventListener('visibilitychange', this.documentVisibilityChanged)
|
||||
this.stopStream()
|
||||
}
|
||||
|
||||
@ -58,15 +74,25 @@ export default class Uv4lMjpeg extends Mixins(BaseMixin) {
|
||||
}
|
||||
}
|
||||
|
||||
visibilityChanged() {
|
||||
documentVisibilityChanged() {
|
||||
const visibility = document.visibilityState
|
||||
this.visibilityChanged(visibility === 'visible')
|
||||
}
|
||||
|
||||
if (visibility === 'visible') {
|
||||
visibilityChanged(newVal: boolean) {
|
||||
if (newVal) {
|
||||
this.startStream()
|
||||
return
|
||||
}
|
||||
|
||||
this.stopStream()
|
||||
}
|
||||
|
||||
onload() {
|
||||
if (this.aspectRatio === null && this.$refs.webcamUv4lMjpegImage) {
|
||||
this.aspectRatio =
|
||||
this.$refs.webcamUv4lMjpegImage.naturalWidth / this.$refs.webcamUv4lMjpegImage.naturalHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -19,8 +19,8 @@ const updateSW = registerSW({
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// vue-observe-visibility
|
||||
import VueObserveVisibility from 'vue-observe-visibility'
|
||||
Vue.use(VueObserveVisibility)
|
||||
import { ObserveVisibility } from 'vue-observe-visibility'
|
||||
Vue.directive('observe-visibility', ObserveVisibility)
|
||||
|
||||
//vue-meta
|
||||
import VueMeta from 'vue-meta'
|
||||
|
Loading…
x
Reference in New Issue
Block a user