Files
GhiVideo/PROGRESS.md
T
b23042andClaude Opus 4.8 78fc090360 초기 커밋: 스테이션(측점) 기반 주행영상 플레이어
- 클라이언트(React/Vite): Video.js 플레이어, 하단 스테이션바, POI/구조물 영상 오버레이, 카메라 파라미터 보정, 미니맵/RoutePanel
- 서버(Express): Range 스트리밍, HLS 변환, 프레임 추출, tus 업로드, 주석 API
- 최근 작업: 스테이션바 종점역 표출/양끝 정렬/미도착(재생불가) 표시, POI 라벨 '구분' 우선·컴팩트 팝업·겹침제외 토글·동일좌표 다중행, 진행방향(상/하) 우선 표출, 커서 배지 크기·픽셀 떨림 개선

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:37:57 +09:00

266 lines
18 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROGRESS.md — abcvideo 진행 상태
> 이 파일은 각 단계의 진행 상태를 기록합니다.
> 에이전트는 작업 시작/완료 시 반드시 이 파일을 업데이트합니다.
---
## 현재 상태 요약
- 마지막 완료 단계: 구조물 시설종별(1종/2종/3종) 표시 필터 (2026-06-19)
- 직전 완료 단계: 폴더 임포트 v2.0 데이터 형상 전용화 (2026-06-19)
- 블로커: FFmpeg 미설치 (HLS/프레임 기능은 FFmpeg 설치 후 동작)
- 검증 결과: VERIFICATION.md 참조 (모든 항목 통과, V3-3 RAF 버그 1건 수정 완료)
- v2.0 전환: geoData.ts/types/geo.ts/geoStore.ts/StationBar.tsx 수정, client 빌드 통과
---
## 단계별 진행 기록
### 단계 1: 프로젝트 구조 세팅
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- 루트 package.json (npm workspaces)
- shared/src/types.ts (공유 타입)
- server/ 스캐폴드 + tsx dev 환경
- client/ Vite + React 18 + TypeScript + Tailwind
- storage/ 디렉토리 구조
- npm install 성공 (514 packages)
- server TypeScript 컴파일 에러 없음
- GET /api/health 응답 확인
- 미완료/이슈:
- better-sqlite3 ^12.8.0으로 업그레이드 (Node 24 prebuilt binary 문제)
- 다음 단계 참고:
- server/package.json에 better-sqlite3 ^12.8.0 사용 중
- client Vite dev는 아직 미확인 (브라우저 확인 필요)
- server/src/app.ts는 최소 스캐폴드 — 단계 2에서 확장
### 단계 2: 백엔드 서버
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- server/src/config.ts — 환경변수 기반 설정
- server/src/services/ffmpeg.ts — FFprobe/FFmpeg spawn 래퍼 (probeVideo, runFFmpeg, getVideoFps 등)
- server/src/services/streaming.ts — Range Request 스트리밍 (10MB 청크, 1MB highWaterMark)
- server/src/services/storage.ts — better-sqlite3 초기화, Annotation CRUD, 임시파일 정리
- server/src/middleware/security.ts — pathTraversalGuard, validateMimeType, securityHeaders
- server/src/routes/stream.ts — GET /api/stream/:videoId
- server/src/routes/hls.ts — POST /convert, GET /status, GET /progress(SSE), GET /index.m3u8, GET /:segment
- server/src/routes/frame.ts — GET /api/frame/:videoId?time=|frame=
- server/src/routes/upload.ts — 청크 업로드 (init/chunk/complete/status), @tus/server 대신 자체 구현
- server/src/routes/meta.ts — GET /api/videos, GET /api/meta/:videoId, DELETE /api/videos/:videoId
- server/src/routes/annotations.ts — CRUD + export(vtt/srt/json/csv)
- server/src/app.ts — 전체 통합, 헬스체크, 에러 핸들러
- server/.env — 환경변수 파일
- server/tsconfig.json — rootDirs 수정 (shared 타입 접근 허용)
- GET /api/health, /api/videos, /api/annotations/:videoId 정상 응답 확인
- 미완료/이슈:
- @tus/server 2.3.0이 ESM-only("type":"module")라 CommonJS 서버와 호환 불가 → 자체 청크 업로드 구현으로 대체
- FFmpeg 시스템 PATH 미등록 상태 → HLS 변환/프레임 추출은 FFmpeg 설치 후 동작
- 다음 단계 참고:
- 청크 업로드 클라이언트는 POST /api/upload/init → POST /api/upload/chunk (X-Upload-Id, X-Chunk-Index 헤더, application/octet-stream body) → POST /api/upload/complete 순서 사용
- HLS 변환 완료 후 /api/hls/:videoId/index.m3u8 로 플레이리스트 접근
- 서버는 포트 3001에서 실행 중
### 단계 3: 기본 플레이어
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/types/player.ts — VideoSource, PlayerState 타입
- client/src/store/playerStore.ts — Zustand 플레이어 상태 스토어
- client/src/store/annotationStore.ts — 주석 Zustand 스토어
- client/src/hooks/useVideoPlayer.ts — Video.js 8 초기화, loadLocalFile, loadServerStream, switchToHls
- client/src/components/player/VideoPlayer.tsx — forwardRef + VideoPlayerHandle API
- client/src/components/player/HlsConversionStatus.tsx — SSE 진행률 바
- client/src/components/sidebar/VideoList.tsx — 서버 영상 목록
- client/src/App.tsx — 전체 레이아웃 통합
- npm install: video.js@^8.23.0, @videojs/http-streaming, hls.js@^1.6.15, interactjs@^1.10.27, subsrt-ts@^2.0.3
- TypeScript 에러 없음, 빌드 성공 (dist 1.47MB)
- 미완료/이슈:
- interact.js 패키지명 오류 → interactjs (하이픈 없음) 으로 설치
- 빌드 경고: chunk > 500kB (video.js 특성상 정상, 운영환경 분할 불필요)
- 다음 단계 참고:
- VideoPlayer는 forwardRef<VideoPlayerHandle> 사용 — App에서 playerRef.current?.loadServerStream() 호출
- hls.js backBufferLength: 30 설정 완료
- MemoOverlay는 interactjs 사용 (import from 'interactjs')
### 단계 4: 프레임 추출
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/utils/frameCapture.ts — 단일 Canvas 재사용, captureFrame, downloadDataUrl
- client/src/components/player/FrameCaptureButton.tsx — Shift+S 캡처 버튼
- VideoPlayer에 캡처 통합 (getVideoElement → captureFrame → downloadDataUrl)
- 미완료/이슈: 서버 FFmpeg 정확 추출(로컬 파일 정확 추출 시)은 미구현 — Canvas 즉시 미리보기만 구현
- 다음 단계 참고: (없음)
### 단계 5: 단위 이동
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/hooks/useFrameStep.ts — requestVideoFrameCallback FPS 감지, stepForward/stepBackward
- client/src/hooks/useKeyboard.ts — CLAUDE.md 키보드 단축키 전체 구현
- 프레임/초/장면 단위 이동 완성 (Space/←/→/J/L/,/./[/]/F/M/+/-/Shift+S/Shift+M/0-9)
- 미완료/이슈: (없음)
- 다음 단계 참고: (없음)
### 단계 6: 텍스트 오버레이
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/hooks/useAnnotations.ts — 주석 CRUD API 연동
- client/src/components/overlay/MemoOverlay.tsx — interactjs 드래그, x%/y% 좌표
- client/src/components/sidebar/AnnotationPanel.tsx — 자막/메모 탭, 내보내기(VTT/SRT/JSON/CSV)
- client/src/components/AddAnnotationModal.tsx — 주석 추가 모달
- client/src/utils/timecode.ts — secondsToTimecode, timecodeToSeconds, frameToSeconds, secondsToFrame
- 미완료/이슈:
- WebVTT track 요소를 통한 자막형 렌더링은 미구현 (주석 저장/표시는 완성, track 연동은 단계 7)
- 다음 단계 참고:
- 자막형 WebVTT track 연동은 단계 7 UI/UX 통합 시 추가 가능
- 내보내기는 /api/annotations/:videoId/export?format=vtt|srt|json|csv 백엔드 API 사용
### 검증 (VERIFICATION)
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- VERIFICATION.md 작성 및 전 항목 검증 실행
- V1 정적 검증: server/client tsc 에러 없음, 빌드 성공
- V2 서버 API: 헬스체크/영상목록/업로드/주석 CRUD/내보내기/보안 모두 통과
- V3 코드 품질: 10/10 통과 (MemoOverlay RAF 버그 발견 및 수정)
- V4 파일 구조: 19개 필수 파일 전부 존재 확인
- 수정 사항:
- `VideoPlayerHandle``getVideoElement()` 추가 (VideoPlayer.tsx)
- App.tsx RAF 루프 추가 → MemoOverlay `currentTime` 60fps 업데이트
### 기능 확장: 카메라 파라미터 보정 패널 (2026-03-26)
- 상태: ✅ 완료
- 완료 항목:
- client/src/utils/geoProjection.ts — CameraParams 인터페이스 추가, projectPoint 시그니처 변경
- yawOffset(number) → params(CameraParams) 로 통합
- Roll 회전 수학 추가 (Rodrigues, fwd 축 기준): right_r = right0·cosR up0·sinR
- 주점 오프셋(cx0, cy0) 핀홀 투영에 적용
- 초점 배율(focalScale) 적용: f_eff = focalLen × focalScale
- pxRaw/pyRaw 필드 추가 (클램프 전 원본값, FOV 이탈 감지용)
- DEFAULT_CAMERA_PARAMS export
- client/src/components/overlay/StationOverlay.tsx — 파라미터 패널 UI 전면 개편
- ParamRow 컴포넌트: 슬라이더 + 직접 수치 입력(Enter/blur 커밋) 양방향 동기화
- 자세 보정 섹션: Yaw(-180~+180°), Pitch(-45~+45°), Roll(-45~+45°)
- 내부표정 섹션: f×(0.3~3.0배율), cx₀(-0.5~+0.5), cy₀(-0.5~+0.5)
- 현재 유효값 실시간 표시: yaw_eff, pitch_eff, f_eff(mm), hFOV(°)
- 측점 Canvas 마커 구현: 다이아몬드 ◆ + 측점명 레이블 + 거리(m)
- FOV 이탈 측점 → 가장자리 방향 화살표(drawEdgeArrow)
- POI 마커: + 심볼 + 레이블
- 지면 가이드선: 측점 → 화면 하단 점선
- 나침반 HUD: hFOV를 focalScale 반영하여 동적 계산
- 초기화 버튼, 드론 경로 체크박스
- 빌드 성공: tsc + vite build 에러 없음 (2026-03-26)
- 발견 사항:
- SRT gb_roll=0.0 (전 프레임 고정) → Roll 보정은 이 영상에서 효과 없으나 다른 영상 대비 구조 완성
- 자기 편각(-8°) 보정이 yawOffset 슬라이더의 주된 용도
- focalScale로 실효 FOV를 조정할 수 있어 정밀 보정 가능
- 미완료/이슈:
- GCP 클릭 기반 자동 Resection 미구현 (yaw/pitch 자동 계산)
- 파라미터 설정값 영속 저장(localStorage) 미구현
### 기능 확장: 지리정보 오버레이 + 측점 검증 (2026-03-25)
- 상태: ✅ 완료
- 완료 항목:
- server/src/services/geoMatch.ts — 블렌더 방식 월드 ENU 좌표계 구현
- geoToEnu(): 위경도 → ENU(m), 기준점 위도로 cos(lat) 고정 (전 프레임 일관성)
- projectEnu(): 상대 ENU 벡터 + 카메라 회전 → 핀홀 투영
- project3D(): geoToEnu + projectEnu 래퍼, origin 파라미터 지원
- findFramesForPoi / findPoisForFrame — frames[0]를 월드 원점으로 전달
- getDroneFrames() 신규 export
- server/src/routes/geo.ts — yawOffset 쿼리 파라미터, GET /api/geo/frames 엔드포인트
- client/src/utils/geoProjection.ts — 서버와 동일한 블렌더 방식 ENU 투영, ref 파라미터 추가
- client/src/components/overlay/StationOverlay.tsx — yaw 보정 슬라이더(-180~+180°), 드론 경로 오버레이, pathFrames[0] 원점 전달
- client/src/components/geo/StationVerify.tsx — 측점 목록 + 클릭 검증 패널 (신규)
- client/src/App.tsx — 우측 패널에 "측점" 탭 추가
- server/client 빌드 모두 에러 없음 확인
- 발견 사항:
- cos(lat) 보정을 각 프레임 위도가 아닌 기준점(frame 0) 위도로 고정해야 전 구간 일관성 보장
- CSV yaw/pitch/roll = 짐벌(카메라) 방향 (pitch=-29.8°는 짐벌 고정 틸트, roll=0은 짐벌 보정)
- frame 1791 기준 158K300: px=70.1% (사용자 관측 ~73%와 일치)
- 미완료/이슈:
- Z값(표고) 미적용 — 측점 z값이 ~0m이라 수직 오차 존재, 실제 표고 데이터 확보 후 보정 필요
- GCP 캘리브레이션 UI 미구현 (클릭으로 yaw/pitch 오프셋 자동 계산)
### 지리정보 오버레이 — Python advanced_tuner_v2 포팅 (2026-03-30)
- 상태: ✅ 완료
- 완료 항목:
- geoProjection.ts 재구현: R_b2w=Rz(-yaw)*Rx(pitch)*Ry(roll), R_w2c=R_align@R_b2w.T (Python 동치)
- pitch/roll 절대값 → 오프셋 방식 (Python spn_pitch/roll 기본값 0과 동일)
- sensorH 기본값 20.25mm (=36×9/16, 16:9 영상 기준, 기존 24mm 수직 스케일 오류 수정)
- CameraParams에 offX/offY/offZ 추가 (Python off_x/y/z 동치)
- geoMatch.ts: center.csv 224점 로더 추가 (getCenterlinePoints)
- GET /api/geo/centerline 엔드포인트 추가
- StationOverlay: center.csv 중심선 빨간 선분 시각화 (Python 방식), Catmull-Rom next6 스플라인 제거
- 파라미터 패널: 위치 보정(offX/Y/Z), 센서 크기(senW/H) 슬라이더 추가
- DEFAULT_CAMERA_PARAMS = Python 기본값 (모든 오프셋 0, focal=24, sensor=36)
- 다음 단계 참고:
- off_x/y/z 슬라이더로 GPS 위치 오차 보정 필요
- swap_xy 옵션 미구현 (Python에는 있음, 필요시 추가)
### 단계 7: UI/UX 통합
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/components/ErrorBoundary.tsx — React 에러 바운더리 (오류 발생 시 복구 UI)
- client/src/components/HelpOverlay.tsx — 키보드 단축키 도움말 오버레이
- client/src/main.tsx — ErrorBoundary로 App 래핑
- client/src/hooks/useKeyboard.ts — ? 키 (Shift+Slash) → onToggleHelp 콜백 추가
- client/src/components/player/VideoPlayer.tsx — onToggleHelp prop 수신 및 useKeyboard에 전달
- client/src/App.tsx — showHelp 상태 + HelpOverlay 렌더링 통합
- README.md — 프로젝트 설치/실행/단축키/환경변수 문서 작성
- TypeScript 타입 에러 없음, 프로덕션 빌드 성공 (1,480 kB / gzip 453 kB)
- 미완료/이슈:
- 빌드 경고: chunk > 500kB (video.js 특성상 정상)
- 다음 단계 참고: 전체 7단계 완료
### 기능 확장: 구조물 시설종별(1종/2종/3종) 표시 필터 (2026-06-19)
- 상태: ✅ 완료
- 목표: 구조물 시설종별에 따라 재생바(StationBar)/패널(RoutePanel) 표시 필터. 플레이어에 1/2/3종 체크박스, 기본 1종☑2종☑3종☐, localStorage 지속.
- 완료 항목:
- client/src/types/geo.ts: RouteStructure 에 grade?: string 추가(시설종별 원문).
- client/src/utils/geoData.ts (parseStructures): pushFrom spec 에 gradeFallback 추가. 교량 시설종별 idx1, 터널 idx5 를 헤더명('시설종별') indexOf 우선 + 폴백으로 읽어 trim, 빈 값이면 undefined. 구교(06)는 gradeFallback 미지정 → grade undefined.
- client/src/store/settingsStore.ts (신설): zustand + persist('ghivideo.settings'). gradeFilter 기본 {1종:true,2종:true,3종:false}, setGradeFilter 액션. KNOWN_GRADES, isGradeVisible(공유 표시규칙) export. merge 로 누락 키 기본값 보충.
- 표시규칙 isGradeVisible: grade 없음 || KNOWN 미포함('기타'/'인상' 등) → 항상 표시, 1/2/3종은 gradeFilter[grade]===true 일 때만.
- client/src/stationbar/StationBar.tsx: storeStructuresRaw 를 isGradeVisible 로 필터(useMemo, gradeFilter 구독) → structureMarks 가 필터된 목록 사용.
- client/src/components/overlay/RoutePanel.tsx: filteredPois 에 isGradeVisible 조건 추가(동일 규칙, gradeFilter 구독).
- client/src/components/player/VideoPlayer.tsx: 좌하단 컨트롤 그룹(배속 행 인근)에 "시설등급" 라벨 + 1/2/3종 체크박스 3개. showVideoControls && geoLoaded 일 때 노출. setGradeFilter 바인딩(즉시 토글).
- 실데이터 검증(하행)회덕-대전조차장 v2.0, 좌표필터 적용 후):
- 교량 12건: 시설종별 3종 ×10, 기타 ×2 ("인상" 1건은 깨진 좌표로 이미 필터됨)
- 터널 2건: 시설종별 2종 ×2
- 구교 9건: 시설종별 컬럼 없음 → grade undefined(항상 표시)
- 기본값(1종☑2종☑3종☐) 적용 시: 3종 교량 10건 숨김, 기타 교량 2건·터널 2건(2종)·구교 9건 표시.
- 빌드: nvm use 20 → npm run build -w client → tsc + vite 에러 0 (기존 chunk>500kB 경고만).
- 미완료/후속:
- 이 데이터엔 1종 구조물이 없어 1종 체크박스 효과는 다른 데이터에서 검증 필요(구조는 완성).
- StationOverlay(영상 위 측점 마커)는 structures 미사용이라 필터 미적용 — 요구범위(재생바/패널)대로.
- 상세 기록: docs/history/2026-06-19_시설종별-필터.md
### 기능 전환: 폴더 임포트 v2.0 데이터 형상 전용화 (2026-06-19)
- 상태: ✅ 완료
- 목표: 폴더 선택 시 v2.0 형상만 로딩. v1.0(POI_위경도값/측점_위경도값/center.csv) 지원 코드 제거.
- 완료 항목:
- client/src/types/geo.ts:
- DirectionChange 타입 신설(station/from/to/atSeconds)
- RouteStructure 에 lengthM?, category? 추가
- FolderGeoData 에 structures: RouteStructure[], directionChanges: DirectionChange[] 추가
- client/src/utils/geoData.ts (전면 재작성, v2.0 전용):
- 인코딩 자동 감지: decodeBytes(buf) — 앞 3바이트 EF BB BF → UTF-8, 아니면 EUC-KR, 잔여 BOM 제거
- readCsv(file) 시그니처 단순화(인코딩 인자 제거)
- parseStations: building/01)측점.csv → 측점 + 방향전환점 추출(비고 정규식)
- parsePois: <base>_POI.csv(UTF-8) + building/02)지장물.csv(EUC-KR) 병합
- parseStructures: 03)교량(bridge)+04)터널(tunnel)+06)구교(bridge/category='구교') → RouteStructure[], 위경도 범위(33~39/124~132) 밖 깨진 행 필터
- mergeStructures: route.json structures 를 name 매칭으로 CSV 구조물에 override/augment(선택적 보정 레이어)
- buildCenterlineFromStations: v2.0엔 center.csv 없음 → 측점 mileage 정렬 폴리라인을 중심선으로 생성
- v1.0 코드 제거: POI_위경도값/측점_위경도값/타원체고 식별, center.csv 로더, encoding 하드코딩 분기
- client/src/store/geoStore.ts: structures, directionChanges 적재
- client/src/stationbar/StationBar.tsx: 구조물 마커를 geoStore.structures(CSV 유래) 우선으로 전환(기존 routeMeta.structures 대체). 위치 로직(station→lat/lon→이름매칭→mileage)은 lat/lon 지원으로 그대로 활용.
- 실데이터 검증(하행)회덕-대전조차장 데이터(v2.0)):
- 인코딩: 드론/POI/05)출입문/06)구교=UTF-8(BOM), 01~04=EUC-KR (BOM 자동감지로 일괄 처리)
- directionChanges 2건: 157K930 상행→하행@125s, 158K340 하행→상행@75s
- 측점 47개, POI 137개(<base>_POI 114 + 지장물 23), 구조물 교량 12(깨진 1행 필터)+터널 2+구교 9
- 빌드: nvm use 20 → npm run build -w client → tsc + vite 에러 0 (기존 chunk>500kB 경고만)
- 미완료/후속과제:
- 05)출입문번호.csv: 운영데이터로 재생바 아이콘이 과도해져 기본 표시 제외. 파싱 자체를 스킵(type='door' 보관 비용 대비 효용 낮음, Timeline 렌더는 bridge/tunnel/station만). 추후 필요 시 parseDoors 추가.
- directionChanges 와이어링: 타입/스토어까지 적재 완료. StationBar barSegments 방향전환은 현재 연속 체이니지(chain) 자동 판정 로직을 유지(directionChanges 로 강제 분할은 미적용). chain 자동판정이 영상과 일치하므로 회귀 위험을 피해 후속과제로 둠.
- 다음 단계 참고:
- geoStore.structures/directionChanges 가 신규 노출됨 — 후속 소비처에서 활용 가능
- StationOverlay/StationVerify/RoutePanel/RouteInfoOverlay 는 stations/pois/routeMeta 만 사용하므로 변경 불필요(검증 완료)
- 상세 기록: docs/history/2026-06-19_v2.0-import전환.md