From 423a9093ed3ee8cd08a9caa793a060403193db33 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sun, 12 Nov 2023 21:06:36 +0100 Subject: [PATCH 01/10] feat(webcam): add support for go2rtc webrtc Signed-off-by: Stefan Dej --- .../settings/Webcams/WebcamForm.vue | 1 + src/components/webcams/WebcamWrapperItem.vue | 4 + .../webcams/streamers/DynamicCamLoader.ts | 3 + .../webcams/streamers/WebrtcGo2rtc.vue | 205 ++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 src/components/webcams/streamers/WebrtcGo2rtc.vue diff --git a/src/components/settings/Webcams/WebcamForm.vue b/src/components/settings/Webcams/WebcamForm.vue index 167dc1f85..57fcd3a06 100644 --- a/src/components/settings/Webcams/WebcamForm.vue +++ b/src/components/settings/Webcams/WebcamForm.vue @@ -231,6 +231,7 @@ export default class WebcamForm extends Mixins(BaseMixin, WebcamMixin) { { 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: 'webrtc-go2rtc', text: this.$t('Settings.WebcamsTab.WebrtcGo2rtc') }, { value: 'webrtc-mediamtx', text: this.$t('Settings.WebcamsTab.WebrtcMediaMTX') }, { value: 'hlsstream', text: this.$t('Settings.WebcamsTab.Hlsstream') }, { value: 'jmuxer-stream', text: this.$t('Settings.WebcamsTab.JMuxerStream') }, diff --git a/src/components/webcams/WebcamWrapperItem.vue b/src/components/webcams/WebcamWrapperItem.vue index 989a88e07..4e7217ef1 100644 --- a/src/components/webcams/WebcamWrapperItem.vue +++ b/src/components/webcams/WebcamWrapperItem.vue @@ -27,6 +27,9 @@ + @@ -51,6 +54,7 @@ import { DynamicCamLoader } from '@/components/webcams/streamers/DynamicCamLoade Uv4lMjpegAsync: DynamicCamLoader('Uv4lMjpeg'), WebrtcCameraStreamerAsync: DynamicCamLoader('WebrtcCameraStreamer'), WebrtcMediaMTXAsync: DynamicCamLoader('WebrtcMediaMTX'), + WebrtcGo2rtcAsync: DynamicCamLoader('WebrtcGo2rtc'), }, }) export default class WebcamWrapperItem extends Mixins(BaseMixin) { diff --git a/src/components/webcams/streamers/DynamicCamLoader.ts b/src/components/webcams/streamers/DynamicCamLoader.ts index 663206e51..a41182f37 100644 --- a/src/components/webcams/streamers/DynamicCamLoader.ts +++ b/src/components/webcams/streamers/DynamicCamLoader.ts @@ -10,6 +10,7 @@ type StreamerTypes = | 'Uv4lMjpeg' | 'WebrtcCameraStreamer' | 'WebrtcMediaMTX' + | 'WebrtcGo2rtc' function getDynamicCamImport(componentName: StreamerTypes) { // split each webcam streamer into its own chunk @@ -32,6 +33,8 @@ function getDynamicCamImport(componentName: StreamerTypes) { return () => import('@/components/webcams/streamers/WebrtcCameraStreamer.vue') case 'WebrtcMediaMTX': return () => import('@/components/webcams/streamers/WebrtcMediaMTX.vue') + case 'WebrtcGo2rtc': + return () => import('@/components/webcams/streamers/WebrtcGo2rtc.vue') } } diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue new file mode 100644 index 000000000..b780e4311 --- /dev/null +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -0,0 +1,205 @@ + + + + + From 1534f23e786d879d6ca6986091e76a8ec594f59d Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sun, 12 Nov 2023 21:07:47 +0100 Subject: [PATCH 02/10] locale(en): add english locale Signed-off-by: Stefan Dej --- src/locales/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/locales/en.json b/src/locales/en.json index d37a31640..6fd2e5568 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1170,6 +1170,7 @@ "Vertically": "vertically", "Webcams": "Webcams", "WebrtcCameraStreamer": "WebRTC (camera-streamer)", + "WebrtcGo2rtc": "WebRTC (go2rtc)", "WebrtcJanus": "WebRTC (janus-gateway)", "WebrtcMediaMTX": "WebRTC (MediaMTX)" } From a251760ba05fe204531e019eef652841a1dde0da Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Wed, 15 Nov 2023 00:35:28 +0100 Subject: [PATCH 03/10] fix: use ssl websocket connection when the interface is secure Signed-off-by: Stefan Dej --- src/components/webcams/streamers/WebrtcGo2rtc.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index b780e4311..29de86c81 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -62,7 +62,7 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { const url = new URL('api/ws' + urlSearch, this.camSettings.stream_url) url.searchParams.set('media', 'video+audio') // change protocol to ws - url.protocol = 'ws:' + url.protocol = this.$store.state.socket.protocol + ':' // output a warning, if no src is set in the url if (!url.searchParams.has('src')) { From 2adbfc7be19cbc06cb52f713a06eacb3ecfdff64 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Wed, 15 Nov 2023 20:09:32 +0100 Subject: [PATCH 04/10] fix: stop scheduledRestart after opening the websocket Signed-off-by: Stefan Dej --- src/components/webcams/streamers/WebrtcGo2rtc.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index 29de86c81..c932a6529 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -132,6 +132,11 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { this.log('open') this.status = 'connected' + if (this.restartTimeout !== null) { + clearTimeout(this.restartTimeout) + this.restartTimeout = null + } + this.pc?.addEventListener('icecandidate', (ev) => { if (!ev.candidate) return const msg = { type: 'webrtc/candidate', value: ev.candidate.candidate } From a828a1dd4d11631aca04490f15abf866d1f0fb86 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Wed, 15 Nov 2023 22:34:31 +0100 Subject: [PATCH 05/10] fix: fix issue when video tag not exists Signed-off-by: Stefan Dej --- src/components/webcams/streamers/WebrtcGo2rtc.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index c932a6529..c7a8321c0 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -31,7 +31,7 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { pc: RTCPeerConnection | null = null ws: WebSocket | null = null - restartPause = 5000 + restartPause = 2000 restartTimeout: any = null status: string = 'connecting' @@ -107,6 +107,11 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { // adapted from https://github.com/AlexxIT/go2rtc/blob/master/www/webrtc.html start() { + if (!this.video) { + this.scheduleRestart() + return + } + this.log('connecting to ' + this.url) this.status = 'connecting' From d8241f5172fde068836e8fe88079070162427c74 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Fri, 17 Nov 2023 23:37:21 +0100 Subject: [PATCH 06/10] fix: fix issue with multiple open websockets Signed-off-by: Stefan Dej --- src/components/webcams/streamers/WebrtcGo2rtc.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index c7a8321c0..770b4e546 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -130,7 +130,7 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { this.ws = new WebSocket(this.url) this.ws.addEventListener('open', () => this.onWebSocketOpen()) this.ws.addEventListener('message', (ev) => this.onWebSocketMessage(ev)) - this.ws.addEventListener('close', () => this.onWebSocketClose()) + this.ws.addEventListener('close', (ev) => this.onWebSocketClose(ev)) } onWebSocketOpen() { @@ -167,10 +167,11 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { } } - onWebSocketClose() { + onWebSocketClose(ev: CloseEvent) { this.log('close') this.status = 'disconnected' - this.scheduleRestart() + + if (!ev.wasClean) this.scheduleRestart() } terminate() { From 565fc72d5faf9238d253291713583bdfa4ce55dc Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 9 Dec 2023 11:46:03 +0100 Subject: [PATCH 07/10] feat: add option to enable/disable audio Signed-off-by: Stefan Dej --- .../settings/Webcams/WebcamForm.vue | 30 +++++++++++++++++++ .../webcams/streamers/WebrtcGo2rtc.vue | 7 ++++- src/locales/de.json | 1 + src/locales/en.json | 1 + src/store/gui/webcams/types.ts | 1 + 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/components/settings/Webcams/WebcamForm.vue b/src/components/settings/Webcams/WebcamForm.vue index 57fcd3a06..5a43c4fbc 100644 --- a/src/components/settings/Webcams/WebcamForm.vue +++ b/src/components/settings/Webcams/WebcamForm.vue @@ -111,6 +111,15 @@ :label="$t('Settings.WebcamsTab.HideFps')" /> + + + + +
@@ -264,6 +273,10 @@ export default class WebcamForm extends Mixins(BaseMixin, WebcamMixin) { return ['mjpegstreamer', 'mjpegstreamer-adaptive'].includes(this.webcam.service) } + get hasAudioOption() { + return ['webrtc-go2rtc'].includes(this.webcam.service) + } + get hideFps() { return this.webcam.extra_data?.hideFps ?? false } @@ -281,6 +294,23 @@ export default class WebcamForm extends Mixins(BaseMixin, WebcamMixin) { this.webcam.extra_data.hideFps = newVal } + get enableAudio() { + return this.webcam.extra_data?.enableAudio ?? false + } + + set enableAudio(newVal) { + if (!('extra_data' in this.webcam)) { + this.webcam.extra_data = { + enableAudio: newVal, + } + + return + } + + // @ts-ignore + this.webcam.extra_data.enableAudio = newVal + } + mounted() { this.oldWebcamName = this.webcam.name } diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index 770b4e546..41f883719 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -60,7 +60,12 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { get url() { const urlSearch = new URL(this.camSettings.stream_url).search.toString() const url = new URL('api/ws' + urlSearch, this.camSettings.stream_url) - url.searchParams.set('media', 'video+audio') + + // create media types array + const media = ['video'] + if (this.camSettings.extra_data?.enableAudio ?? false) media.push('audio') + + url.searchParams.set('media', media.join('+')) // change protocol to ws url.protocol = this.$store.state.socket.protocol + ':' diff --git a/src/locales/de.json b/src/locales/de.json index 44326dbd9..e8b227864 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1146,6 +1146,7 @@ "CreateWebcam": "Erstelle Webcam", "EditCrowsnestConf": "crowsnest.conf bearbeiten", "EditWebcam": "Webcam bearbeiten", + "EnableAudio": "Ton einschalten", "FlipWebcam": "Webcam-Bild spiegeln:", "HideFps": "FPS-Anzeige verstecken", "Hlsstream": "HLS-Stream", diff --git a/src/locales/en.json b/src/locales/en.json index 6601f644c..c0f99508f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1164,6 +1164,7 @@ "CreateWebcam": "Create Webcam", "EditCrowsnestConf": "Edit crowsnest.conf", "EditWebcam": "Edit Webcam", + "EnableAudio": "Enable audio", "FlipWebcam": "Flip webcam image:", "HideFps": "Hide FPS counter", "Hlsstream": "HLS Stream", diff --git a/src/store/gui/webcams/types.ts b/src/store/gui/webcams/types.ts index 50f03153b..a5566b62c 100644 --- a/src/store/gui/webcams/types.ts +++ b/src/store/gui/webcams/types.ts @@ -26,6 +26,7 @@ export interface GuiWebcamStateWebcam { rotation: number aspect_ratio?: string extra_data?: { + enableAudio?: boolean hideFps?: boolean } source?: 'config' | 'database' From 6f3466c57f0c99d98166dfeb10254f72a466452f Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 9 Dec 2023 13:28:56 +0100 Subject: [PATCH 08/10] fix: check wrong url before converting it Signed-off-by: Stefan Dej --- src/components/webcams/streamers/WebrtcGo2rtc.vue | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index 41f883719..d24986938 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -58,8 +58,15 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { } get url() { - const urlSearch = new URL(this.camSettings.stream_url).search.toString() - const url = new URL('api/ws' + urlSearch, this.camSettings.stream_url) + let urlSearch = '' + let url = new URL(location.href) + + try { + urlSearch = new URL(this.camSettings.stream_url).search.toString() + url = new URL('api/ws' + urlSearch, this.camSettings.stream_url) + } catch (e) { + this.log('invalid url', this.camSettings.stream_url) + } // create media types array const media = ['video'] From 701fe6d45999880c0072807e474cc03b1c734513 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 9 Dec 2023 13:59:27 +0100 Subject: [PATCH 09/10] feat: add support for connectionstatechange to restart failed/disconnected streams Signed-off-by: Stefan Dej --- src/components/webcams/streamers/WebrtcGo2rtc.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index d24986938..d23b50a65 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -147,7 +147,6 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { onWebSocketOpen() { this.log('open') - this.status = 'connected' if (this.restartTimeout !== null) { clearTimeout(this.restartTimeout) @@ -160,6 +159,15 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { this.ws?.send(JSON.stringify(msg)) }) + this.pc?.addEventListener('connectionstatechange', () => { + this.status = (this.pc?.connectionState ?? '').toString() + this.log('connection state changed', this.status) + + if (['failed', 'disconnected'].includes(this.status)) { + this.scheduleRestart() + } + }) + this.pc ?.createOffer() .then((offer) => this.pc?.setLocalDescription(offer)) From 2b7bbc2a7bfd3ce8e34dbb00980a760745c3c27b Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 9 Dec 2023 14:01:16 +0100 Subject: [PATCH 10/10] feat: restart stream if enableAudio changes Signed-off-by: Stefan Dej --- src/components/webcams/streamers/WebrtcGo2rtc.vue | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/webcams/streamers/WebrtcGo2rtc.vue b/src/components/webcams/streamers/WebrtcGo2rtc.vue index d23b50a65..f3bb47926 100644 --- a/src/components/webcams/streamers/WebrtcGo2rtc.vue +++ b/src/components/webcams/streamers/WebrtcGo2rtc.vue @@ -70,7 +70,7 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { // create media types array const media = ['video'] - if (this.camSettings.extra_data?.enableAudio ?? false) media.push('audio') + if (this.enableAudio) media.push('audio') url.searchParams.set('media', media.join('+')) // change protocol to ws @@ -84,6 +84,10 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { return this.convertUrl(url.toString(), this.printerUrl) } + get enableAudio() { + return this.camSettings.extra_data?.enableAudio ?? false + } + // stop and restart the video if the url changes @Watch('url') changedUrl() { @@ -91,6 +95,13 @@ export default class WebrtcGo2rtc extends Mixins(BaseMixin, WebcamMixin) { this.start() } + // stop and restart the video if enableAudio changes + @Watch('enableAudio') + changedEnableAudio() { + this.terminate() + this.start() + } + get expanded(): boolean { return this.$store.getters['gui/getPanelExpand']('webcam-panel', this.viewport) ?? false }