Initial web ui and control for robot

This commit is contained in:
2026-04-30 21:35:18 +00:00
parent d64e1c24c8
commit 91f5f4d3ab
23 changed files with 2688 additions and 4 deletions
@@ -0,0 +1,78 @@
import { useEffect, useRef } from 'react'
// Signaling host for the GStreamer WebRTC server (hostname only, e.g. rapsbot-v2.local).
// Set VITE_WEBRTC_HOST in .env.local or as an env var; defaults to the current page host.
const signalingHost = import.meta.env.VITE_WEBRTC_HOST || window.location.hostname
const SIGNALING_WS_URL = `ws://${signalingHost}:8443`
export function VideoStream() {
const videoRef = useRef(null)
const pcRef = useRef(null)
const wsRef = useRef(null)
useEffect(() => {
console.log('[WebRTC] Connecting to', SIGNALING_WS_URL)
const pc = new RTCPeerConnection({})
const ws = new WebSocket(SIGNALING_WS_URL)
pcRef.current = pc
wsRef.current = ws
pc.onconnectionstatechange = () => console.log('[WebRTC] PC state:', pc.connectionState)
pc.oniceconnectionstatechange = () => console.log('[WebRTC] ICE state:', pc.iceConnectionState)
pc.ontrack = ({ streams }) => {
console.log('[WebRTC] Track received, streams:', streams.length)
if (videoRef.current) videoRef.current.srcObject = streams[0]
}
pc.onicecandidate = ({ candidate }) => {
if (candidate && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
type: 'ice',
mlineindex: candidate.sdpMLineIndex,
candidate: candidate.candidate,
}))
}
}
ws.onopen = () => console.log('[WebRTC] Signaling connected')
ws.onclose = () => console.log('[WebRTC] Signaling closed')
ws.onmessage = async (event) => {
const msg = JSON.parse(event.data)
console.log('[WebRTC] Signal:', msg.type, msg)
if (msg.type === 'offer') {
await pc.setRemoteDescription({ type: 'offer', sdp: msg.sdp })
const answer = await pc.createAnswer()
await pc.setLocalDescription(answer)
ws.send(JSON.stringify({ type: 'answer', sdp: answer.sdp }))
console.log('[WebRTC] Answer sent')
} else if (msg.type === 'ice') {
await pc.addIceCandidate({ sdpMLineIndex: msg.mlineindex, candidate: msg.candidate })
}
}
let cleanedUp = false
ws.onerror = (e) => {
if (!cleanedUp) console.error('[WebRTC] Signaling error', e)
}
return () => {
cleanedUp = true
pc.close()
ws.close()
}
}, [])
return (
<video
ref={videoRef}
autoPlay
playsInline
muted
style={{ width: '100%', maxHeight: '60vh', background: '#000', display: 'block' }}
/>
)
}