morris-telepresence / src /components /VideoFrame.tsx
GenAIJake's picture
Route Reachy signaling through same-origin native bridge
8e47e0e verified
Raw History Blame Contribute Delete
2.06 kB
import { useEffect, useState } from 'react';
interface VideoFrameProps {
videoRef: (element: HTMLVideoElement | null) => void;
micMuted: boolean;
audioMuted: boolean;
preview: boolean;
showOverlays?: boolean;
inactiveLabel?: string;
}
export function VideoFrame({
videoRef,
micMuted,
audioMuted,
preview,
showOverlays = true,
inactiveLabel,
}: VideoFrameProps) {
const [streaming, setStreaming] = useState(false);
useEffect(() => {
if (preview) setStreaming(false);
}, [preview]);
return (
<section className="camera" aria-label="Reachy camera">
<video
ref={videoRef}
className={streaming ? 'camera__video' : 'camera__video camera__video--hidden'}
autoPlay
playsInline
muted={audioMuted}
onPlaying={() => setStreaming(true)}
onEmptied={() => setStreaming(false)}
/>
{!streaming && (
<div className="camera__placeholder">
<span className="camera__placeholder-mark" aria-hidden="true">
<img src="/reachy-icon.svg" alt="" />
</span>
<span>
{preview
? 'Preview camera — no live video'
: inactiveLabel ?? 'Waiting for Reachy camera'}
</span>
</div>
)}
{showOverlays && (
<>
<div className="camera__badge camera__badge--left" aria-hidden="true">
<i className={streaming ? 'camera__dot camera__dot--live' : 'camera__dot'} />
{streaming ? 'Live' : 'Camera'}
</div>
<div className="camera__badge camera__badge--right" aria-label="Audio status">
<span title={micMuted ? 'Phone microphone off' : 'Phone microphone on'}>
{micMuted ? 'Mic off' : 'Mic on'}
</span>
<span className="camera__badge-separator" />
<span title={audioMuted ? 'Robot speaker off' : 'Robot speaker on'}>
{audioMuted ? 'Sound off' : 'Sound on'}
</span>
</div>
</>
)}
</section>
);
}