From 864a1d2a22580a33b5e928734fd256c2133fb672 Mon Sep 17 00:00:00 2001
From: Paul Oliver <contact@pauloliver.dev>
Date: Sat, 3 Jan 2026 00:26:51 +0000
Subject: Adds camera streaming to frontend

---
 hsm-web/Client/src/CameraStream.vue | 103 ++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)
 create mode 100644 hsm-web/Client/src/CameraStream.vue

(limited to 'hsm-web/Client/src/CameraStream.vue')

diff --git a/hsm-web/Client/src/CameraStream.vue b/hsm-web/Client/src/CameraStream.vue
new file mode 100644
index 0000000..1defed2
--- /dev/null
+++ b/hsm-web/Client/src/CameraStream.vue
@@ -0,0 +1,103 @@
+<template>
+  <h2>Camera Stream</h2>
+  <button @click='toggleStream()' :disabled='disabled'>{{ command }}</button>
+  <video ref='player' muted></video>
+</template>
+
+<script>
+import axios              from 'axios'
+import config             from './config'
+import GstWebRTCAPI       from '@tomoxv/gstwebrtc-api/src/gstwebrtc-api.js'
+import { useTemplateRef } from 'vue'
+
+export default {
+  data() {
+    return {
+      api:       null,
+      listener:  null,
+      player:    null,
+      session:   null,
+
+      command:   'Loading',
+      disabled:  true,
+      streaming: false
+    }
+  },
+  mounted() {
+    this.player = useTemplateRef('player')
+    this.monitor()
+    this.bindStream()
+  },
+  methods: {
+    // Continuously checks if WebRTC stream is running on server
+    async monitor() {
+      const res = await axios.get(config.api + '/isStreaming')
+  
+      switch (res.status) {
+      case 200:
+        this.command   = res.data ? 'Stop' : 'Play'
+        this.disabled  = false
+        this.streaming = res.data
+        break
+      default:
+        this.command   = 'Error'
+        this.disabled  = true
+        this.streaming = false
+      }
+  
+      setTimeout(this.monitor, 1000)
+    },
+
+    // Toggles WebRTC stream
+    async toggleStream() {
+      const ep  = this.streaming ? '/stopStream' : '/startStream'
+      const res = await axios.get(config.api + ep)
+
+      if (res.status != 200) {
+        console.error(res)
+      }
+    },
+
+    // Binds WebRTC stream to video element
+    bindStream() {
+      this.api = new GstWebRTCAPI({
+        meta:               { name: 'WebClient-' + Date.now() },
+        signalingServerUrl: 'ws://' + window.location.hostname + ':8443'
+      })
+
+      this.listener = {
+        producerAdded: (producer) => {
+          console.log("Producer added", producer)
+  
+          this.session = this.api.createConsumerSession(producer.id)
+          this.session.addEventListener('streamsChanged', () => {
+            if (this.session.streams.length > 0) {
+              this.player.srcObject = this.session.streams[0]
+              this.player.play()
+            }
+          })
+  
+          this.session.connect()
+        },
+  
+        producerRemoved: (producer) => {
+          console.log("Producer removed", producer)
+  
+          this.player.pause()
+          this.player.srcObject = null
+          this.session          = null
+        }
+      }
+  
+      this.api.registerProducersListener(this.listener)
+    }
+  }
+}
+</script>
+
+<style>
+video {
+  height: 360px;
+  width:  480px;
+}
+</style>
-- 
cgit v1.2.3-70-g09d2

