# AppRobotWebcam – Roadmap ## Ziel Sauberer, fokussierter Webcam-Streaming-Service als Docker-Container. Kein Robot-Control, kein ArUco – nur zwei Verantwortlichkeiten: 1. **Live-Video** mit minimaler Latenz (MJPEG über WebSocket) 2. **Standbilder** auf Abruf via HTTP REST (`/api/snapshot/cam{n}`) Das Homing-Projekt holt seine Standbilder über den Snapshot-Endpunkt – keine weitere Kopplung nötig. --- ## Architektur ``` USB Kameras │ ▼ FFmpeg (v4l2 → MJPEG) │ ▼ Node.js Server (Express + ws) ├── WebSocket /ws/cam0, /ws/cam1 → Browser (Live-Stream) └── HTTP GET /api/snapshot/cam0 → Homing-Projekt (JPEG) ``` **Stack-Entscheide (bereits umgesetzt):** | Komponente | Wahl | Begründung | |------------|------|------------| | Webserver | Node.js + Express | Wartbar, grosses Ecosystem, user-präferiert | | WebSocket | `ws`-Library | Schlank, bewährt, kein Overhead | | Video-Capture | FFmpeg | Stabil, flexibel, MJPEG-passthrough möglich | | Stream-Protokoll | MJPEG über WebSocket | Geringste Latenz, einfach im Browser | | Snapshot-API | HTTP GET → raw JPEG | Einfachste Schnittstelle für Consumer | | Container | `dockerfile_inline` in docker-compose | Kein separates Dockerfile, Portainer-tauglich | --- ## Tasks ### Phase 1 – Grundgerüst ✅ (erledigt) - [x] Projektstruktur anlegen - [x] `package.json` mit Abhängigkeiten (express, ws) - [x] `docker-compose.yaml` mit `dockerfile_inline` (Node.js + FFmpeg, kein separates Dockerfile) - [x] `server.js` – HTTP + WebSocket-Server, Graceful Shutdown - [x] `src/deviceDetect.js` – Kamera-Erkennung (env → by-id → /dev/video*) - [x] `src/videoStream.js` – FFmpegStreamer (MJPEG splitten, WebSocket broadcast, Auto-Restart mit Backoff) - [x] `src/snapshotService.js` – REST-Endpunkt: aktuellstes Frame aus laufendem Stream - [x] `public/index.html` + `public/viewer.js` – Basis-Viewer ### Phase 2 – Deployment & Latenz-Baseline - [ ] **Kamera-Zugriff im Container** verifizieren: - `/dev/video0` und `/dev/video2` im Container sichtbar - `group_add: video` greift (Zugriffsrechte) - Fallback auf YUYV422 wenn MJPEG nicht unterstützt - [ ] **Latenz messen** (Baseline): - Uhr auf Kamera richten, Screenshot → Differenz ablesen - Zielwert: <100 ms Ende-zu-Ende (Kamera → Browser-Pixel) - [ ] **Multi-Format-Fallback** implementieren: MJPEG → YUYV422 → RGB24 - [ ] **Health-Endpunkt** `/health` erweitern: Kamera-Status, verbundene Clients, FPS ### Phase 3 – Latenz optimieren - [ ] **FFmpeg-Flags tunen** für minimale Latenz: ``` -fflags nobuffer -flags low_delay -probesize 32 -analyzeduration 0 ``` - [ ] **Native MJPEG pass-through** testen: Wenn Kamera MJPEG nativ bei Zielauflösung liefert → `-vcodec copy` (kein Re-Encoding, minimale CPU-Last, minimale Latenz) - [ ] **Canvas-Rendering** im Browser: `createImageBitmap()` statt Blob-URL-Overhead - [ ] **WebRTC evaluieren**: <50 ms möglich, aber STUN/TURN-Komplexität in Docker – sinnvoll erst wenn MJPEG-Latenz >150 ms bleibt ### Phase 4 – Hochauflösende Snapshots **Aktuell**: Snapshot = letztes Frame aus dem Stream (Auflösung = Stream-Auflösung). **Ziel**: Snapshot in originaler Kamera-Auflösung (z.B. 1280×960). Drei Optionen (noch offen): | Option | Vorgehen | Pro | Contra | |--------|----------|-----|--------| | A | Stream-Frame direkt nehmen | Sofort, kein Aufwand | Auflösung = Stream-Auflösung | | B | Zweite FFmpeg-Pipeline 0.5 FPS High-Res | Immer verfügbar | CPU-Last, pipe:3 Komplexität | | C | Einmaliger `ffmpeg -frames:v 1` on-demand | Hohe Qualität | ~500 ms Delay, Stream-Unterbrechung | - [ ] Option wählen und implementieren - [ ] Mit Homing-Projekt testen (Consumer von `/api/snapshot`) - [ ] Snapshot-Metadaten in Response-Headern: Zeitstempel, Auflösung, Kamera-ID - [ ] Optionaler Webhook: POST nach Snapshot an konfigurierbaren Endpunkt ### Phase 5 – Robustheit & Produktion - [ ] Kamera hot-plug: Stream-Neustart wenn `/dev/videoX` verschwindet/wiederkommt - [ ] Resource Limits: `--memory 512m --cpus 1.0` in docker-compose - [ ] HTTPS: Reverse Proxy (nginx/traefik) vorschalten – kein TLS im App-Code - [ ] JSON-Logging mit Level (info/warn/error) - [ ] Kamera-Parameter per env var: `CAM0_WIDTH`, `CAM0_HEIGHT`, `CAM0_FPS`, `CAM0_QUALITY` --- ## Abgrenzung zu appRobotVideoControls | Feature | appRobotVideoControls | appRobotWebcam | |---------|-----------------------|----------------| | Video-Streaming | ✅ | ✅ (verbessert) | | Snapshots | ✅ (komplex, dual-pipe) | ✅ (HTTP REST, einfach) | | Robot-Control (G-Code) | ✅ | ❌ anderes Projekt | | ArUco / Homing | ✅ (Python+OpenCV) | ❌ anderes Projekt | | Gamepad / Keyboard | ✅ | ❌ | | HTTPS (self-signed) | ✅ | ❌ (Reverse Proxy empfohlen) | | Separates Dockerfile | ✅ (gross, OpenCV-Build) | ❌ (inline in compose) | --- ## Ports & Netzwerk | Service | Container-Port | Host-Port | |---------|---------------|-----------| | HTTP + WS | 8080 | 8444 | ```bash # Netzwerk einmalig erstellen (falls noch nicht vorhanden) docker network create appRobotNet ``` --- ## Datei-Struktur ``` appRobotWebcam/ ├── src/ │ ├── deviceDetect.js Kamera-Erkennung (env → by-id → /dev/video*) │ ├── videoStream.js FFmpeg-MJPEG-Streamer + WebSocket-Broadcast │ └── snapshotService.js REST-Router für /api/snapshot ├── public/ │ ├── index.html Basis-Viewer │ └── viewer.js WebSocket-Client, MJPEG-Rendering ├── doc/ │ ├── 01_WebcamRoadmap.md (diese Datei) │ └── 05_OptionalToDo_roadmap.md Control-Optionen ├── docker-compose.yaml Einzige Docker-Datei (dockerfile_inline) ├── package.json └── server.js Einstiegspunkt ```