feat: rotate webcam in Mjpegstreamer-adaptive mode (#923)

This commit is contained in:
Stefan Dej 2022-08-01 21:29:10 +02:00 committed by GitHub
parent 5ce78fae4a
commit f27a7770a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 7 deletions

View File

@ -163,6 +163,17 @@
:label="$t('Settings.WebcamsTab.TargetFPS')"></v-text-field> :label="$t('Settings.WebcamsTab.TargetFPS')"></v-text-field>
</v-col> </v-col>
</v-row> </v-row>
<v-row v-if="form.service === 'mjpegstreamer-adaptive'">
<v-col class="py-2">
<v-select
v-model="form.rotate"
:items="rotateItems"
outlined
dense
hide-details
:label="$t('Settings.WebcamsTab.Rotate')"></v-select>
</v-col>
</v-row>
<v-row> <v-row>
<v-col class="py-1"> <v-col class="py-1">
<v-checkbox <v-checkbox
@ -242,6 +253,7 @@ interface webcamForm {
targetFps: number targetFps: number
urlStream: string urlStream: string
urlSnapshot: string urlSnapshot: string
rotate: number
flipX: boolean flipX: boolean
flipY: boolean flipY: boolean
} }
@ -272,6 +284,7 @@ export default class SettingsWebcamsTab extends Mixins(BaseMixin, WebcamMixin) {
targetFps: 15, targetFps: 15,
urlStream: '', urlStream: '',
urlSnapshot: '', urlSnapshot: '',
rotate: 0,
flipX: false, flipX: false,
flipY: false, flipY: false,
} }
@ -281,6 +294,13 @@ export default class SettingsWebcamsTab extends Mixins(BaseMixin, WebcamMixin) {
unique: (value: string) => !this.existsWebcamName(value) || this.$t('Settings.WebcamsTab.NameAlreadyExists'), unique: (value: string) => !this.existsWebcamName(value) || this.$t('Settings.WebcamsTab.NameAlreadyExists'),
} }
private rotateItems = [
{ value: 0, text: '0°' },
{ value: 90, text: '90°' },
{ value: 180, text: '180°' },
{ value: 270, text: '270°' },
]
declare $refs: { declare $refs: {
webcamForm: any webcamForm: any
} }
@ -348,6 +368,7 @@ export default class SettingsWebcamsTab extends Mixins(BaseMixin, WebcamMixin) {
this.form.targetFps = webcam.targetFps this.form.targetFps = webcam.targetFps
this.form.urlStream = webcam.urlStream this.form.urlStream = webcam.urlStream
this.form.urlSnapshot = webcam.urlSnapshot this.form.urlSnapshot = webcam.urlSnapshot
this.form.rotate = webcam.rotate ?? 0
this.form.flipX = webcam.flipX this.form.flipX = webcam.flipX
this.form.flipY = webcam.flipY this.form.flipY = webcam.flipY
@ -368,6 +389,7 @@ export default class SettingsWebcamsTab extends Mixins(BaseMixin, WebcamMixin) {
targetFps: this.form.targetFps, targetFps: this.form.targetFps,
urlStream: this.form.urlStream, urlStream: this.form.urlStream,
urlSnapshot: this.form.urlSnapshot, urlSnapshot: this.form.urlSnapshot,
rotate: this.form.rotate,
flipX: this.form.flipX, flipX: this.form.flipX,
flipY: this.form.flipY, flipY: this.form.flipY,
} }
@ -392,6 +414,7 @@ export default class SettingsWebcamsTab extends Mixins(BaseMixin, WebcamMixin) {
this.form.targetFps = 15 this.form.targetFps = 15
this.form.urlStream = '/webcam/?action=stream' this.form.urlStream = '/webcam/?action=stream'
this.form.urlSnapshot = '/webcam/?action=snapshot' this.form.urlSnapshot = '/webcam/?action=snapshot'
this.form.rotate = 0
this.form.flipX = false this.form.flipX = false
this.form.flipY = false this.form.flipY = false
} }

View File

@ -34,7 +34,7 @@
<script lang="ts"> <script lang="ts">
import Component from 'vue-class-component' import Component from 'vue-class-component'
import { Mixins, Prop } from 'vue-property-decorator' import { Mixins, Prop, Watch } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base' import BaseMixin from '@/components/mixins/base'
@Component @Component
@ -70,8 +70,9 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
} }
let transforms = '' let transforms = ''
if ('flipX' in this.camSettings && this.camSettings.flipX) transforms += ' scaleX(-1)' if (this.camSettings.flipX ?? false) transforms += ' scaleX(-1)'
if ('flipX' in this.camSettings && this.camSettings.flipY) transforms += ' scaleY(-1)' if (this.camSettings.flipY ?? false) transforms += ' scaleY(-1)'
if ((this.camSettings.rotate ?? 0) === 180) transforms += ' rotate(180deg)'
if (transforms.trimStart().length) output.transform = transforms.trimStart() if (transforms.trimStart().length) output.transform = transforms.trimStart()
if (this.aspectRatio) output.aspectRatio = this.aspectRatio if (this.aspectRatio) output.aspectRatio = this.aspectRatio
@ -83,6 +84,10 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
return this.currentFPS < 10 ? '0' + this.currentFPS.toString() : this.currentFPS return this.currentFPS < 10 ? '0' + this.currentFPS.toString() : this.currentFPS
} }
get rotate() {
return [90, 270].includes(this.camSettings.rotate ?? 0)
}
refreshFrame() { refreshFrame() {
if (this.isVisible) { if (this.isVisible) {
this.refresh = new Date().getTime() this.refresh = new Date().getTime()
@ -105,12 +110,31 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
const frame: any = await this.loadImage(url.toString()) const frame: any = await this.loadImage(url.toString())
canvas.width = canvas.clientWidth canvas.width = canvas.clientWidth
canvas.height = canvas.clientWidth * (frame.height / frame.width) if (this.rotate) {
if (this.aspectRatio === null) { if (this.aspectRatio === null) this.aspectRatio = frame.height / frame.width
this.aspectRatio = frame.width / frame.height canvas.height = canvas.clientWidth / (frame.height / frame.width)
} else {
if (this.aspectRatio === null) this.aspectRatio = frame.width / frame.height
canvas.height = canvas.clientWidth * (frame.width / frame.height)
} }
await ctx?.drawImage(frame, 0, 0, frame.width, frame.height, 0, 0, canvas.width, canvas.height) if (this.rotate) {
const scale = canvas.height / frame.width
const x = canvas.width / 2
const y = canvas.height / 2
ctx.translate(x, y)
ctx.rotate((this.camSettings.rotate * Math.PI) / 180)
await ctx?.drawImage(
frame,
(-frame.width / 2) * scale,
(-frame.height / 2) * scale,
frame.width * scale,
frame.height * scale
)
ctx.rotate(-((this.camSettings.rotate * Math.PI) / 180))
ctx.translate(-x, -y)
} else await ctx?.drawImage(frame, 0, 0, frame.width, frame.height, 0, 0, canvas.width, canvas.height)
this.isLoaded = true this.isLoaded = true
} }
@ -190,5 +214,10 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin) {
clearTimeout(this.timer) clearTimeout(this.timer)
this.timer = undefined this.timer = undefined
} }
@Watch('camSettings', { immediate: true, deep: true })
camSettingsChanged() {
this.aspectRatio = null
}
} }
</script> </script>

View File

@ -950,6 +950,7 @@
"Name": "Name", "Name": "Name",
"NameAlreadyExists": "Name already exists", "NameAlreadyExists": "Name already exists",
"Required": "required", "Required": "required",
"Rotate": "Rotate",
"SaveWebcam": "Save Webcam", "SaveWebcam": "Save Webcam",
"Service": "Service", "Service": "Service",
"TargetFPS": "Target FPS", "TargetFPS": "Target FPS",

View File

@ -12,6 +12,7 @@ export interface GuiWebcamStateWebcam {
targetFps: number targetFps: number
urlStream: string urlStream: string
urlSnapshot: string urlSnapshot: string
rotate?: number
flipX: boolean flipX: boolean
flipY: boolean flipY: boolean
} }