Initial commit — Alta Video Player (WebAVP)
Web-based surveillance video player for Alta/Ava Security camera exports with multi-camera sync, timeline, digital zoom, motion analytics, and cryptographic integrity verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Alta Video Player (WebAVP)** — a web-based surveillance video player for Alta/Ava Security camera exports. Users drag-drop video files or ZIP archives (including AES-256 encrypted ones) and get a multi-camera synchronized playback experience with timeline, digital zoom, cryptographic integrity verification, and automatic motion analytics.
|
||||
|
||||
## Running the App
|
||||
|
||||
```bash
|
||||
python3 app.py
|
||||
# Serves on http://0.0.0.0:5152
|
||||
```
|
||||
|
||||
No build step. No dependencies needed at runtime — `app.py` uses only Python stdlib (`http.server`). The `requirements.txt` (flask, requests, gunicorn) is vestigial; the server was rewritten to pure stdlib.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Backend (`app.py`)
|
||||
|
||||
Minimal HTTP server with three routes:
|
||||
- `GET /` — serves `templates/index.html`
|
||||
- `GET /static/*` — serves static files with path traversal protection
|
||||
- `GET /api/verify-cert?serial=...&certificateHash=...` — proxies certificate verification to `https://aware.avasecurity.com/api/v1/public/verifyServerCertificate`
|
||||
|
||||
### Frontend (`templates/index.html`)
|
||||
|
||||
Single self-contained HTML file (~5000 lines) with inline CSS and JavaScript in an IIFE. This is the entire application — there is no framework, no build system, no separate JS modules.
|
||||
|
||||
**State model:** Global `channels` Map keyed by channel name. Each channel holds segments (video blobs + time ranges), metadata, DOM references, color, and zoom state. Global timeline state (`globalStart`, `globalEnd`, `currentTime`) synchronizes all cameras. Motion analytics state lives in the `motionState` object.
|
||||
|
||||
**Key subsystems:**
|
||||
- **File ingestion** — drag-drop files/folders, ZIP extraction (JSZip for plain, custom AES-256-CTR for encrypted), metadata pairing by base filename, concurrent import guard (`isImporting` flag)
|
||||
- **Multi-camera grid** — responsive CSS grid (1–9 cameras), drag-to-reorder, click-to-expand
|
||||
- **Playback engine** — `requestAnimationFrame` tick loop, per-channel segment visibility management, variable speed (0.25x–8x), frame stepping
|
||||
- **Timeline** — interactive scrub bar with zoom (mouse wheel), minimap, per-channel segment indicators with color-coded dots, motion heatmap row
|
||||
- **Digital zoom** — per-camera scroll-to-zoom (up to 10x) with click-drag panning
|
||||
- **Magnifier tool** — draw rectangle to zoom into region
|
||||
- **Slideshow mode** — animated grid showing only currently active feeds with transitions
|
||||
- **Motion analytics** — automatic background motion detection on load (see below)
|
||||
- **Integrity verification** — offline X.509 certificate parsing, RSASSA-PKCS1-v1_5 and ECDSA signature verification via Web Crypto API, optional cloud verification through `/api/verify-cert`
|
||||
- **Session persistence** — IndexedDB caching of video blobs and metadata for page refresh survival
|
||||
|
||||
**External dependency:** `/static/jszip.min.js` (vendored, for unencrypted ZIP parsing).
|
||||
|
||||
## Motion Analytics Subsystem
|
||||
|
||||
Zero-dependency canvas-based motion detection that runs automatically when videos are loaded.
|
||||
|
||||
### How it works
|
||||
1. **Auto-scan on load** — `scheduleAutoScan()` is called from `flushPending()` and `loadSessionData()` after videos finish loading. Debounced 800ms to batch all segments.
|
||||
2. **Dedicated scan videos** — scan creates temporary offscreen `<video>` elements per segment (with `preload='auto'` for fast seeking) that don't interfere with the playback engine. Each is destroyed after its segment is processed.
|
||||
3. **Pixel-diff at 160x120** — frames are drawn to a small offscreen canvas. Each pixel's RGB delta is compared against a threshold. Changed pixels are counted and mapped to a 10x8 hotspot grid.
|
||||
4. **Sensitivity slider (1–100)** — maps to two internal thresholds via `sensitivityToThresholds()`: `pixelThreshold` (how different a pixel must be) and `changeThreshold` (what % of pixels must change). Presets: Indoor (30), Default (40), Parking (45), Outdoor (55).
|
||||
5. **Motion clusters** — consecutive motion detections within `clusterGap` (5s) are merged into clusters with start/end times, peak change %, and hotspot data.
|
||||
6. **Results visualization** — motion heatmap row on timeline (purple/red intensity), clickable cluster markers, scrollable event cards in the analytics panel with hotspot mini-grids.
|
||||
|
||||
### Key state: `motionState`
|
||||
- `sensitivity` (1–100), `scanInterval` (2s default), `clusterGap` (5s)
|
||||
- `detector.canvas` / `detector.ctx` — 160x120 offscreen canvas for pixel comparison
|
||||
- `detector.prevFrames` — Map of channelName → previous ImageData
|
||||
- `motionProfiles` — Map of channelName → array of `{ time, changePercent, hotspots, hasMotion }`
|
||||
- `motionClusters` — sorted array of `{ startTime, endTime, channelName, peakChange, ... }`
|
||||
- `isScanning` / `scanAbort` — scan lifecycle
|
||||
- `isMonitoring` — live monitor mode flag
|
||||
|
||||
### Key functions
|
||||
- `detectMotion(videoEl, channelName)` — core pixel-diff, returns `{ hasMotion, changePercent, hotspots }`
|
||||
- `scanTimeline({ auto })` — full scan orchestrator, creates offscreen videos, yields to UI every 12 frames
|
||||
- `scheduleAutoScan()` — debounced auto-trigger after video load
|
||||
- `setScanUIState('scanning'|'idle')` — manages rail button grey-out and progress UI
|
||||
- `seekToMotion(direction)` — skip to next/previous motion cluster
|
||||
- `monitorTick(absTime)` — called from `tick()` during playback for live detection
|
||||
- `buildMotionClusters()` — post-scan cluster identification
|
||||
- `renderMotionResults()` — populates panel: sparkline, summary, event cards
|
||||
|
||||
### UI elements
|
||||
- **Analytics panel** — right slide-out (400px), class `.analytics-panel`, toggled via `railAnalyticsBtn`
|
||||
- **Rail button** — greyed out with pulsing purple border (`.scanning` class) during auto-scan
|
||||
- **Skip-to-motion buttons** — `btnPrevMotion` / `btnNextMotion` in transport controls
|
||||
- **Keyboard shortcuts** — Shift+N (next motion), Shift+P (previous motion)
|
||||
|
||||
### Two-tier distribution strategy
|
||||
The browser version provides motion detection only (zero dependencies, fully offline). A future downloadable local app will add:
|
||||
- Object classification via bundled ONNX/YOLO model (person/vehicle/animal detection)
|
||||
- BYOA (Bring Your Own Agent) cloud AI integration for scene understanding, license plates, cross-camera tracking
|
||||
- Optional local LLM support (Ollama) for fully offline AI reasoning
|
||||
|
||||
## Memory Management
|
||||
|
||||
Video elements must be properly destroyed to avoid browser memory exhaustion:
|
||||
- **`destroyVideoEl(videoEl)`** — pauses video, removes `src`, calls `.load()` to force browser to release buffered data. Must be called before removing video elements from DOM.
|
||||
- **`video.preload = 'metadata'`** — all playback videos use metadata-only preloading to avoid buffering entire files into RAM. Scan videos use `'auto'` temporarily and are destroyed after use.
|
||||
- **`newSession()`** — comprehensive teardown: stops scan/monitor, destroys all video elements, revokes all blob URLs, nulls blob references, releases WebGL contexts, clears all state.
|
||||
- **`isImporting` guard** — prevents concurrent file imports which could cause race conditions and duplicate segments.
|
||||
- Slideshow video elements are destroyed on pane transitions and when slideshow is toggled off.
|
||||
|
||||
## Key Conventions
|
||||
|
||||
- All frontend code lives in `templates/index.html` — CSS at top, then the IIFE script block
|
||||
- Video elements are created on-demand and hidden (not removed) for performance
|
||||
- Segment visibility is recalculated every animation frame during playback
|
||||
- The `batchingSegments` flag defers rendering during bulk file imports
|
||||
- Keyboard shortcuts are defined inline (Space=play/pause, arrows=seek, S=slideshow, M=magnifier, F=fullscreen, [/]=speed, 0=reset zoom, Shift+N/P=skip motion)
|
||||
- Motion scan runs automatically on load — the analytics rail button is disabled until scan completes
|
||||
- Right-side panels (log, analytics) auto-close when the other opens
|
||||
Reference in New Issue
Block a user