79 lines
2.4 KiB
React
79 lines
2.4 KiB
React
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' }}
|
|
/>
|
|
)
|
|
}
|