Spaces:
Running
Running
Upload 51 files
Browse files
src/components/MultiSourceCaptioningView.tsx
CHANGED
@@ -83,6 +83,7 @@ export default function MultiSourceCaptioningView() {
|
|
83 |
|
84 |
const videoRef = useRef<HTMLVideoElement | null>(null);
|
85 |
const overlayVideoRef = useRef<HTMLVideoElement | null>(null);
|
|
|
86 |
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
87 |
const imageRef = useRef<HTMLImageElement | null>(null);
|
88 |
const webcamStreamRef = useRef<MediaStream | null>(null);
|
@@ -116,8 +117,8 @@ export default function MultiSourceCaptioningView() {
|
|
116 |
}, [videoRef, overlayVideoRef, uploadedUrl, videoUrl, mode]);
|
117 |
|
118 |
const processVideoFrame = async () => {
|
119 |
-
if (!
|
120 |
-
const video =
|
121 |
const canvas = canvasRef.current;
|
122 |
if (video.paused || video.ended || video.videoWidth === 0) return;
|
123 |
canvas.width = video.videoWidth;
|
@@ -132,19 +133,20 @@ export default function MultiSourceCaptioningView() {
|
|
132 |
boxes = parseFlatBoxArray(output);
|
133 |
}
|
134 |
boxes = normalizeBoxes(boxes);
|
135 |
-
|
136 |
-
|
137 |
-
console.log("Canvas size:", canvas.width, canvas.height);
|
138 |
-
if (boxes.length > 0) {
|
139 |
-
const [x1, y1, x2, y2] = boxes[0].bbox_2d;
|
140 |
-
console.log("First box coords:", x1, y1, x2, y2);
|
141 |
-
}
|
142 |
-
if (boxes.length === 0) setInferenceStatus("No boxes detected or model output invalid.");
|
143 |
if (Array.isArray(boxes) && boxes.length > 0) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
const scaleX = canvas.width / video.videoWidth;
|
145 |
const scaleY = canvas.height / video.videoHeight;
|
146 |
-
ctx
|
147 |
-
drawBoundingBoxesOnCanvas(ctx, boxes, { color: "#FF00FF", lineWidth: 4, font: "20px Arial", scaleX, scaleY }); // Use visible color and thick line
|
148 |
}
|
149 |
});
|
150 |
};
|
@@ -397,24 +399,27 @@ export default function MultiSourceCaptioningView() {
|
|
397 |
/>
|
398 |
</div>
|
399 |
<div className="relative w-full max-w-xl">
|
|
|
400 |
<video
|
401 |
-
ref={
|
402 |
src={videoUrl}
|
403 |
controls
|
404 |
autoPlay
|
405 |
loop
|
|
|
|
|
406 |
className="w-full rounded-lg shadow-lg mb-2"
|
407 |
style={{ background: "#222" }}
|
408 |
/>
|
|
|
409 |
<video
|
410 |
-
ref={
|
411 |
src={videoUrl}
|
412 |
-
controls={false}
|
413 |
autoPlay
|
414 |
loop
|
415 |
muted
|
416 |
-
|
417 |
-
style={{
|
418 |
/>
|
419 |
<canvas
|
420 |
ref={canvasRef}
|
@@ -489,24 +494,27 @@ export default function MultiSourceCaptioningView() {
|
|
489 |
{/* Show uploaded video */}
|
490 |
{uploadedFile && isVideoFile(uploadedFile) && (
|
491 |
<div className="relative w-full max-w-xl">
|
|
|
492 |
<video
|
493 |
-
ref={
|
494 |
src={uploadedUrl}
|
495 |
controls
|
496 |
autoPlay
|
497 |
loop
|
|
|
|
|
498 |
className="w-full rounded-lg shadow-lg mb-2"
|
499 |
style={{ background: "#222" }}
|
500 |
/>
|
|
|
501 |
<video
|
502 |
-
ref={
|
503 |
src={uploadedUrl}
|
504 |
-
controls={false}
|
505 |
autoPlay
|
506 |
loop
|
507 |
muted
|
508 |
-
|
509 |
-
style={{
|
510 |
/>
|
511 |
<canvas
|
512 |
ref={canvasRef}
|
@@ -524,24 +532,27 @@ export default function MultiSourceCaptioningView() {
|
|
524 |
{/* Show example video if no file uploaded */}
|
525 |
{!uploadedFile && (
|
526 |
<div className="relative w-full max-w-xl">
|
|
|
527 |
<video
|
528 |
-
ref={
|
529 |
src={EXAMPLE_VIDEO_URL}
|
530 |
controls
|
531 |
autoPlay
|
532 |
loop
|
|
|
|
|
533 |
className="w-full rounded-lg shadow-lg mb-2"
|
534 |
style={{ background: "#222" }}
|
535 |
/>
|
|
|
536 |
<video
|
537 |
-
ref={
|
538 |
src={EXAMPLE_VIDEO_URL}
|
539 |
-
controls={false}
|
540 |
autoPlay
|
541 |
loop
|
542 |
muted
|
543 |
-
|
544 |
-
style={{
|
545 |
/>
|
546 |
<canvas
|
547 |
ref={canvasRef}
|
|
|
83 |
|
84 |
const videoRef = useRef<HTMLVideoElement | null>(null);
|
85 |
const overlayVideoRef = useRef<HTMLVideoElement | null>(null);
|
86 |
+
const processingVideoRef = useRef<HTMLVideoElement | null>(null);
|
87 |
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
88 |
const imageRef = useRef<HTMLImageElement | null>(null);
|
89 |
const webcamStreamRef = useRef<MediaStream | null>(null);
|
|
|
117 |
}, [videoRef, overlayVideoRef, uploadedUrl, videoUrl, mode]);
|
118 |
|
119 |
const processVideoFrame = async () => {
|
120 |
+
if (!processingVideoRef.current || !canvasRef.current) return;
|
121 |
+
const video = processingVideoRef.current;
|
122 |
const canvas = canvasRef.current;
|
123 |
if (video.paused || video.ended || video.videoWidth === 0) return;
|
124 |
canvas.width = video.videoWidth;
|
|
|
133 |
boxes = parseFlatBoxArray(output);
|
134 |
}
|
135 |
boxes = normalizeBoxes(boxes);
|
136 |
+
// Box persistence logic (2 seconds)
|
137 |
+
const now = Date.now();
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
if (Array.isArray(boxes) && boxes.length > 0) {
|
139 |
+
if (!window._boxHistory) window._boxHistory = [];
|
140 |
+
window._boxHistory = window._boxHistory.filter((b) => now - b.timestamp < 2000);
|
141 |
+
window._boxHistory.push(...boxes.map(box => ({ ...box, timestamp: now })));
|
142 |
+
}
|
143 |
+
// Draw all boxes from last 2 seconds
|
144 |
+
const boxHistory = (window._boxHistory || []).filter((b) => now - b.timestamp < 2000);
|
145 |
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
146 |
+
if (boxHistory.length > 0) {
|
147 |
const scaleX = canvas.width / video.videoWidth;
|
148 |
const scaleY = canvas.height / video.videoHeight;
|
149 |
+
drawBoundingBoxesOnCanvas(ctx, boxHistory, { color: "#FF00FF", lineWidth: 4, font: "20px Arial", scaleX, scaleY });
|
|
|
150 |
}
|
151 |
});
|
152 |
};
|
|
|
399 |
/>
|
400 |
</div>
|
401 |
<div className="relative w-full max-w-xl">
|
402 |
+
{/* Visible overlay video for user */}
|
403 |
<video
|
404 |
+
ref={overlayVideoRef}
|
405 |
src={videoUrl}
|
406 |
controls
|
407 |
autoPlay
|
408 |
loop
|
409 |
+
muted
|
410 |
+
playsInline
|
411 |
className="w-full rounded-lg shadow-lg mb-2"
|
412 |
style={{ background: "#222" }}
|
413 |
/>
|
414 |
+
{/* Hidden processing video for FastVLM/canvas */}
|
415 |
<video
|
416 |
+
ref={processingVideoRef}
|
417 |
src={videoUrl}
|
|
|
418 |
autoPlay
|
419 |
loop
|
420 |
muted
|
421 |
+
playsInline
|
422 |
+
style={{ display: "none" }}
|
423 |
/>
|
424 |
<canvas
|
425 |
ref={canvasRef}
|
|
|
494 |
{/* Show uploaded video */}
|
495 |
{uploadedFile && isVideoFile(uploadedFile) && (
|
496 |
<div className="relative w-full max-w-xl">
|
497 |
+
{/* Visible overlay video for user */}
|
498 |
<video
|
499 |
+
ref={overlayVideoRef}
|
500 |
src={uploadedUrl}
|
501 |
controls
|
502 |
autoPlay
|
503 |
loop
|
504 |
+
muted
|
505 |
+
playsInline
|
506 |
className="w-full rounded-lg shadow-lg mb-2"
|
507 |
style={{ background: "#222" }}
|
508 |
/>
|
509 |
+
{/* Hidden processing video for FastVLM/canvas */}
|
510 |
<video
|
511 |
+
ref={processingVideoRef}
|
512 |
src={uploadedUrl}
|
|
|
513 |
autoPlay
|
514 |
loop
|
515 |
muted
|
516 |
+
playsInline
|
517 |
+
style={{ display: "none" }}
|
518 |
/>
|
519 |
<canvas
|
520 |
ref={canvasRef}
|
|
|
532 |
{/* Show example video if no file uploaded */}
|
533 |
{!uploadedFile && (
|
534 |
<div className="relative w-full max-w-xl">
|
535 |
+
{/* Visible overlay video for user */}
|
536 |
<video
|
537 |
+
ref={overlayVideoRef}
|
538 |
src={EXAMPLE_VIDEO_URL}
|
539 |
controls
|
540 |
autoPlay
|
541 |
loop
|
542 |
+
muted
|
543 |
+
playsInline
|
544 |
className="w-full rounded-lg shadow-lg mb-2"
|
545 |
style={{ background: "#222" }}
|
546 |
/>
|
547 |
+
{/* Hidden processing video for FastVLM/canvas */}
|
548 |
<video
|
549 |
+
ref={processingVideoRef}
|
550 |
src={EXAMPLE_VIDEO_URL}
|
|
|
551 |
autoPlay
|
552 |
loop
|
553 |
muted
|
554 |
+
playsInline
|
555 |
+
style={{ display: "none" }}
|
556 |
/>
|
557 |
<canvas
|
558 |
ref={canvasRef}
|