Files
appRobotWebcam/doc/01_WebcamRoadmap.md

5.7 KiB
Raw Blame History

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)

  • Projektstruktur anlegen
  • package.json mit Abhängigkeiten (express, ws)
  • docker-compose.yaml mit dockerfile_inline (Node.js + FFmpeg, kein separates Dockerfile)
  • server.js HTTP + WebSocket-Server, Graceful Shutdown
  • src/deviceDetect.js Kamera-Erkennung (env → by-id → /dev/video*)
  • src/videoStream.js FFmpegStreamer (MJPEG splitten, WebSocket broadcast, Auto-Restart mit Backoff)
  • src/snapshotService.js REST-Endpunkt: aktuellstes Frame aus laufendem Stream
  • 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
# 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