Spaces:
Sleeping
Sleeping
| import { useEffect, useRef, useState, useCallback } from 'react'; | |
| export interface WebcamState { | |
| videoRef: React.RefObject<HTMLVideoElement | null>; | |
| isReady: boolean; | |
| error: string | null; | |
| facingMode: 'user' | 'environment'; | |
| switchCamera: () => void; | |
| } | |
| /** | |
| * Hook to access the device webcam. | |
| * Supports front/back camera toggle for mobile devices. | |
| */ | |
| export function useWebcam(): WebcamState { | |
| const videoRef = useRef<HTMLVideoElement | null>(null); | |
| const streamRef = useRef<MediaStream | null>(null); | |
| const [isReady, setIsReady] = useState(false); | |
| const [error, setError] = useState<string | null>(null); | |
| const [facingMode, setFacingMode] = useState<'user' | 'environment'>('user'); | |
| const startCamera = useCallback(async (mode: 'user' | 'environment') => { | |
| // Stop any existing stream first | |
| streamRef.current?.getTracks().forEach(t => t.stop()); | |
| setIsReady(false); | |
| setError(null); | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ | |
| video: { width: { ideal: 1280 }, height: { ideal: 720 }, facingMode: mode }, | |
| audio: false, | |
| }); | |
| streamRef.current = stream; | |
| if (videoRef.current) { | |
| videoRef.current.srcObject = stream; | |
| videoRef.current.onloadedmetadata = () => { | |
| videoRef.current?.play(); | |
| setIsReady(true); | |
| }; | |
| } | |
| } catch (err) { | |
| const msg = err instanceof Error ? err.message : String(err); | |
| setError( | |
| msg.includes('Permission') | |
| ? 'Camera permission denied. Please allow camera access and reload.' | |
| : `Camera error: ${msg}` | |
| ); | |
| } | |
| }, []); | |
| // Start camera on mount and whenever facingMode changes | |
| useEffect(() => { | |
| startCamera(facingMode); | |
| return () => { | |
| streamRef.current?.getTracks().forEach(t => t.stop()); | |
| }; | |
| }, [facingMode, startCamera]); | |
| const switchCamera = useCallback(() => { | |
| setFacingMode(prev => (prev === 'user' ? 'environment' : 'user')); | |
| }, []); | |
| return { videoRef, isReady, error, facingMode, switchCamera }; | |
| } | |