From 77c20bc3f19229642da942ed4bde3a3ace678052 Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:50:16 +0200 Subject: [PATCH] claude: webcam --- doc/01_WebcamRoadmap.md | 172 ++++++++++++++++-------------------- doc/03_Protocoll_roadmap.md | 100 +++++++++++++++++++++ docker-compose.yaml | 78 +++++++++++----- go2rtc.yaml | 27 +++--- package.json | 12 ++- public/index.html | 46 ++++------ public/viewer.js | 169 +++++++---------------------------- server.js | 76 +++++++--------- src/snapshotService.js | 31 +++---- 9 files changed, 346 insertions(+), 365 deletions(-) create mode 100644 doc/03_Protocoll_roadmap.md diff --git a/doc/01_WebcamRoadmap.md b/doc/01_WebcamRoadmap.md index 6cc2679..ea5a983 100644 --- a/doc/01_WebcamRoadmap.md +++ b/doc/01_WebcamRoadmap.md @@ -2,106 +2,89 @@ ## Ziel -Sauberer, fokussierter Webcam-Streaming-Service als Docker-Container. -Kein Robot-Control, kein ArUco – nur zwei Verantwortlichkeiten: +Sauberer, fokussierter Webcam-Service als Docker-Container. Kein Robot-Control, kein +ArUco – nur zwei Verantwortlichkeiten: -1. **Live-Video** mit minimaler Latenz (MJPEG über WebSocket) +1. **Live-Video** mit minimaler Latenz (WebRTC via go2rtc) 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. +Das Homing-Projekt holt seine Standbilder über den Snapshot-Endpunkt – keine weitere Kopplung. --- -## Architektur +## Architektur (final) ``` USB Kameras │ ▼ -FFmpeg (v4l2 → MJPEG) +go2rtc (Capture · H.264-Encode · WebRTC) ── intern, Port 1984 + UDP 8555 │ ▼ -Node.js Server (Express + ws) - ├── WebSocket /ws/cam0, /ws/cam1 → Browser (Live-Stream) - └── HTTP GET /api/snapshot/cam0 → Homing-Projekt (JPEG) +Node.js / Express ── öffentlich, Port 8444 + ├── / eigener Viewer (go2rtc -Component) + ├── /api/ws WebRTC-Signaling → proxied zu go2rtc + ├── /api/snapshot/* Standbilder → proxied zu go2rtc /api/frame.jpeg + └── /health ``` -**Stack-Entscheide (bereits umgesetzt):** +**Warum go2rtc statt eigenem FFmpeg-Stream:** +Erste Version (eigener Node-FFmpeg-MJPEG-Stream über WebSocket) hatte spürbare Latenz. +go2rtc ist ein spezialisierter Streaming-Server: WebRTC mit ~50–150 ms, automatisches +Encoding, ICE-Negotiation, robuster Client mit Auto-Fallback (WebRTC→MSE→MJPEG). + +**Warum Node davor (statt go2rtc direkt):** +- Ein einziger öffentlicher Port (8444); go2rtc-Admin bleibt unerreichbar +- Stabile Snapshot-Schnittstelle, entkoppelt von go2rtc-Interna +- Eigener, schlanker Viewer + +**Stack-Entscheide:** | 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 | +| Streaming | go2rtc | WebRTC out-of-the-box, niedrige Latenz, internet-tauglich | +| Webserver | Node.js + Express | wartbar, user-präferiert | +| Proxy | http-proxy-middleware | reicht HTTP + WebSocket transparent durch | +| Live-Protokoll | WebRTC (Fallback MSE/MJPEG) | niedrigste Latenz, skaliert über Internet | +| Snapshot-API | HTTP GET → JPEG | einfachste Schnittstelle für Consumer | +| Container | docker-compose, `configs` inline | kein Dockerfile-File, Portainer-tauglich | --- ## Tasks -### Phase 1 – Grundgerüst ✅ (erledigt) +### Phase 1 – Grundgerüst ✅ +- [x] Projektstruktur, package.json, docker-compose mit inline-Config +- [x] Erste Version (Node-FFmpeg-MJPEG über WebSocket) – verworfen wegen Latenz -- [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 – Umstieg auf go2rtc / WebRTC ✅ +- [x] go2rtc als Streaming-Backend (Kamera-Capture + WebRTC) +- [x] go2rtc-Config in docker-compose eingebettet (`configs.content`) +- [x] Node als Reverse-Proxy (`/api/ws`, `/api/frame.jpeg`, Player-Scripts) +- [x] Eigener Viewer mit go2rtc ``-Component (Auto-Fallback) +- [x] Stabile Snapshot-API `/api/snapshot/cam{n}` +- [x] Auflösung fest 640×480 → Latenz „akzeptabel" (war vorher das Hauptproblem) -### Phase 2 – Deployment & Latenz-Baseline +### Phase 3 – Latenz final tunen (offen) +- [ ] Messvergleich WebRTC ⟷ MJPEG durchführen → siehe `03_Protocoll_roadmap.md` +- [ ] Falls nötig: Auflösung 320×240 testen (kleiner = weniger Browser-Last) +- [ ] Falls nötig: Keyframe-Intervall senken (`-g 15`), zerolatency-Tuning +- [ ] Prüfen ob Kamera natives H.264 liefert (`v4l2-ctl --list-formats`) → kein Re-Encode -- [ ] **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 4 – Internet-Härtung (offen, vor Produktiv-Schaltung) +- [ ] **TLS**: Reverse Proxy (Caddy/nginx/traefik) mit HTTPS vor Port 8444 + (WebRTC im Browser läuft über Internet zuverlässig nur im secure context) +- [ ] **WebRTC-Candidate**: `stun:8555` testen; falls NAT-Probleme → feste public IP/Domain + in der go2rtc-Config eintragen (`candidates: [robot.example.com:8555]`) +- [ ] **TURN**: nur falls reines STUN + Port-Forward UDP 8555 nicht reicht → coturn +- [ ] **Zugriffsschutz**: Basic-Auth oder Token am Reverse Proxy (1–3 bekannte User) +- [ ] **Firewall**: TCP 8444 + UDP 8555 forwarden; Port 1984 NICHT exponieren -### 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` +### Phase 5 – Robustheit (optional) +- [ ] Kamera hot-plug: go2rtc-Verhalten bei Device-Verlust prüfen +- [ ] Resource Limits dokumentieren (`mem_limit`, `cpus`) +- [ ] JSON-Logging +- [ ] Snapshot-Metadaten / optionaler Webhook nach Snapshot --- @@ -109,26 +92,21 @@ Drei Optionen (noch offen): | Feature | appRobotVideoControls | appRobotWebcam | |---------|-----------------------|----------------| -| Video-Streaming | ✅ | ✅ (verbessert) | -| Snapshots | ✅ (komplex, dual-pipe) | ✅ (HTTP REST, einfach) | +| Video-Streaming | eigener FFmpeg-MJPEG/WS | go2rtc / WebRTC | +| 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) | +| ArUco / Homing | ✅ | ❌ anderes Projekt | +| Separates Dockerfile | ✅ (OpenCV-Build) | ❌ (inline in compose) | --- -## Ports & Netzwerk +## Ports -| Service | Container-Port | Host-Port | -|---------|---------------|-----------| -| HTTP + WS | 8080 | 8444 | - -```bash -# Netzwerk einmalig erstellen (falls noch nicht vorhanden) -docker network create appRobotNet -``` +| Dienst | Port | Exponiert? | +|--------|------|-----------| +| Node Viewer + API + Signaling | TCP 8444 | ja (Firewall) | +| WebRTC Media | UDP 8555 | ja (Firewall) | +| go2rtc HTTP/Debug-UI | TCP 1984 | nein (nur intern/LAN) | --- @@ -137,16 +115,16 @@ docker network create appRobotNet ``` 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 +│ └── snapshotService.js Snapshot-Router (proxied go2rtc /api/frame.jpeg) ├── public/ -│ ├── index.html Basis-Viewer -│ └── viewer.js WebSocket-Client, MJPEG-Rendering +│ ├── index.html Viewer (lädt go2rtc ) +│ └── viewer.js baut Kamera-Views, Auto-Fallback WebRTC→MSE→MJPEG ├── doc/ -│ ├── 01_WebcamRoadmap.md (diese Datei) -│ └── 05_OptionalToDo_roadmap.md Control-Optionen -├── docker-compose.yaml Einzige Docker-Datei (dockerfile_inline) +│ ├── 01_WebcamRoadmap.md (diese Datei) +│ ├── 03_Protocoll_roadmap.md WebRTC⟷MJPEG-Vergleich (nachzuholen) +│ └── 05_OptionalToDo_roadmap.md Control-Integration (Optionen) +├── docker-compose.yaml einzige Deploy-Datei (go2rtc-Config eingebettet) +├── go2rtc.yaml nur Referenz/lokal (Config ist in compose eingebettet) ├── package.json -└── server.js Einstiegspunkt +└── server.js Node-Einstiegspunkt ``` diff --git a/doc/03_Protocoll_roadmap.md b/doc/03_Protocoll_roadmap.md new file mode 100644 index 0000000..dac9044 --- /dev/null +++ b/doc/03_Protocoll_roadmap.md @@ -0,0 +1,100 @@ +# AppRobotWebcam – Protokoll-Vergleich WebRTC ⟷ MJPEG + +## Status + +- **Auflösung fest auf 640×480** → Latenz ist jetzt **„immer akzeptabel"** (vorher schwankend/träge). + Das bestätigt: ein Teil der gefühlten Latenz kam von zu großen Frames im Browser + (Decode + Render). Kleineres Bild = schnellerer Browser. +- **Entscheid vorläufig: WebRTC** – skaliert besser über echtes Internet (NAT, mehrere User). +- go2rtc bleibt die Basis → **http://thinkcentre.local:1984** bleibt jederzeit als + Vergleichs- und Debug-Oberfläche verfügbar. + +> Der direkte Messvergleich WebRTC ⟷ MJPEG steht noch aus (Zeitgründen). +> Diese Datei hält fest, **wie** man ihn nachholt. + +--- + +## Warum überhaupt vergleichen? + +| Protokoll | Latenz (LAN, 480p) | Mechanik | Bandbreite | +|-----------|--------------------|----------|------------| +| **MJPEG** | am niedrigsten | Jedes Frame ein eigenständiges JPEG, kein Buffer, `` rendert sofort | hoch (jedes Frame voll) | +| **WebRTC** | niedrig | H.264-Encode + Jitter-Buffer im Browser; dafür effiziente Kompression | niedrig | +| **MSE** | mittel–hoch | Für VOD optimiert, größerer Puffer | niedrig | + +- **MJPEG** = theoretische Latenz-Untergrenze, aber bei mehr Usern / über Internet + bandbreitenhungrig und ohne NAT-Traversal. +- **WebRTC** = minimal mehr Latenz durch Encode/Buffer, dafür internet-tauglich + (NAT-Traversal via STUN/TURN, geringe Bandbreite, mehrere User). + +Für **1–3 User im LAN** kann MJPEG gewinnen. Über **Internet** gewinnt WebRTC fast immer. + +--- + +## So führst du den Vergleich durch + +go2rtc stellt jeden Stream über **mehrere** Protokolle bereit. Jeweils einzeln und +direkt im Browser öffnen (für cam1: `cam0` → `cam1` ersetzen): + +| Modus | URL | Was es testet | +|-------|-----|---------------| +| **MJPEG roh** | `http://thinkcentre.local:1984/api/stream.mjpeg?src=cam0` | Untergrenze: reines Bild, kein Player, kein Buffer | +| **WebRTC pur** | `http://thinkcentre.local:1984/webrtc.html?src=cam0` | WebRTC isoliert | +| **MSE pur** | `http://thinkcentre.local:1984/mse.html?src=cam0` | MSE isoliert (Referenz) | +| **Alle Links** | `http://thinkcentre.local:1984/links.html?src=cam0` | go2rtc listet selbst alle Endpunkte auf | + +> Hinweis zur go2rtc-Startseite: Die Checkboxen (WebRTC/MSE/MJPEG) oben filtern nur, +> welche Technologien der **kombinierte** Player `stream.html` ausprobieren darf – +> sichtbar ändert sich dabei nichts, weil er automatisch die erste passende nimmt. +> Für einen echten Vergleich **die obigen Einzel-URLs** verwenden. + +### Latenz messen (objektiv, in ms) + +1. Handy-Stoppuhr mit Millisekunden-Anzeige vor die Kamera halten. +2. MJPEG-URL und WebRTC-URL in zwei Tabs/Fenstern nebeneinander öffnen. +3. Einen Screenshot machen, der **die echte Stoppuhr** und **beide Stream-Bilder** + gleichzeitig zeigt (Handy + Monitor zusammen abfotografieren ist am einfachsten). +4. Differenz „echte Zeit ↔ Bild im Stream" ablesen = Gesamt-Latenz pro Protokoll. + +### Ergebnis-Tabelle (später ausfüllen) + +| Kamera | MJPEG roh | WebRTC | MSE | Sieger | +|--------|-----------|--------|-----|--------| +| cam0 | ? ms | ? ms | ? ms | ? | +| cam1 | ? ms | ? ms | ? ms | ? | + +--- + +## Entscheidungslogik nach dem Test + +- **WebRTC ≈ MJPEG (Differenz < ~50 ms):** + → Bei **WebRTC** bleiben. Vorteil Internet-Skalierung überwiegt die paar ms. + +- **MJPEG deutlich schneller (Differenz > ~100 ms) UND nur LAN-Nutzung:** + → Optional **MJPEG-Viewer** zusätzlich anbieten (simple ``-Seite). + go2rtc liefert MJPEG ohnehin schon unter `/api/stream.mjpeg?src=camN`. + +- **Auflösung weiter drücken:** + → In `docker-compose.yaml` unter `configs:` `video_size=640x480` → `320x240`. + Test wiederholen. Kleiner = weniger Browser-Last = weniger Latenz. + +--- + +## Weitere Latenz-Stellschrauben (falls WebRTC noch zu träge) + +1. **Keyframe-Intervall senken** – H.264 startet erst beim nächsten Keyframe. + In der go2rtc-Quelle den Encoder mit `-g 15` (Keyframe alle 0.5 s @30fps) zwingen. +2. **Kamera-natives H.264** – falls die Webcam H.264 direkt liefert (UVC H.264), + kann go2rtc ohne Re-Encode durchreichen → minimale CPU + Latenz. + Prüfen mit: `v4l2-ctl -d /dev/video0 --list-formats` +3. **`zerolatency`-Tuning** im Encoder (ultrafast + tune zerolatency). +4. **WebRTC statt über Proxy direkt** – Jitter-Buffer des Browsers ist Fixkosten, + lässt sich nur begrenzt beeinflussen. + +--- + +## Wichtig: go2rtc bleibt erhalten + +Da der finale Aufbau weiter auf go2rtc setzt, bleibt **http://thinkcentre.local:1984** +dauerhaft als Debug-/Vergleichs-UI nutzbar. Der Protokoll-Vergleich kann also +**jederzeit später** nachgeholt werden, ohne etwas umzubauen. diff --git a/docker-compose.yaml b/docker-compose.yaml index eb12647..e08fa09 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,43 +1,65 @@ name: approbotwebcam -# ── Portainer Web-Editor: dieses YAML einfügen, dann Deploy ───────────────── +# ════════════════════════════════════════════════════════════════════════════ +# FINALER WebRTC-AUFBAU – go2rtc (Streaming) + Node.js (Viewer/Proxy/API) +# ════════════════════════════════════════════════════════════════════════════ # -# Voraussetzungen: -# 1. Code auf dem Server (git clone / Synology Drive sync) -# 2. go2rtc.yaml muss im selben Verzeichnis liegen wie dieses File -# 3. In Portainer → "Environment variables": -# APP_PATH = /absoluter/pfad/zum/appRobotWebcam +# Portainer: Stack → Web editor → dieses YAML einfügen → Deploy. +# Vorher in Portainer → "Environment variables": +# APP_PATH = /absoluter/pfad/zum/appRobotWebcam (Code muss dort liegen) # -# Firewall: genau zwei Ports freigeben: -# TCP 8444 → HTTP (Viewer · Snapshot-API · WebRTC-Signaling) -# UDP 8555 → WebRTC Media (go2rtc direkt, kann nicht proxiert werden) +# WICHTIG: Vor jedem Redeploy sicherstellen, dass server.js / public/ / src/ +# auf dem Server unter APP_PATH aktuell sind (Synology-Sync abwarten). # -# network_mode: host → beide Container teilen den Host-Netzwerk-Stack. -# Das ist für WebRTC entscheidend: go2rtc bekommt die echte Host-IP als -# ICE-Kandidat, nicht eine Docker-interne 172.x-Adresse. -# ───────────────────────────────────────────────────────────────────────────── +# Firewall (Internet): TCP 8444 (Viewer+API+Signaling) · UDP 8555 (WebRTC-Media) +# Port 1984 (go2rtc) NICHT nach aussen – läuft nur intern via localhost. +# +# Zugriff: +# Viewer: http://:8444/ +# Snapshot (Homing) http://:8444/api/snapshot/cam0 +# go2rtc-Debug-UI http://:1984/ (nur intern/LAN) +# ════════════════════════════════════════════════════════════════════════════ + +configs: + go2rtc_yaml: + # Komplette go2rtc-Config eingebettet – keine separate Datei nötig. + content: | + streams: + cam0: "ffmpeg:device?video=/dev/video0&video_size=640x480#video=h264#video=mjpeg" + cam1: "ffmpeg:device?video=/dev/video2&video_size=640x480#video=h264#video=mjpeg" + webrtc: + listen: ":8555" + candidates: + # stun:8555 → go2rtc erkennt die öffentliche IP automatisch (für Internet). + # Falls das nicht klappt: feste IP/Domain eintragen, z.B. + # - robot.example.com:8555 + - stun:8555 + api: + listen: ":1984" + log: + level: info services: - # ── go2rtc: Kamera-Capture + H.264-Encoding + WebRTC ────────────────────── + # ── go2rtc: Kamera-Capture · H.264-Encoding · WebRTC ────────────────────── go2rtc: image: ghcr.io/alexxit/go2rtc container_name: AppRobotGo2RTC restart: unless-stopped - network_mode: host + network_mode: host # echte Host-IP als WebRTC-ICE-Kandidat devices: - /dev/video0:/dev/video0 - /dev/video2:/dev/video2 group_add: - video - volumes: - # go2rtc.yaml liegt im selben Verzeichnis wie docker-compose.yaml - - ${APP_PATH:-.}/go2rtc.yaml:/config/go2rtc.yaml:ro + configs: + - source: go2rtc_yaml + target: /config/go2rtc.yaml - # ── webcam: Node.js (Viewer · Snapshot-Proxy · WebRTC-Signaling-Proxy) ─── + # ── webcam: Node.js (Viewer · /api/ws-Proxy · Snapshot-API) ────────────── webcam: build: - context: /tmp # Leerer Build-Context – Code kommt per Bind-Mount + context: /tmp # Leerer Build-Context – Code kommt per Bind-Mount dockerfile_inline: | FROM node:lts-bookworm-slim WORKDIR /usr/src/app @@ -45,11 +67,23 @@ services: image: approbotwebcam:latest container_name: AppRobotWebcam restart: unless-stopped - network_mode: host - command: sh -c "npm install && node server.js" + network_mode: host # erreicht go2rtc via localhost:1984 + command: sh -c "npm install --omit=dev && node server.js" volumes: - ${APP_PATH:-.}:/usr/src/app environment: - NODE_ENV=production - PORT=8444 - GO2RTC_URL=http://localhost:1984 + depends_on: + - go2rtc + +# ── FALLBACK ────────────────────────────────────────────────────────────────── +# Meckert Portainer beim Deploy über "configs content" (sehr alte Compose-Version)? +# → den configs-Block oben löschen und stattdessen beim go2rtc-Service mounten: +# volumes: +# - ${APP_PATH:-.}/go2rtc.yaml:/config/go2rtc.yaml:ro +# +# Bleibt eine Kamera schwarz? → in der Config oben die Quelle ersetzen durch die +# simple, bestätigte Form (ohne Auflösung): "ffmpeg:/dev/video0#video=h264#video=mjpeg" +# ──────────────────────────────────────────────────────────────────────────────── diff --git a/go2rtc.yaml b/go2rtc.yaml index e9b5e0c..8d8d732 100644 --- a/go2rtc.yaml +++ b/go2rtc.yaml @@ -1,23 +1,22 @@ +# Hinweis: Diese Datei wird für das Portainer-Deployment NICHT mehr gebraucht – +# die Config ist jetzt direkt in docker-compose.yaml eingebettet (configs.content). +# Sie bleibt hier nur als Referenz / für lokales go2rtc ohne Compose. + streams: - # FFmpeg öffnet die v4l2-Kamera und encodiert zu H.264 für WebRTC - # Falls die Kamera kein MJPEG liefert: "#video=h264" durch "#video=mjpeg" oder "#video=vp8" ersetzen - cam0: - - "ffmpeg:/dev/video0#video=h264" - cam1: - - "ffmpeg:/dev/video2#video=h264" + # device?-Form: go2rtc öffnet die v4l2-Kamera mit fixer Auflösung. + # #video=h264 → für WebRTC (transcodiert) + # #video=mjpeg → für MJPEG-Endpoint (Passthrough, niedrigste Latenz) + cam0: "ffmpeg:device?video=/dev/video0&video_size=640x480#video=h264#video=mjpeg" + cam1: "ffmpeg:device?video=/dev/video2&video_size=640x480#video=h264#video=mjpeg" + + # Simple Fallback-Form (ohne Auflösungs-Vorgabe), falls device? Probleme macht: + # cam0: "ffmpeg:/dev/video0#video=h264#video=mjpeg" webrtc: - ice_servers: - - urls: - - stun:stun.l.google.com:19302 - - stun:stun1.l.google.com:19302 - # Fixer UDP-Port → einfache Firewall-Regel: UDP 8555 weiterleiten - listen: ":8555/udp" + listen: ":8555" api: listen: ":1984" - # Erlaubt Requests vom Node.js-Proxy (gleicher Host, anderer Port) - origin: "*" log: level: info diff --git a/package.json b/package.json index e6fdfac..5986de0 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,15 @@ { "name": "approbotwebcam", - "version": "0.1.0", - "description": "Low-latency webcam streaming service for robot vision", + "version": "0.2.0", + "description": "Low-latency WebRTC webcam service (go2rtc + Node proxy) for robot vision", "main": "server.js", "scripts": { "start": "node server.js", - "dev": "nodemon server.js" + "dev": "node server.js" }, "dependencies": { - "express": "^4.21.1" - }, - "devDependencies": { - "nodemon": "^3.1.7" + "express": "^4.21.1", + "http-proxy-middleware": "^3.0.3" }, "engines": { "node": ">=20" diff --git a/public/index.html b/public/index.html index 20b8834..6c674b0 100644 --- a/public/index.html +++ b/public/index.html @@ -6,51 +6,32 @@ AppRobotWebcam