diff --git a/doc/streamCompression.md b/doc/streamCompression.md new file mode 100644 index 0000000..54c34ad --- /dev/null +++ b/doc/streamCompression.md @@ -0,0 +1,248 @@ +# Stream-Komprimierung (MJPEG → H.264) — Abarbeitungsliste + +> **Status (2026-06-16):** Der H.264-Pfad ist **im Code vollständig vorhanden und +> unit-getestet**, aber **noch nie auf dem Host scharf geschaltet oder gemessen**. +> Diese Datei ist die ausführbare ToDo-Liste, um ihn in Betrieb zu nehmen — destilliert +> aus [14_ReRender_roadmap.md](14_ReRender_roadmap.md) (Hintergrund/Entwurf), +> [02_HardwareEncoding.md](02_HardwareEncoding.md), [03_Protocoll_roadmap.md](03_Protocoll_roadmap.md) +> und [04_Delay_roadmap.md](04_Delay_roadmap.md). +> +> **Es ist also kein „von Null bauen".** Die offene Arbeit ist: *scharf schalten → messen → +> tunen → ausrollen* — und zwar **auf dem Host gemessen, nicht vorhergesagt** +> (Memory-Regel; alle Zahlen unten ohne Messung sind ausdrücklich **Hypothesen**). + +--- + +## Ausgangslage in einem Satz + +Der Live-Stream geht heute als **MJPEG** raus (jedes Frame ein vollständiges JPEG). Das ist +zwar pro Frame komprimiert, aber ohne Inter-Frame-Kompression → hohe Bitrate, und der Client +muss jedes Frame einzeln dekodieren und in ein `` schieben. Bei mehreren Kameras bringt +das schwache Laptops an die Grenze. Mehr Kameras kommen → das Problem wächst. + +## Warum H.264 dem Laptop hilft (Motivation + eine Korrektur) + +„Unkomprimiert" trifft es nicht ganz — MJPEG **ist** komprimiert, nur eben **intra-frame**. +Der Gewinn von H.264 ist trotzdem real und doppelt: + +1. **Inter-Frame-Kompression** (nur Bildänderungen übertragen) → deutlich weniger Bitrate + (Hypothese: ~2–5 MBit/s statt MJPEG-Bitrate; vor dem Umbau messen, siehe ToDo 0). +2. **Hardware-Decode im Browser** — H.264 dekodiert der Client in der GPU; MJPEG dekodiert + er pro Frame auf der CPU/im Main-Thread und tauscht das ``. Genau **das** ist die + Last, die mehrere Streams auf dem Laptop erzeugen. → H.264 entlastet primär den Client. + +> ⚠️ **Realität prüfen, nicht annehmen:** Alle Kameras laufen aktuell auf `liveSize` +> **320×240** ([../cameras.json](../cameras.json)). `1920x1080` dort ist die `hiresSize` +> (Einzelbild beim HD-Knopf), **kein** Dauerstream. Bei 320×240 ist die MJPEG-Bitrate schon +> klein → der Bandbreiten-Gewinn könnte gering sein, der **Client-Decode-Gewinn** aber +> trotzdem zählen. Das entscheidet ToDo 0. + +--- + +## Was bereits im Code steckt (Datei-Pointer) + +| Baustein | Datei | Zustand | +|---|---|---| +| Encoder-Wahl (VAAPI/QSV/libx264) + MSE-Codec-String + FFmpeg-Args | [../src/hwencode.js](../src/hwencode.js) | ✅ + Unit-Test [../test/hwencode.test.js](../test/hwencode.test.js) | +| fMP4-Box-Parser (Init-Segment + Fragmente) | [../src/fmp4Parser.js](../src/fmp4Parser.js) | ✅ + Unit-Test [../test/fmp4Parser.test.js](../test/fmp4Parser.test.js) | +| `encode='h264'`-Zweig: Init-Cache, Fan-out, MJPEG-Nebenausgang (fd 3) für Snapshots | [../src/cameraSwitch.js](../src/cameraSwitch.js) | ✅ | +| `video/mp4`-Route (Init-first Fan-out), `encode`/`mseCodec` in `/api/snapshot`+`/api/cameras` | [../src/snapshotService.js](../src/snapshotService.js) | ✅ | +| MSE-`