Files
appRobotWebcam/docker-compose.yaml
2026-06-07 17:00:43 +02:00

128 lines
7.5 KiB
YAML
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.
name: approbotwebcam
# ════════════════════════════════════════════════════════════════════════════
# AppRobotWebcam Node-MJPEG-Schalter
# ════════════════════════════════════════════════════════════════════════════
#
# Node besitzt jede Kamera direkt (eine CameraSwitch-Instanz pro /dev/videoN).
# Live: FFmpeg → MJPEG multipart → Browser <img>. Latenz: ~139 ms.
# HD-Grab: Live-FFmpeg stoppen (close-Event = FD frei) → hires-FFmpeg →
# JPEG an Client → Live zurück. Auflösungen in cameras.json konfiguriert.
#
# Kameras (aktuell, by-id = stabil über Reboots):
# cam0 C270 /dev/video0 Live 640×480, Hires 1280×960
# cam1 C270 /dev/video2 Live 640×480, Hires 1280×960
# cam2 C920 /dev/video4 Live 640×480, Hires 1920×1080
#
# Portainer: Stack → Web editor → dieses YAML → Deploy.
# APP_PATH = /absoluter/pfad/zum/appRobotWebcam
#
# Netz: hängt am externen Bridge-Netz "approbot_default" → vom HTTPS-Proxy im
# selben Netz erreichbar als http://AppRobotWebcam:8444 (oder webcam:8444).
# Firewall: TCP 8444 am Host nur für direkten LAN-Zugriff (ports:-Mapping). Läuft
# alles über den Proxy → ports:-Zeile raus, dann ist am Host nichts offen.
#
# Zugriff:
# Viewer: http://<host>:8444/
# Live-Stream: http://<host>:8444/api/stream/cam0
# Snapshot: http://<host>:8444/api/snapshot/cam0
# HD-Snapshot: http://<host>:8444/api/snapshot/cam0/hires
# Kamera-Liste: http://<host>:8444/api/cameras
# Status: http://<host>:8444/health
# ════════════════════════════════════════════════════════════════════════════
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
# Hängt am externen Netz approbot_default (statt host-Mode) → der HTTPS-Proxy im
# selben Netz erreicht den Container per Name (http://AppRobotWebcam:8444).
networks:
- approbot_default
# 8444 am Host veröffentlicht → direkter LAN-Zugriff (http://<host>:8444) bleibt.
# Wenn ALLES über den Proxy läuft, diesen ports-Block entfernen → proxy-only.
ports:
- "8444:8444"
command: sh -c "npm install --omit=dev && node server.js"
volumes:
- ${APP_PATH:-.}:/usr/src/app
devices:
# by-id (Host) → /dev/videoN (Container) stabil über Reboots und USB-Re-Plugs.
# Rechte Seite = Pfad den cameras.json + FFmpeg im Container sehen.
- /dev/v4l/by-id/usb-046d_0825_3BB3FE20-video-index0:/dev/video0 # cam0 C270 (046d:0825)
- /dev/v4l/by-id/usb-046d_081b_342D4F40-video-index0:/dev/video2 # cam1 C270 (046d:081b)
- /dev/v4l/by-id/usb-046d_HD_Pro_Webcam_C920_9C5591DF-video-index0:/dev/video4 # cam2 C920
# GPU-Renderknoten für H.264-Encoding (VAAPI Intel/AMD). Nur nötig, wenn eine
# Kamera encode='h264' nutzt. Auf der Intel-Box bestätigt: /dev/dri/renderD128.
- /dev/dri:/dev/dri
group_add:
- video
- render # Zugriff auf /dev/dri/renderD128 (VAAPI). GID via `getent group render`.
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)
# # h264 = GPU-H.264 → MSE (Bandbreite sparen, braucht GPU)
# - ON_DEMAND=true # Live nur bei Zuschauern (Default); 'false' = dauerhaft an
# - IDLE_GRACE_MS=15000 # Karenz nach letztem Zuschauer vor dem Stop
#
# ── H.264-Hardware-Encoding (nur relevant für encode='h264') ──────────────
# - GPU=intel # intel|amd → VAAPI (gemeinsamer Pfad) · none → libx264 (CPU-Test)
# # → HIER die Maschine wählen: 'intel' (UHD 630) oder 'amd' (680M)
# - HWENC=vaapi # vaapi|qsv|libx264 Encoder erzwingen (überschreibt GPU)
# - HWENC_DEVICE=/dev/dri/renderD128 # VAAPI/QSV-Renderknoten
# - H264_BITRATE=3M # Zielbitrate
# - H264_GOP= # Keyframe-Abstand (Default ~2×fps); kleiner = schnellerer Einstieg, mehr Bitrate
# - H264_PROFILE=main # constrained_baseline|main|high (muss zum Treiber passen)
# - H264_FRAG_MS=200 # fMP4-Fragmentlänge in ms
# - H264_JPEG_FPS=2 # Bildrate des MJPEG-Nebenausgangs (für /api/snapshot)
# - H264_MSE_CODEC= # MSE-Codec-String überschreiben, falls der Browser meckert (z.B. avc1.640020)
# ── Netzwerk ────────────────────────────────────────────────────────────────────
# Externes, bereits existierendes Bridge-Netz (vom Stack "approbot"). Wird hier nur
# referenziert, nicht erstellt. Prüfen: docker network inspect approbot_default
networks:
approbot_default:
external: true
name: approbot_default
# ── Hinweise ────────────────────────────────────────────────────────────────────
# • Neue oder geänderte Kamera: cameras.json anpassen + Redeploy (kein Code-Änderung).
# by-id-Namen ermitteln: ls -la /dev/v4l/by-id/
# Neues Device hier eintragen (by-id → /dev/videoN), dann cameras.json-Eintrag.
#
# • Bleibt eine Kamera schwarz oder liefert falsche Auflösung?
# Direkt auf dem Host testen (ohne Docker, beweist was die Kamera real kann):
# node tools/hires-probe.js /dev/video4 1920x1080 copybsf
# Alternativ manuell:
# v4l2-ctl --list-formats-ext -d /dev/video4 # MJPG-Auflösungen anzeigen
# Nur MJPEG-native Auflösungen in cameras.json verwenden (YUYV = Software-Encode = ~50% CPU).
#
# • HD-Grab liefert schlechte Qualität?
# Standard ist copybsf (Kamera-JPEG pur, keine zweite Kompression).
# "hiresEncode": "mjpeg" in cameras.json nur als Fallback, erzeugt Re-Encode-Artefakte.
#
# • Code-Stand im Container prüfen:
# docker exec AppRobotWebcam grep -n "Grab (minWidth" src/cameraSwitch.js
# Zeigt die neue Log-Zeile → aktueller Code läuft. "1280-Grab" → staler Code.
#
# • Compose v2 ist Pflicht (dockerfile_inline). Bei Portainer-Warnung: Docker-Engine updaten.
# ────────────────────────────────────────────────────────────────────────────────