import React, { useRef, useEffect, useState } from 'react';
import { Camera } from 'lucide-react';
interface VideoFeedProps {
onFrameCapture: (base64: string) => void;
isMonitoring: boolean;
}
export const VideoFeed: React.FC<VideoFeedProps> = ({ onFrameCapture, isMonitoring }) => {
const videoRef = useRef<HTMLVideoElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
const [error, setError] = useState<string | null>(null);
const [streamActive, setStreamActive] = useState(false);
/* ------------------- Camera Setup ------------------- */
useEffect(() => {
let stream: MediaStream | null = null;
const startCamera = async () => {
try {
stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'user', width: { ideal: 1280 }, height: { ideal: 720 } }
});
if (videoRef.current) {
videoRef.current.srcObject = stream;
setStreamActive(true);
}
} catch (err) {
console.error("Camera error:", err);
setError("Could not access camera. Ensure permissions are granted.");
}
};
startCamera();
return () => {
if (stream) stream.getTracks().forEach(track => track.stop());
};
}, []);
/* ------------------- Frame Capture ------------------- */
useEffect(() => {
let intervalId: number;
if (isMonitoring && streamActive) {
intervalId = window.setInterval(() => {
if (videoRef.current && canvasRef.current) {
const video = videoRef.current;
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
if (ctx && video.readyState === 4) {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
onFrameCapture(canvas.toDataURL('image/jpeg', 0.8));
}
}
}, 4000);
}
return () => window.clearInterval(intervalId);
}, [isMonitoring, streamActive, onFrameCapture]);
/* ------------------- Render ------------------- */
return (
<div className="relative w-full h-full bg-black rounded-lg overflow-hidden border border-slate-700 shadow-2xl group">
{error ? (
<div className="absolute inset-0 flex flex-col items-center justify-center text-red-500 font-mono text-sm p-4 text-center">
<Camera className="mb-2 w-12 h-12" />
<span className="font-bold tracking-widest">{error}</span>
</div>
) : (
<>
<video
ref={videoRef}
autoPlay
playsInline
muted
className="w-full h-full object-cover opacity-80 grayscale-[10%]"
/>
<canvas ref={canvasRef} className="hidden" />
{/* ---------------- HUD Overlay ---------------- */}
<div className="absolute inset-0 pointer-events-none">
{/* Corner Brackets */}
<div className="absolute top-4 left-4 w-8 h-8 border-t-2 border-l-2 border-aegis-accent opacity-50" />
<div className="absolute top-4 right-4 w-8 h-8 border-t-2 border-r-2 border-aegis-accent opacity-50" />
<div className="absolute bottom-4 left-4 w-8 h-8 border-b-2 border-l-2 border-aegis-accent opacity-50" />
<div className="absolute bottom-4 right-4 w-8 h-8 border-b-2 border-r-2 border-aegis-accent opacity-50" />
{/* Center Reticle */}
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<div className={`w-12 h-12 border border-aegis-accent rounded-full flex items-center justify-center transition-all duration-300 ${isMonitoring ? 'scale-100 opacity-80' : 'scale-50 opacity-20'}`}>
<div className="w-1 h-1 bg-red-500 rounded-full animate-pulse"></div>
</div>
</div>
{/* Scan Line */}
{isMonitoring && (
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-aegis-accent/10 to-transparent animate-scan" />
)}
{/* Status Indicators */}
<div className="absolute top-4 left-14 bg-black/50 backdrop-blur px-2 py-1 rounded border border-gray-700">
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${isMonitoring ? 'bg-red-500 animate-pulse' : 'bg-gray-500'}`} />
<span className="font-mono text-xs text-white tracking-widest uppercase">
{isMonitoring ? 'AEGIS // LIVE' : 'AEGIS // STANDBY'}
</span>
</div>
</div>
<div className="absolute bottom-4 right-14 font-mono text-xs text-aegis-accent opacity-70">
CAM_01 // 1080p // 30FPS
</div>
</div>
</>
)}
</div>
);
};