Files
appRobotWebcam/doc/01_WebcamRoadmap.md

153 lines
5.7 KiB
Markdown
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.
# 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
```