返回 presentation-ai
WebcamOverlay.tsx
根目录 / src / components / notebook / presentation / recording / WebcamOverlay.tsx
1 "use client";
2
3 import { motion } from "motion/react";
4 import { useCallback, useEffect, useRef, useState } from "react";
5
6 import { cn } from "@/lib/utils";
7 import { usePresentationRecordingState } from "@/states/presentation-recording-state";
8
9 function WebcamOverlay() {
10 const {
11 wantsToRecord,
12 webcamEnabled,
13 selectedVideoDeviceId,
14 selectedAudioDeviceId,
15 setOverlayPosition,
16 setCamStream,
17 } = usePresentationRecordingState();
18 const videoRef = useRef<HTMLVideoElement | null>(null);
19 const [localStream, setLocalStream] = useState<MediaStream | null>(null);
20
21 console.log("WebcamOverlay", wantsToRecord, webcamEnabled);
22 // Get camera stream when setup opens and webcam is enabled
23 const getCameraStream = useCallback(async () => {
24 if (!webcamEnabled || !wantsToRecord) return;
25
26 try {
27 const stream = await navigator.mediaDevices.getUserMedia({
28 video: selectedVideoDeviceId
29 ? { deviceId: { exact: selectedVideoDeviceId } }
30 : true,
31
32 audio: selectedAudioDeviceId
33 ? { deviceId: { exact: selectedAudioDeviceId } }
34 : true,
35 });
36 setLocalStream(stream);
37 setCamStream(stream);
38 } catch (err) {
39 console.error("Failed to get camera stream:", err);
40 }
41 }, [
42 webcamEnabled,
43 wantsToRecord,
44 selectedVideoDeviceId,
45 selectedAudioDeviceId,
46 setCamStream,
47 ]);
48
49 useEffect(() => {
50 if (wantsToRecord && webcamEnabled) {
51 getCameraStream();
52 }
53
54 return () => {
55 if (localStream) {
56 localStream.getTracks().forEach((track) => track.stop());
57 setLocalStream(null);
58 }
59 };
60 }, [wantsToRecord, webcamEnabled, selectedVideoDeviceId, getCameraStream]);
61
62 useEffect(() => {
63 const v = videoRef.current;
64 if (!v) return;
65 if (localStream && webcamEnabled) {
66 v.srcObject = localStream;
67 v.muted = true;
68 v.playsInline = true;
69 v.play().catch(() => {});
70 }
71 }, [localStream, webcamEnabled]);
72
73 if (!wantsToRecord || !webcamEnabled || !localStream) return null;
74
75 return (
76 <motion.div
77 drag
78 dragMomentum={false}
79 dragConstraints={{
80 top: 0,
81 left: 0,
82 right: window.innerWidth,
83 bottom: window.innerHeight,
84 }}
85 onDragEnd={(_, info) => {
86 console.log(info.point);
87 setOverlayPosition(
88 Math.max(0, info.point.x),
89 Math.max(0, info.point.y),
90 );
91 }}
92 className={cn(
93 "fixed z-999999 overflow-hidden rounded-xl border border-border shadow",
94 "aspect-video w-80 bg-background/50 backdrop-blur supports-backdrop-filter:bg-background/40",
95 )}
96 >
97 <video
98 ref={videoRef}
99 className="presentation-webcam-overlay block h-auto w-full"
100 />
101 </motion.div>
102 );
103 }
104
105 export default WebcamOverlay;
106
106 lines Plain Text