Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add WebRTC (camera streamer) support #1275

Merged
merged 70 commits into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
194485b
feat: add WebRTC in webcam settings option
meteyou Oct 10, 2022
088fa21
refactor: remove unused getter
meteyou Oct 10, 2022
a68dde1
feat: WIP
meteyou Oct 10, 2022
99ca183
feat: WIP
meteyou Oct 10, 2022
10c557a
feat: WIP
meteyou Oct 10, 2022
31db926
feat: WIP
meteyou Oct 10, 2022
d218820
refactor: cleanup code in webRTC component
meteyou Oct 11, 2022
4946510
feat: add flip H/V to webrtc
meteyou Oct 12, 2022
335ef50
Merge branch 'develop' into feat/add-camera-streamer-webrtc
meteyou Oct 20, 2022
a0a40e5
feat: add WebRTC to webcam grid
meteyou Oct 23, 2022
83325e7
feat: add WebRTC in webcam settings option
meteyou Oct 10, 2022
9d87065
refactor: remove unused getter
meteyou Oct 10, 2022
a164a95
feat: WIP
meteyou Oct 10, 2022
0437af3
feat: WIP
meteyou Oct 10, 2022
233119f
feat: WIP
meteyou Oct 10, 2022
5434d22
feat: WIP
meteyou Oct 10, 2022
8287c41
refactor: cleanup code in webRTC component
meteyou Oct 11, 2022
8fc1e0c
feat: add flip H/V to webrtc
meteyou Oct 12, 2022
f001d1c
feat: add WebRTC to webcam grid
meteyou Oct 23, 2022
ae8b187
refactor: add debug output
meteyou Nov 1, 2022
63f2b68
feat: auto restart stream
meteyou Nov 13, 2022
bfd0a2c
feat: restart stream when url changed
meteyou Nov 14, 2022
bbd3234
feat: WIP
meteyou Oct 10, 2022
c116298
feat: WIP
meteyou Oct 10, 2022
0a9d162
feat: WIP
meteyou Oct 10, 2022
a22f9d0
feat: WIP
meteyou Oct 10, 2022
788ce94
refactor: cleanup code in webRTC component
meteyou Oct 11, 2022
c217fe7
feat: add flip H/V to webrtc
meteyou Oct 12, 2022
e1e1236
refactor: update to develop
meteyou Nov 20, 2022
30652e2
Merge remote-tracking branch 'origin/feat/add-camera-streamer-webrtc'…
meteyou Nov 20, 2022
72350c6
chore(changelog): update changelog
meteyou Dec 10, 2022
ad21435
chore(changelog): update changelog
meteyou Dec 10, 2022
303be73
chore(changelog): update changelog
meteyou Dec 10, 2022
59ea6df
chore: update cliff settings for testing
meteyou Dec 10, 2022
9c23446
docs(changelog): update changelog
meteyou Dec 10, 2022
357d4e1
chore: update cliff settings for testing
meteyou Dec 10, 2022
cca8cf1
feat: add WebRTC in webcam settings option
meteyou Oct 10, 2022
a9036e5
refactor: remove unused getter
meteyou Oct 10, 2022
32ff2c1
feat: WIP
meteyou Oct 10, 2022
a0ad671
feat: WIP
meteyou Oct 10, 2022
7ec923a
feat: WIP
meteyou Oct 10, 2022
0f0ca00
feat: WIP
meteyou Oct 10, 2022
66e3f21
refactor: cleanup code in webRTC component
meteyou Oct 11, 2022
99d6230
feat: add flip H/V to webrtc
meteyou Oct 12, 2022
0463dba
feat: add WebRTC to webcam grid
meteyou Oct 23, 2022
160778d
refactor: add debug output
meteyou Nov 1, 2022
754a009
feat: auto restart stream
meteyou Nov 13, 2022
54b41a7
feat: restart stream when url changed
meteyou Nov 14, 2022
7904932
feat: WIP
meteyou Oct 10, 2022
ab8f1c2
feat: WIP
meteyou Oct 10, 2022
15cbea0
feat: WIP
meteyou Oct 10, 2022
abe41a2
feat: WIP
meteyou Oct 10, 2022
1ae12f1
refactor: cleanup code in webRTC component
meteyou Oct 11, 2022
ed58dfc
feat: add flip H/V to webrtc
meteyou Oct 12, 2022
fc76ff3
refactor: update to develop
meteyou Nov 20, 2022
cb15110
feat: WIP
meteyou Oct 10, 2022
622a1b3
feat: WIP
meteyou Oct 10, 2022
1c96269
feat: WIP
meteyou Oct 10, 2022
9f0fbd8
refactor: cleanup code in webRTC component
meteyou Oct 11, 2022
6821c43
feat: add flip H/V to webrtc
meteyou Oct 12, 2022
70cbfeb
fix: merging issues
meteyou Dec 10, 2022
d2c3b21
Merge remote-tracking branch 'origin/feat/add-camera-streamer-webrtc'…
meteyou Dec 10, 2022
73961fd
fix: merging issues
meteyou Dec 11, 2022
20b61a7
Merge branch 'develop' into feat/add-camera-streamer-webrtc
meteyou Jan 2, 2023
45eb760
Merge branch 'develop' into feat/add-camera-streamer-webrtc
meteyou Feb 18, 2023
5cc787b
refactor: rename webrtc to webrtc-camerastreamer
meteyou Feb 18, 2023
9c6f6b4
refactor: simplify template of WebrtcCameraStreamer.vue
meteyou Feb 18, 2023
c46ffe3
Merge branch 'develop' into feat/add-camera-streamer-webrtc
meteyou Feb 21, 2023
5b32a0b
fix: fix for firefox
meteyou Mar 4, 2023
0e8bfe9
fix: fix eventListener connectionstatechange
meteyou Mar 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions src/components/panels/WebcamPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,25 @@
<v-row>
<v-col class="pb-0" style="position: relative">
<template v-if="currentCam.service === 'grid'">
<webcam-grid :webcams="webcams"></webcam-grid>
<webcam-grid :webcams="webcams" />
</template>
<template v-else-if="currentCam.service === 'mjpegstreamer'">
<webcam-mjpegstreamer :cam-settings="currentCam"></webcam-mjpegstreamer>
<webcam-mjpegstreamer :cam-settings="currentCam" />
</template>
<template v-else-if="currentCam.service === 'mjpegstreamer-adaptive'">
<webcam-mjpegstreamer-adaptive :cam-settings="currentCam"></webcam-mjpegstreamer-adaptive>
<webcam-mjpegstreamer-adaptive :cam-settings="currentCam" />
</template>
<template v-else-if="currentCam.service === 'uv4l-mjpeg'">
<webcam-uv4l-mjpeg :cam-settings="currentCam"></webcam-uv4l-mjpeg>
<webcam-uv4l-mjpeg :cam-settings="currentCam" />
</template>
<template v-else-if="currentCam.service === 'ipstream'">
<webcam-ipstreamer :cam-settings="currentCam"></webcam-ipstreamer>
<webcam-ipstreamer :cam-settings="currentCam" />
</template>
<template v-else-if="currentCam.service === 'hlsstream'">
<webcam-hlsstreamer :cam-settings="currentCam"></webcam-hlsstreamer>
<webcam-hlsstreamer :cam-settings="currentCam" />
</template>
<template v-else-if="currentCam.service === 'webrtc-camerastreamer'">
<webcam-webrtc-camerastreamer :cam-settings="currentCam" />
</template>
<template v-else>
<p class="text-center py-3 font-italic">{{ $t('Panels.WebcamPanel.UnknownWebcamService') }}</p>
Expand All @@ -75,9 +78,10 @@
<script lang="ts">
import Mjpegstreamer from '@/components/webcams/Mjpegstreamer.vue'
import MjpegstreamerAdaptive from '@/components/webcams/MjpegstreamerAdaptive.vue'
import Ipstreamer from '@/components/webcams/Ipstreamer.vue'
import Hlsstreamer from '@/components/webcams/Hlsstreamer.vue'
import Ipstreamer from '@/components/webcams/Ipstreamer.vue'
import Uv4lMjpeg from '@/components/webcams/Uv4lMjpeg.vue'
import WebrtcCameraStreamer from '@/components/webcams/WebrtcCameraStreamer.vue'
import WebcamGrid from '@/components/webcams/WebcamGrid.vue'
import Component from 'vue-class-component'
import { Mixins, Prop } from 'vue-property-decorator'
Expand All @@ -95,6 +99,7 @@ import WebcamMixin from '@/components/mixins/webcam'
'webcam-ipstreamer': Ipstreamer,
'webcam-hlsstreamer': Hlsstreamer,
'webcam-uv4l-mjpeg': Uv4lMjpeg,
'webcam-webrtc-camerastreamer': WebrtcCameraStreamer,
'webcam-grid': WebcamGrid,
},
})
Expand Down
27 changes: 11 additions & 16 deletions src/components/settings/SettingsWebcamsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,22 @@
</v-col>
<v-col class="col-12 col-sm-6 text-center" align-self="center">
<template v-if="form.service === 'mjpegstreamer'">
<webcam-mjpegstreamer :cam-settings="form"></webcam-mjpegstreamer>
<webcam-mjpegstreamer :cam-settings="form" />
</template>
<template v-else-if="form.service === 'mjpegstreamer-adaptive'">
<webcam-mjpegstreamer-adaptive :cam-settings="form"></webcam-mjpegstreamer-adaptive>
<webcam-mjpegstreamer-adaptive :cam-settings="form" />
</template>
<template v-else-if="form.service === 'uv4l-mjpeg'">
<webcam-uv4l-mjpeg :cam-settings="form"></webcam-uv4l-mjpeg>
<webcam-uv4l-mjpeg :cam-settings="form" />
</template>
<template v-else-if="form.service === 'ipstream'">
<webcam-ipstreamer :cam-settings="form"></webcam-ipstreamer>
<webcam-ipstreamer :cam-settings="form" />
</template>
<template v-else-if="form.service === 'hlsstream'">
<webcam-hlsstreamer :cam-settings="form"></webcam-hlsstreamer>
<webcam-hlsstreamer :cam-settings="form" />
</template>
<template v-else-if="form.service === 'webrtc-camerastreamer'">
<webcam-webrtc-camerastreamer :cam-settings="form" />
</template>
<template v-else>
<p class="text-center py-3 font-italic">
Expand Down Expand Up @@ -223,6 +226,7 @@ import { GuiWebcamStateWebcam } from '@/store/gui/webcams/types'
import Mjpegstreamer from '@/components/webcams/Mjpegstreamer.vue'
import MjpegstreamerAdaptive from '@/components/webcams/MjpegstreamerAdaptive.vue'
import Uv4lMjpeg from '@/components/webcams/Uv4lMjpeg.vue'
import WebrtcCameraStreamer from '@/components/webcams/WebrtcCameraStreamer.vue'
import Ipstreamer from '@/components/webcams/Ipstreamer.vue'
import { mdiMenuDown, mdiDelete, mdiPencil, mdiWebcam } from '@mdi/js'
import WebcamMixin from '@/components/mixins/webcam'
Expand Down Expand Up @@ -251,6 +255,7 @@ interface webcamForm {
'webcam-mjpegstreamer-adaptive': MjpegstreamerAdaptive,
'webcam-uv4l-mjpeg': Uv4lMjpeg,
'webcam-ipstreamer': Ipstreamer,
'webcam-webrtc-camerastreamer': WebrtcCameraStreamer,
'webcam-hlsstreamer': Hlsstreamer,
},
})
Expand Down Expand Up @@ -315,21 +320,11 @@ export default class SettingsWebcamsTab extends Mixins(BaseMixin, WebcamMixin) {
{ value: 'mjpegstreamer-adaptive', text: this.$t('Settings.WebcamsTab.MjpegstreamerAdaptive') },
{ value: 'uv4l-mjpeg', text: this.$t('Settings.WebcamsTab.Uv4lMjpeg') },
{ value: 'ipstream', text: this.$t('Settings.WebcamsTab.Ipstream') },
{ value: 'webrtc-camerastreamer', text: this.$t('Settings.WebcamsTab.WebrtcCameraStreamer') },
{ value: 'hlsstream', text: this.$t('Settings.WebcamsTab.Hlsstream') },
]
}

get webcamStyle() {
let transforms = ''
if (this.form.flipX) transforms += ' scaleX(-1)'
if (this.form.flipY) transforms += ' scaleY(-1)'
if (transforms.trimLeft().length) {
return { transform: transforms.trimLeft() }
}

return ''
}

get configfiles() {
return this.$store.getters['files/getDirectory']('config')?.childrens ?? []
}
Expand Down
15 changes: 10 additions & 5 deletions src/components/webcams/WebcamGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@
<v-row dense>
<v-col v-for="webcam in webcams" :key="webcam.id" cols="6">
<template v-if="webcam.service === 'mjpegstreamer'">
<webcam-mjpegstreamer :cam-settings="webcam"></webcam-mjpegstreamer>
<webcam-mjpegstreamer :cam-settings="webcam" />
</template>
<template v-else-if="webcam.service === 'mjpegstreamer-adaptive'">
<webcam-mjpegstreamer-adaptive :cam-settings="webcam"></webcam-mjpegstreamer-adaptive>
<webcam-mjpegstreamer-adaptive :cam-settings="webcam" />
</template>
<template v-else-if="webcam.service === 'uv4l-mjpeg'">
<webcam-uv4l-mjpeg :cam-settings="webcam"></webcam-uv4l-mjpeg>
<webcam-uv4l-mjpeg :cam-settings="webcam" />
</template>
<template v-else-if="webcam.service === 'ipstream'">
<webcam-ipstreamer :cam-settings="webcam"></webcam-ipstreamer>
<webcam-ipstreamer :cam-settings="webcam" />
</template>
<template v-else-if="webcam.service === 'hlsstream'">
<webcam-hlsstreamer :cam-settings="webcam"></webcam-hlsstreamer>
<webcam-hlsstreamer :cam-settings="webcam" />
</template>
<template v-else-if="webcam.service === 'webrtc-camerastreamer'">
<webcam-webrtc-camerastreamer :cam-settings="webcam" />
</template>
<template v-else>
<p class="text-center py-3 font-italic">{{ $t('Panels.WebcamPanel.UnknownWebcamService') }}</p>
Expand All @@ -35,6 +38,7 @@ import MjpegstreamerAdaptive from '@/components/webcams/MjpegstreamerAdaptive.vu
import Uv4lMjpeg from '@/components/webcams/Uv4lMjpeg.vue'
import Ipstreamer from '@/components/webcams/Ipstreamer.vue'
import Hlsstreamer from '@/components/webcams/Hlsstreamer.vue'
import WebrtcCameraStreamer from '@/components/webcams/WebrtcCameraStreamer.vue'
import { GuiWebcamStateWebcam } from '@/store/gui/webcams/types'

@Component({
Expand All @@ -44,6 +48,7 @@ import { GuiWebcamStateWebcam } from '@/store/gui/webcams/types'
'webcam-uv4l-mjpeg': Uv4lMjpeg,
'webcam-ipstreamer': Ipstreamer,
'webcam-hlsstreamer': Hlsstreamer,
'webcam-webrtc-camerastreamer': WebrtcCameraStreamer,
},
})
export default class WebcamGrid extends Mixins(BaseMixin) {
Expand Down
187 changes: 187 additions & 0 deletions src/components/webcams/WebrtcCameraStreamer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
<template>
<div>
<video
v-show="status === 'connected'"
ref="stream"
class="webcamStream"
:style="webcamStyle"
autoplay
muted
playsinline />
<v-row v-if="status !== 'connected'">
<v-col class="_webcam_webrtc_output text-center d-flex flex-column justify-center align-center">
<v-progress-circular v-if="status === 'connecting'" indeterminate color="primary" class="mb-3" />
<span class="mt-3">{{ status }}</span>
</v-col>
</v-row>
</div>
</template>

<script lang="ts">
import { Component, Mixins, Prop, Ref, Watch } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'

@Component
export default class WebrtcCameraStreamer extends Mixins(BaseMixin) {
private pc: RTCPeerConnection | null = null
private useStun = false
private remote_pc_id: string | null = null
private aspectRatio: null | number = null
private status: string = 'connecting'

@Prop({ required: true })
camSettings: any

@Prop({ default: null }) declare readonly printerUrl: string | null

@Ref() declare stream: HTMLVideoElement

get url() {
const baseUrl = this.camSettings.urlStream
let url = new URL(baseUrl, this.printerUrl === null ? this.hostUrl.toString() : this.printerUrl)
url.port = this.hostPort.toString()

if (baseUrl.startsWith('http') || baseUrl.startsWith('://')) url = new URL(baseUrl)

return decodeURIComponent(url.toString())
}

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.trimStart().length) output.transform = transforms.trimStart()

if (this.aspectRatio) output.aspectRatio = this.aspectRatio

return output
}

get streamConfig() {
let config: any = {
sdpSemantics: 'unified-plan',
}

if (this.useStun) {
config.iceServers = [{ urls: ['stun:stun.l.google.com:19302'] }]
}

return config
}

startStream() {
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1

this.pc = new RTCPeerConnection(this.streamConfig)
this.pc.addTransceiver('video', { direction: 'recvonly' })

this.pc.addEventListener(
'track',
(evt) => {
if (evt.track.kind == 'video' && this.$refs.stream) {
// @ts-ignore
this.$refs.stream.srcObject = evt.streams[0]
}
},
false
)

this.pc.addEventListener('connectionstatechange', () => {
this.status = (this.pc?.connectionState ?? '').toString()

if (['failed', 'disconnected'].includes(this.status)) {
setTimeout(async () => {
await this.pc?.close()
this.startStream()
}, 500)
}
})

fetch(this.url, {
body: JSON.stringify({
type: 'request',
//res: params.res
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
})
.then((response) => response.json())
.then((answer) => {
this.remote_pc_id = answer.id
return this.pc?.setRemoteDescription(answer)
})
.then(() => this.pc?.createAnswer())
.then((answer) => this.pc?.setLocalDescription(answer))
.then(() => {
// wait for ICE gathering to complete
return new Promise((resolve) => {
const checkState = () => {
if (this.pc?.iceGatheringState === 'complete') {
this.pc?.removeEventListener('icegatheringstatechange', checkState)
resolve(true)
}
}

if (this.pc?.iceGatheringState === 'complete') resolve(true)
else this.pc?.addEventListener('icegatheringstatechange', checkState)
})
})
.then(() => {
const offer = this.pc?.localDescription
return fetch(this.url, {
body: JSON.stringify({
type: offer?.type,
id: this.remote_pc_id,
sdp: offer?.sdp,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'POST',
})
})
.then((response: any) => {
if (isFirefox) this.status = 'connected'
return response.json()
})
.catch(function (e) {
window.console.error(e)
})
}

mounted() {
this.startStream()
}

beforeDestroy() {
this.pc?.close()
}

@Watch('url')
async changedUrl() {
await this.pc?.close()
this.startStream()
}
}
</script>

<style scoped>
.webcamStream {
width: 100%;
}

._webcam_webrtc_output {
aspect-ratio: calc(3 / 2);
}

video {
width: 100%;
}
</style>
3 changes: 2 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,8 @@
"UrlStream": "URL Stream",
"Uv4lMjpeg": "UV4L-MJPEG",
"Vertically": "vertically",
"Webcams": "Webcams"
"Webcams": "Webcams",
"WebrtcCameraStreamer": "WebRTC (camera-streamer)"
}
},
"ScrewsTiltAdjust": {
Expand Down