241 lines
10 KiB
Markdown
241 lines
10 KiB
Markdown
# đŚ Re-Render & Compress (H.264)
|
||
|
||
> Status: **Entwurf / noch nichts implementiert.** Alle Zahlen unten sind
|
||
> Hypothesen, bis sie auf dem Host gemessen sind (siehe Phase 0).
|
||
|
||
## Problem
|
||
|
||
MJPEG Ăźberträgt jedes Frame als vollständiges JPEG â die Bandbreite skaliert
|
||
linear mit AuflĂśsung Ă Framerate Ă Clients. Bei hĂśheren AuflĂśsungen oder mehreren
|
||
gleichzeitigen Clients kann das im WAN / mobil teuer werden.
|
||
|
||
â ď¸ **Wichtig â aktuelle Realität prĂźfen, nicht annehmen:**
|
||
Die Live-Streams laufen derzeit auf **320Ă240** (`liveSize` pro Kamera in
|
||
[cameras.json](../cameras.json)), **nicht** 1080p. Das `1920x1080` dort ist die
|
||
**`hiresSize`** â also das *Einzelbild* beim HD-Knopf bzw. im Snapshot-Modus, kein
|
||
Dauerstream. Die oft zitierten â~30 MBit/s" gelten also bestenfalls fĂźr einen
|
||
hypothetischen 1080p-Dauerstream, nicht fĂźr den heutigen Betrieb.
|
||
|
||
đ Bevor hier irgendwas gebaut wird, gilt **Phase 0: messen**. Ohne belastbare
|
||
Bandbreiten-Zahl ist unklar, ob sich der ganze Umbau Ăźberhaupt lohnt.
|
||
|
||
## Ziel
|
||
|
||
Reduktion der Bandbreite durch **optionale, pro Kamera schaltbare** Neukodierung
|
||
MJPEG â H.264 (GPU), **ohne** die schlanke Default-Architektur (Node besitzt die
|
||
Kameras, MJPEG-Passthrough, `<img>`-Viewer) fĂźr den LAN-Fall aufzugeben.
|
||
|
||
Zielbitrate (Hypothese): ~2â5 MBit/s bei vergleichbarer wahrgenommener Qualität.
|
||
|
||
---
|
||
|
||
## â ď¸ Der eigentliche Knackpunkt zuerst: Wiedergabe im Browser
|
||
|
||
Das ist der Teil, der den Umbau groĂ macht â und der im ersten Entwurf als
|
||
âUI-Checkbox" verharmlost war.
|
||
|
||
Der aktuelle Viewer rendert den Stream in einem **`<img>`**
|
||
([public/viewer.js](../public/viewer.js)), gespeist aus
|
||
`multipart/x-mixed-replace` ([src/snapshotService.js](../src/snapshotService.js)).
|
||
Ein `<img>` kann **ausschlieĂlich MJPEG** darstellen.
|
||
|
||
> **H.264 läuft niemals in einem `<img>`.** âDer Browser soll mit beidem umgehen"
|
||
> ist daher kein Schalter, sondern ein **zweiter, vollständiger Wiedergabe-Pfad.**
|
||
|
||
H.264 + MJPEG schlieĂen sich auch im Transport gegenseitig aus â H.264 lässt sich
|
||
nicht in MJPEG-multipart verpacken. Es braucht einen eigenen Container und einen
|
||
eigenen Player. Optionen:
|
||
|
||
| Transport | Client | Aufwand | Latenz (Erwartung, zu messen) |
|
||
|-----------|--------|---------|-------------------------------|
|
||
| **MSE (fMP4)** | `<video>` + `MediaSource` + JS-Feeder | mittel | gut, mit Low-Latency-Tuning; sonst 200 msâ1 s Puffer |
|
||
| **WebRTC** | `RTCPeerConnection` + Signaling | hoch | am niedrigsten |
|
||
| HLS/DASH | `<video>` / hls.js | gering | Sekunden â fĂźr Live untauglich |
|
||
|
||
**Achtung â DĂŠjĂ -vu:** WebRTC + H.264 ist genau das, was go2rtc gemacht hat und
|
||
was bewusst entfernt wurde (siehe Architektur-Doku). WebRTC wĂźrde Signaling-
|
||
Infrastruktur wieder einfĂźhren. **Empfehlung: MSE-fMP4**, weil es die âNode besitzt
|
||
die Kameras"-Architektur erhält (Node â ffmpeg â Byte-Stream â Browser) und keine
|
||
ICE/STUN/TURN-Maschinerie braucht. EndgĂźltige Wahl erst nach der Latenz-Messung
|
||
(Phase 0).
|
||
|
||
### MSE-Besonderheit: Init-Segment fßr späte Clients
|
||
|
||
Anders als bei MJPEG (jedes Frame eigenständig) muss bei fragmentiertem MP4 ein
|
||
Client, der **mitten im Stream** dazukommt, zuerst das **Init-Segment**
|
||
(`ftyp`+`moov`) bekommen, dann die Media-Fragmente. Der Server muss das
|
||
Init-Segment also **zwischenspeichern** und jedem neuen Client zuerst schicken,
|
||
bevor er ihn in den Fan-out hängt. Das ist neue Logik gegenßber dem heutigen
|
||
âjedes Frame an jeden"-Modell.
|
||
|
||
---
|
||
|
||
## LĂśsungsansatz: zwei Modi pro Kamera
|
||
|
||
### 1. đ˘ MJPEG-Passthrough (Default, unverändert)
|
||
- bestehender Pfad: `copybsf` â `mpjpeg` â `multipart` â `<img>`
|
||
- minimale Latenz, keine GPU-Abhängigkeit, ~5 % idle-CPU
|
||
- ideal im LAN / bei wenigen Clients
|
||
|
||
### 2. đľ H.264 (optional, GPU)
|
||
- MJPEG â H.264 (VAAPI/QSV) â fMP4 â MSE-`<video>`
|
||
- drastisch reduzierte Bandbreite, fĂźr mobil / WAN / viele Clients
|
||
- hÜhere Komplexität + GPU-Abhängigkeit + (zu messende) Zusatzlatenz
|
||
|
||
Der Browser wählt **pro Kamera** anhand der Server-Metadaten den richtigen Player
|
||
(`<img>` oder `<video>`).
|
||
|
||
---
|
||
|
||
## Architektur-Entscheidung
|
||
|
||
- Encoding erfolgt **direkt in Node via FFmpeg + GPU** (kein go2rtc mehr).
|
||
- Kamera liefert weiterhin MJPEG; der `CameraSwitch` bleibt einziger Geräte-Ăffner.
|
||
- Der Modus hängt am **vorhandenen `encode`-Feld** (siehe Konfigurationsmodell).
|
||
|
||
```
|
||
Kamera (MJPEG, v4l2)
|
||
â
|
||
âââ´âââââââââââââââââââââââââââ encode = 'copybsf' | 'mjpeg'
|
||
â ffmpeg -c:v copy -bsf mjpeg2jpeg -f mpjpeg
|
||
â â multipart/x-mixed-replace â <img> (heutiger Pfad, unverändert)
|
||
â
|
||
ââââââââââââââââââââââââââââââ encode = 'h264'
|
||
ffmpeg -c:v h264_vaapi -f mp4 (fragmentiert)
|
||
â Byte-Stream (+ gecachtes Init-Segment) â MSE â <video> (neu)
|
||
```
|
||
|
||
---
|
||
|
||
## Konfigurationsmodell
|
||
|
||
**Kein neues `compress`-Flag** â das wĂźrde sich mit dem bestehenden Encode-Schalter
|
||
Ăźberschneiden. Stattdessen das vorhandene `encode`-Feld erweitern, das in
|
||
[server.js](../server.js) und [src/cameraSwitch.js](../src/cameraSwitch.js) bereits
|
||
pro Kamera verdrahtet ist:
|
||
|
||
| `encode` | Bedeutung |
|
||
|----------|-----------|
|
||
| `copybsf` | Default, Bitstream-Copy, niedrigste CPU (heute) |
|
||
| `mjpeg` | Re-Encode MJPEGâMJPEG, Fallback (heute) |
|
||
| `h264` | **neu:** GPU-H.264 â fMP4 (VAAPI/QSV, Auto-Erkennung) |
|
||
|
||
Beispiel `cameras.json`:
|
||
|
||
```json
|
||
{
|
||
"id": "cam2",
|
||
"device": "/dev/video4",
|
||
"stream": true,
|
||
"encode": "h264",
|
||
"liveSize": "640x480"
|
||
}
|
||
```
|
||
|
||
`hiresEncode` bleibt davon unberĂźhrt (HD-Snapshot bleibt JPEG â sinnvoll, da ein
|
||
Einzelbild bandbreiten-unkritisch ist).
|
||
|
||
---
|
||
|
||
## Technische Integration (was wirklich zu tun ist)
|
||
|
||
### 1. GPU in den Container durchreichen
|
||
- `/dev/dri` ins Docker-`devices` (wie in [doc/02_HardwareEncoding.md](02_HardwareEncoding.md) fßr die Intel-Box bestätigt: `/dev/dri/renderD128` vorhanden).
|
||
- Encoder dynamisch wählen (`h264_vaapi` vs. `h264_qsv`) statt Hardcoding.
|
||
|
||
### 2. FFmpeg-H.264-Profil (Node spawnt direkt â `#hardware` von go2rtc gibt es nicht mehr)
|
||
Skizze (VAAPI, Werte in Phase 1 zu tunen/messen):
|
||
```bash
|
||
ffmpeg -fflags nobuffer \
|
||
-f v4l2 -input_format mjpeg -video_size 640x480 -framerate 30 -i /dev/video4 \
|
||
-vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \
|
||
-c:v h264_vaapi -b:v 3M -g 60 \
|
||
-f mp4 -movflags +frag_keyframe+empty_moov+default_base_moof -frag_duration 100000 \
|
||
pipe:1
|
||
```
|
||
- MJPEG-**Decode** bleibt vorerst CPU (USB-MJPEG via VAAPI dekodieren ist wackelig)
|
||
â die ânur GPU"-Erwartung in Phase 0/1 **messen**, nicht annehmen.
|
||
- Kurze GOP (`-g`) + `frag_duration` klein = niedrige Latenz, mehr Overhead â Trade-off messen.
|
||
|
||
### 3. `CameraSwitch` erweitern ([src/cameraSwitch.js](../src/cameraSwitch.js))
|
||
Nicht ânur neue Args" â betroffen sind mehrere Stellen:
|
||
- `videoOutArgs()` um den `h264`-Zweig erweitern (anderer Muxer als `-f mpjpeg`).
|
||
- Der `MpjpegParser` ist MJPEG-spezifisch und greift hier **nicht**; fĂźr fMP4 wird
|
||
der Byte-Stream durchgereicht (Init-Segment cachen, Media-Fragmente fan-out).
|
||
- On-Demand / idle-Stop / Auto-Restart gelten weiter â die Pipeline startet wie
|
||
heute erst bei Verbrauchern.
|
||
|
||
### 4. Neue Stream-Route ([src/snapshotService.js](../src/snapshotService.js))
|
||
- `createStreamRouter` sendet heute `multipart/x-mixed-replace`. FĂźr H.264 braucht
|
||
es eine Variante (oder zweite Route), die `video/mp4` als fortlaufenden
|
||
Byte-Stream liefert und neuen Clients zuerst das Init-Segment schickt.
|
||
- `/api/cameras` muss den Modus (`mjpeg`|`h264`) mitliefern, damit der Viewer den
|
||
Player wählen kann.
|
||
|
||
### 5. Viewer erweitern ([public/viewer.js](../public/viewer.js))
|
||
- Bei `encode==='h264'`: `<video>` + `MediaSource` + SourceBuffer-Feeder statt `<img>`.
|
||
- **Auto-Fallback statt schwarzem Bild:** client-seitig
|
||
`MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E"')` prĂźfen; bei
|
||
fehlender UnterstĂźtzung sichtbare Meldung + automatischer RĂźckfall auf MJPEG,
|
||
nicht stilles Schwarz.
|
||
|
||
### 6. UI ([public/config.html](../public/config.html))
|
||
- Statt Checkbox âCompress": Dropdown/Select fĂźr `encode` (copybsf / mjpeg / h264).
|
||
|
||
---
|
||
|
||
## Hardware-Bewertung (Erwartung â in Phase 0/1 zu bestätigen)
|
||
|
||
### đĽď¸ Intel UHD 630 (Coffee Lake) â die heutige Box
|
||
- VAAPI / Quick Sync (H.264/H.265), `/dev/dri/renderD128` bestätigt.
|
||
- Erwartung: 1â2 H.264-Streams stabil, niedrige CPU bei GPU-Encode.
|
||
|
||
### đĽď¸ AMD Radeon 680M (Rembrandt) â falls Zielhardware
|
||
- VAAPI / VCN 3.x; erwartet deutlich mehr Encode-Reserve.
|
||
- â ď¸ **Erst prĂźfen, ob diese Box Ăźberhaupt Ziel ist** und ob `/dev/dri` + VAAPI dort
|
||
laufen â die bisherige Doku basiert auf der Intel-Box.
|
||
|
||
---
|
||
|
||
## Design-Prinzipien
|
||
|
||
- â
**Backward-compatible** â Default bleibt MJPEG, nichts ändert sich fĂźr LAN.
|
||
- â
**Pro Kamera schaltbar** Ăźber das vorhandene `encode`-Feld.
|
||
- â
**Node behält die Kameras** â kein go2rtc/WebRTC-RĂźckbau.
|
||
- â ď¸ **Low latency** nur, wenn die Messung es bestätigt (MSE puffert).
|
||
- â ď¸ **GPU optional** â H.264-Kameras hängen an funktionierendem VAAPI/QSV.
|
||
|
||
---
|
||
|
||
## Offene Entscheidungen
|
||
|
||
1. **Lohnt es sich Ăźberhaupt?** â Phase 0 (Bandbreite der realen Live-Streams messen).
|
||
2. **Transport: MSE-fMP4 (empfohlen) oder WebRTC?** â entscheidet Latenz + Aufwand,
|
||
abhängig von Phase-0-Messung.
|
||
3. **Decode auf CPU oder GPU?** â messen, ob USB-MJPEG-VAAPI-Decode stabil ist.
|
||
4. **Zielhardware Intel-Box oder AMD 680M?**
|
||
|
||
---
|
||
|
||
## Nächste Schritte (ToDo â ehrlicher Stand: nichts erledigt)
|
||
|
||
**Phase 0 â Messen (Vorbedingung, blockiert alles andere)**
|
||
1. đ˛ Bandbreite der heutigen Live-Streams (320Ă240 bzw. konfigurierte `liveSize`) auf dem Host messen, bei 1 und bei n Clients.
|
||
2. đ˛ Hypothese 1080p-Stream gegen die echte ZielauflĂśsung abgleichen â lohnt der Umbau?
|
||
3. đ˛ Transport-Latenz-Test: ein Test-fMP4-Stream (MSE) vs. heutiges MJPEG, Methode wie in [doc/03_Protocoll_roadmap.md](03_Protocoll_roadmap.md) (Stoppuhr-Foto).
|
||
|
||
**Phase 1 â Encode-Pfad (nur wenn Phase 0 positiv)**
|
||
4. đ˛ `/dev/dri`-Passthrough + VAAPI/QSV-Auto-Erkennung.
|
||
5. đ˛ FFmpeg-H.264-fMP4-Profil definieren; CPU/GPU/Latenz auf dem Host messen.
|
||
|
||
**Phase 2 â Server**
|
||
6. đ˛ `encode='h264'` in `videoOutArgs` / `CameraSwitch` (inkl. Init-Segment-Cache + Byte-Stream-Fan-out).
|
||
7. đ˛ H.264-Stream-Route + Modus in `/api/cameras`.
|
||
|
||
**Phase 3 â Client**
|
||
8. đ˛ MSE-`<video>`-Player + Feature-Detection + Auto-Fallback auf MJPEG.
|
||
9. đ˛ `config.html`: `encode`-Auswahl.
|
||
|
||
**Phase 4 â Verifikation**
|
||
10. đ˛ Bandbreite & CPU/GPU vorher/nachher vergleichen (gemessen, auf dem Host).
|