name: approbotwebcam # ════════════════════════════════════════════════════════════════════════════ # NODE-MJPEG-SCHALTER – ein Node-Container besitzt die Kameras selbst # ════════════════════════════════════════════════════════════════════════════ # # Node startet pro Kamera EINEN FFmpeg (640 MJPEG passthrough) und verteilt den # Stream als multipart/x-mixed-replace an die Browser (). Für HD-Snapshots # stoppt der Schalter den Live-FFmpeg sauber (Prozess-close = Gerät frei), # greift 1280×960, schaltet zurück. go2rtc wird NICHT mehr gebraucht. # # Warum so: go2rtcs API konnte nicht zuverlässig melden, wann FFmpeg das Gerät # freigibt → Race (zwei Encoder auf /dev/videoN = ~108% CPU). Wenn Node FFmpeg # selbst startet, ist dessen 'close'-Event der harte Beweis „Gerät frei". # Siehe doc/09_Bug_reports.md. # # Portainer: Stack → Web editor → dieses YAML → Deploy. # APP_PATH = /absoluter/pfad/zum/appRobotWebcam (Code muss dort liegen) # # Firewall (Internet): TCP 8444 (Viewer + Stream + API). Sonst nichts mehr. # # Zugriff: # Viewer: http://:8444/ # Live-Stream: http://:8444/api/stream/cam0 # Snapshot (Homing): http://:8444/api/snapshot/cam0 (+ /hires) # # ROLLBACK auf den alten go2rtc-Aufbau: git checkout -- docker-compose.yaml # server.js public/ src/ (der go2rtc-Stand liegt in der Git-Historie). # ════════════════════════════════════════════════════════════════════════════ services: webcam: build: context: /tmp dockerfile_inline: | FROM node:lts-bookworm-slim RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg \ && rm -rf /var/lib/apt/lists/* WORKDIR /usr/src/app EXPOSE 8444 image: approbotwebcam:latest container_name: AppRobotWebcam restart: unless-stopped network_mode: host command: sh -c "npm install --omit=dev && node server.js" volumes: - ${APP_PATH:-.}:/usr/src/app devices: # Jede Kamera aus cameras.json muss hier aufgeführt sein. # Empfehlung: statt /dev/videoN → persistente by-id-Pfade verwenden # (ls -la /dev/v4l/by-id/ auf dem Server zeigt die Namen) - /dev/video0:/dev/video0 # C270 (046d:0825) → cam0 in cameras.json - /dev/video2:/dev/video2 # C270 (046d:081b) → cam1 in cameras.json - /dev/video4:/dev/video4 # C920 HD Pro → cam2 in cameras.json group_add: - video environment: - NODE_ENV=production - PORT=8444 # Kamera-Konfiguration (Gerät, Name, Auflösung) → cameras.json im APP_PATH # Globale Fallback-Werte (gelten wenn cameras.json keinen Wert hat): # - LIVE_SIZE=640x480 # - LIVE_FPS=30 # - HIRES_SIZE=1280x960 # - HIRES_FPS=15 # - ENCODE_MODE=copybsf # copybsf = Bitstream-Copy, niedrige CPU (Default) # # mjpeg = Re-Encode (~50%, Fallback falls copybsf zickt) # - ON_DEMAND=true # Live nur bei Zuschauern (Default); 'false' = dauerhaft an # - IDLE_GRACE_MS=15000 # Karenz nach letztem Zuschauer vor dem Stop # ── Hinweise ──────────────────────────────────────────────────────────────────── # • Bleibt eine Kamera schwarz? Geräte prüfen: v4l2-ctl --list-devices # und ob 640x480 bzw. 1280x960 als MJPEG nativ angeboten werden: # v4l2-ctl --list-formats-ext -d /dev/video0 # Nur MJPEG-native Auflösungen bleiben CPU-arm (YUYV → Software-Encode = teuer). # • Meckert Portainer über sehr alte Compose-Syntax (dockerfile_inline)? Dann # Compose/Docker-Engine aktualisieren – dieser Aufbau braucht Compose v2. # ────────────────────────────────────────────────────────────────────────────────