# 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-`