데이터 파이프라인 — 전 구간 프레임·djmd dt 전파·세그먼트 시크·선형 곡선 평활

- geoData: fullFrames(전체 비행 프레임 — 전 구간 측점 검색용, datum·초점 보정 동일
  적용), DJI_VIRTUAL_FPS export, 선형 곡선 평활(Centripetal Catmull-Rom — 정점
  ~6m 세분, 원 정점 전부 통과로 측점 정합 유지, 60m+ 직선·양끝 제외)
- djmdTrack: refineFramesWithDjmd 가 교차상관 dt(초)도 반환
- geoStore: fullFrames·djmdDtSec 상태 — 검색의 CSV↔영상 시간 변환에 사용
- useVideoPlayer.loadLocalFile(startAtSec): 로드 후 지정 시각부터 시작
- VideoPlayer.handleSeekSegment: 측점 검색의 타 세그먼트 점프(전환+시크,
  재생 상태 유지), station_lookahead 등 RouteInfo 타입 문서화

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 16:27:03 +09:00
co-authored by Claude Fable 5
parent 76a2ab4984
commit 87b66d9ed4
6 changed files with 102 additions and 10 deletions
+17 -1
View File
@@ -38,7 +38,7 @@ function collectDropEntry(entry: FileSystemEntry, out: File[]): Promise<void> {
}
export interface VideoPlayerHandle {
loadLocalFile: (file: File) => void;
loadLocalFile: (file: File, autoPlay?: boolean, startAtSec?: number) => void;
loadServerStream: (videoId: string, filename: string) => void | Promise<void>;
seekTo: (time: number) => void;
getVideoElement: () => HTMLVideoElement | null;
@@ -232,6 +232,21 @@ const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
}
};
// 측점 검색 전 구간 점프 — 다른 세그먼트의 통과 지점으로 전환+시크.
// 같은 세그먼트면 시크만, 다르면 loadSegment 후 해당 시각부터(재생 상태 유지).
const handleSeekSegment = async (i: number, timeSec: number): Promise<void> => {
const geo = useGeoStore.getState();
if (usePlayerStore.getState().source?.kind !== 'local') return;
if (i === geo.videoIndex) {
playerRef.current?.currentTime(timeSec);
return;
}
const wasPlaying = usePlayerStore.getState().playing;
const file = await geo.loadSegment(i);
if (!file) return;
loadLocalFile(file, wasPlaying, timeSec);
};
// 분할 영상 연속재생 — 영상이 끝나면 같은 폴더의 다음 영상(이름 오름차순)을 자동 재생.
// 드론 로그(시간 기반)는 loadSegment 가 누적 오프셋으로 재정렬해 궤적 정합을 유지한다.
useEffect(() => {
@@ -625,6 +640,7 @@ const VideoPlayer = forwardRef<VideoPlayerHandle, VideoPlayerProps>(
onStop={handleStop}
onCapture={handleCaptureFrame}
onSeek={handleSeek}
onSeekSegment={(i, t) => { void handleSeekSegment(i, t); }}
showStations={showStations}
onToggleStations={() => setShowStations((v) => !v)}
/>