diff --git a/doc/05_screenShot_roadmap.md b/doc/05_screenShot_roadmap.md index a7298a4..59d227c 100644 --- a/doc/05_screenShot_roadmap.md +++ b/doc/05_screenShot_roadmap.md @@ -388,9 +388,14 @@ Eine `CameraSwitch`-Instanz pro Gerät — der **einzige** Öffner von `/dev/vid immer nur **einen** FFmpeg. Zustände `stopped | live | grabbing`, Mutex pro Kamera. - **Live (Dauerbetrieb):** `ffmpeg -f v4l2 -input_format mjpeg -video_size 640x480 - -framerate 30 -i /dev/videoN -c:v copy -f mpjpeg pipe:1` → kein Re-Encode. Node parst + -framerate 30 -i /dev/videoN -c:v mjpeg -q:v 5 -f mpjpeg pipe:1`. Node parst die mpjpeg-Frames (Content-Length-basiert), hält den letzten (für `/api/snapshot`), sendet sie an alle Stream-Clients. Crash → Auto-Restart nach 1,5 s. + > ⚠ **`-c:v mjpeg` (Re-Encode), NICHT `-c:v copy`.** `copy` ist auf dieser Kamera + > empirisch tot: 04/09 dokumentieren CPU **107%** + hängendes Bild (FFmpeg verschluckt + > sich an den APP-Feldern des Kamera-MJPEG). Re-Encode = der bewährte ~50%-Pfad + > (entspricht go2rtcs `#video=mjpeg`). **Dieser Fehler wurde am 2026-06-05 zunächst + > wiederholt (copy) und dann korrigiert.** - **HD-Grab (`grabHires`):** Live-FFmpeg `SIGTERM` → **auf `close` warten** (FD frei) → 1280-FFmpeg, warmlaufen (ab Frame ≥6, ≥15 KB, Breite ≥1000), besten Frame greifen → beenden, auf `close` warten → `finally`: **immer** Live zurück (Live hat Priorität). @@ -412,8 +417,8 @@ Ein Node-Container mit FFmpeg, Geräte durchgereicht, `group_add: video`. Env-Ov - **Lokal verifiziert (ohne Kamera):** MJPEG-Parser (Unittest, Chunk-robust, `\r\n\r\n` im Body), HTTP-Routing (snapshot/stream/health, 404/503), Crash-Auto-Restart rate-limitiert. -- **FFmpeg-Args = die der bisher funktionierenden go2rtc-Quelle** (`-f v4l2 -input_format - mjpeg -video_size … -framerate …`), nur Ausgabe `-c:v copy -f mpjpeg`. +- **FFmpeg = der bewährte go2rtc-`#video=mjpeg`-Pfad** (Re-Encode mjpeg→mjpeg, ~50% für + 2 Kameras), Ausgabe `-c:v mjpeg -q:v 5 -f mpjpeg`. **Nicht** `copy` (= 107%, s.o.). - **Auf der Hardware noch zu verifizieren:** CPU-Last, Latenz, HD-Blackout-Dauer, und der Bug-Reproweg unten. @@ -436,8 +441,14 @@ Ein Node-Container mit FFmpeg, Geräte durchgereicht, `group_add: video`. Env-Ov ## Mögliche Folgeschritte -- **Hi-Res nativ?** `v4l2-ctl --list-formats-ext -d /dev/video0` — liefert die Kamera - 1280×960 als **MJPEG**? Falls nur YUYV → `-c:v copy` scheitert → andere native Auflösung - oder bewusst Re-Encode (teurer). +- **Auflösungen nativ MJPG?** ✅ **Bestätigt (2026-06-05)** via `v4l2-ctl --list-formats-ext + -d /dev/video0`: Kamera liefert `MJPG` in **640×480 @ 30 fps** (Live) UND **1280×960 @ + 30 fps** (HD). ABER: trotz nativem MJPG ist `-c:v copy` auf dieser Kamera tot (107%, + APP-Feld-Fehler) → **`-c:v mjpeg` (Re-Encode)**. (Optional `HIRES_FPS=30` verkürzt den + Warmup leicht.) +- **CPU unter 50% drücken?** Re-Encode 2×640@30 ≈ 50% (wie go2rtc). Hebel, falls nötig + (zuerst auf dem Host **messen**, nicht raten): `LIVE_FPS=15` (halbiert Encode-Last) oder + Test des Bitstream-Filters `-c:v copy -bsf:v mjpeg2jpeg` (fügt fehlende Tables ohne + Decode hinzu — ungetestet, könnte echtes Low-CPU bringen oder auch scheitern). - **On-Demand Live** (FFmpeg erst bei erstem Client) wäre stromsparender, ist aber bewusst weggelassen — Dauerbetrieb hält die Übergabe-Logik simpel (weniger Race-Fläche). diff --git a/doc/09_Bug_reports.md b/doc/09_Bug_reports.md index 78dc44a..f2fc456 100644 --- a/doc/09_Bug_reports.md +++ b/doc/09_Bug_reports.md @@ -106,6 +106,16 @@ Encoder auf einem `/dev/videoN` sind konstruktiv ausgeschlossen. Crash-Auto-Restart rate-limitiert. **Auf der Hardware noch zu verifizieren:** CPU-Last, Latenz, HD-Blackout-Dauer, kein 106% nach Screenshot+Reconnect (Testplan in 05). -**FFmpeg-Argumente** sind identisch zu denen, die die *bisher funktionierende* go2rtc- -Quelle erzeugte (`-f v4l2 -input_format mjpeg -video_size 640x480 -framerate 30 -i …`), -nur `-c:v copy -f mpjpeg pipe:1` als Ausgabe → kein Re-Encode. \ No newline at end of file +**FFmpeg = der bewährte go2rtc-`#video=mjpeg`-Pfad:** `-f v4l2 -input_format mjpeg +-video_size 640x480 -framerate 30 -i … -c:v mjpeg -q:v 5 -f mpjpeg pipe:1` (Re-Encode +mjpeg→mjpeg, ~50% für 2 Kameras). + +### ⚠ Regression am 2026-06-05 (eingebaut **und** korrigiert): `-c:v copy` + +Erste Fassung des Schalters nutzte `-c:v copy` (Passthrough) → **CPU 100%+ und wieder +hängendes Bild.** Ursache: `copy` ist auf dieser Kamera empirisch tot — 04/09 dokumentieren +es (107%), FFmpeg verschluckt sich an den APP-Feldern des Kamera-MJPEG +(`[mjpeg] unable to decode APP fields: Invalid data`) → keine validen Frames → Freeze. +**Das war exakt der in 04/05 dokumentierte und ignorierte Punkt.** Fix: `-c:v copy` → +`-c:v mjpeg` (Re-Encode, wie go2rtc). Lehre erneut: **erst die Doku-Fakten anwenden, +dann bauen.** \ No newline at end of file diff --git a/src/cameraSwitch.js b/src/cameraSwitch.js index 14250c8..c3c73a1 100644 --- a/src/cameraSwitch.js +++ b/src/cameraSwitch.js @@ -96,7 +96,7 @@ class CameraSwitch extends EventEmitter { '-f', 'v4l2', '-input_format', 'mjpeg', '-video_size', this.liveSize, '-framerate', String(this.liveFps), '-i', this.device, - '-c:v', 'copy', '-f', 'mpjpeg', 'pipe:1', + '-c:v', 'mjpeg', '-q:v', '5', '-f', 'mpjpeg', 'pipe:1', ]; let p; try { @@ -189,7 +189,7 @@ class CameraSwitch extends EventEmitter { '-f', 'v4l2', '-input_format', 'mjpeg', '-video_size', this.hiresSize, '-framerate', String(this.hiresFps), '-i', this.device, - '-c:v', 'copy', '-f', 'mpjpeg', 'pipe:1', + '-c:v', 'mjpeg', '-q:v', '5', '-f', 'mpjpeg', 'pipe:1', ]; let p; try {