feat(results): replace video playback with detection image preview and map

This commit is contained in:
2026-07-07 15:36:27 +05:30
parent d88978a830
commit fb9a654aea
13 changed files with 433 additions and 36 deletions

View File

@@ -1,14 +1,17 @@
'use client';
import { RefObject } from 'react';
import { RefObject, useEffect } from 'react';
import { AlertTriangle, Loader2 } from 'lucide-react';
import type { DetectionResultLog } from '@/types';
import { useDetectionThumbnails } from './useDetectionThumbnails';
import BoundingBoxOverlay from '@/components/annotation/boundingBoxOverlay';
import {
MediaPlayer,
MediaProvider,
Poster,
isVideoProvider,
type MediaPlayerInstance,
useMediaProvider,
} from '@vidstack/react';
import {
DefaultVideoLayout,
@@ -18,22 +21,54 @@ import {
interface AnnotatedVideoPlayerProps {
videoRef: RefObject<MediaPlayerInstance | null>;
logs: DetectionResultLog[];
visibleLogs: DetectionResultLog[];
videoWidth: number;
videoHeight: number;
videoUrl: string | null;
isLoading: boolean;
isError: boolean;
onRetry: () => void;
onTimeUpdate: () => void;
onVideoFrame: (mediaTime: number) => void;
onSeeked: () => void;
}
function VideoFrameSync({
onVideoFrame,
}: Pick<AnnotatedVideoPlayerProps, 'onVideoFrame'>) {
const provider = useMediaProvider();
useEffect(() => {
if (!isVideoProvider(provider)) return;
const video = provider.video;
let callbackId: number;
const update = (_now: number, metadata: VideoFrameCallbackMetadata) => {
onVideoFrame(metadata.mediaTime);
callbackId = video.requestVideoFrameCallback(update);
};
callbackId = video.requestVideoFrameCallback(update);
return () => video.cancelVideoFrameCallback(callbackId);
}, [onVideoFrame, provider]);
return null;
}
export default function AnnotatedVideoPlayer({
videoRef,
logs,
visibleLogs,
videoWidth,
videoHeight,
videoUrl,
isLoading,
isError,
onRetry,
onTimeUpdate,
onVideoFrame,
onSeeked,
}: AnnotatedVideoPlayerProps) {
const thumbnails = useDetectionThumbnails(logs);
@@ -55,6 +90,12 @@ export default function AnnotatedVideoPlayer({
<MediaProvider>
<Poster className="vds-poster" />
</MediaProvider>
<VideoFrameSync onVideoFrame={onVideoFrame} />
<BoundingBoxOverlay
detections={visibleLogs}
videoWidth={videoWidth}
videoHeight={videoHeight}
/>
<DefaultVideoLayout
thumbnails={thumbnails}
icons={defaultLayoutIcons}