Initial web ui and control for robot
This commit is contained in:
@@ -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' }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user