---
title: Benutzerdokumentation
url: https://doc.pstreamer.tv/de/manual/manual.html
lang: de
product: Perfect Streamer
version: 1.13.3.445
---

# Benutzerdokumentation

## Planung und Datenübertragungsprotokolle

Die Software **Perfect Streamer** dient der Übertragung von MPEG-TS-Streams über das öffentliche Internet mit Paketverlusten und Latenzen auf UDP-Basis.

Für jeden MPEG-TS-Stream (**Stream**) werden ein Sender-Server (**Sender**) und ein oder mehrere Empfänger (**Receiver**) konfiguriert; diese Verbindung wird im Folgenden als **Peer** bezeichnet.

Die Konfiguration von Sender und Empfänger reduziert sich auf das Einrichten einer Stream-Liste und der Einstellungen für **input** und **output** jedes Streams. Mehrere **input** in der Liste sorgen für Quellen-Redundanz. Mehrere **output** in der Liste ermöglichen die gleichzeitige Übertragung von Streams an verschiedene Empfänger.

Beim Sender bezeichnet **input** die Quellen der MPEG-TS-Streams und **output** die Übertragung der Streams an die Empfänger. Bei Empfängern bezeichnet **input** den Empfang der Streams vom Sender.

![SPTS-Pipeline](../_images/pipeline_spts.svg)

*Gesamtarchitektur des SPTS-Streams: Eingänge, Verarbeitung auf Stream-Ebene (Input-Switch, TS-Filter, Analyzer, Synchronisierer), OTT / DVR und Ausgänge. Details zu den einzelnen Blöcken siehe folgende Abschnitte.*

Es stehen vier **Peer**-Protokolle für die Übertragung von Streams zwischen Sender und Empfänger zur Verfügung:

- Perfect-Stream-Protokoll (PS1).
- SRT.
- Pro-MPEG / RTP+FEC.
- RIST.

### PS1-Protokoll

Das PS1-Protokoll basiert auf Automatic Repeat reQuest (ARQ). Es ist ressourcenschonend und erlaubt die Übertragung von Streams mit hoher Bitrate.

**Am Sender** — wird im output konfiguriert. Pro Stream ist nur eine Instanz verfügbar. In **Peer** müssen die Logins der Empfänger eingetragen werden. Es wird die UDP listen port festgelegt — sie muss für jeden Stream eindeutig sein.

**Am Empfänger** — wird im Input konfiguriert. Host und Port des Senders sowie Login und Passwort werden angegeben.

Stream-Verschlüsselung (Crypto protection) ist verfügbar, AES-128 wird verwendet. Zum Aktivieren auf beiden Seiten **Crypt Passphrase** als gemeinsamen Schlüssel eintragen.

Während des Betriebs sendet der Empfänger (Client) seine Statistiken an den Sender (Server). Diese sind im Bereich *Peers* nach Auswahl des Clients sichtbar.

Stream-Latenz und Verlustkompensation hängen von den Empfängereinstellungen ab:

**Round Trip Time** — RTT in ms, Standard 300. Geschätzte Kanal-Latenz (Ping). Nach Stream-Start ist der reale RTT in der Statistik (PS1 recovery delay) sichtbar.

**Client Latency (RTT multiplexor)** — RTT-Multiplikator (Standard 10), der die Stream-Latenz im Sender-Puffer bestimmt. Bei Standard ergibt sich also eine Pufferverzögerung von 3000 ms.

Senderseitig gibt es die Latenz-Einstellung (Pufferlänge) **Latency (ms)**. Sie muss größer als die clientseitigen Latenzen sein.

Die Fähigkeit des Protokolls, Verluste zu kompensieren, hängt von der Anzahl der Retransmissions-Anfragen ab und ist von **Client Latency (RTT multiplexor)** bestimmt. Große Verluste führen zu zusätzlichem Netzwerkverkehr. Zur Verringerung der Latenz sollten diese Parameter feiner eingestellt werden.

Korrekte Protokollarbeit zeigt sich in der Client-Statistik. Bei **PS1 recovery**: Not found → Sender-Buffer vergrößern; Duplicates → RTT erhöhen.

Beim Umschalten des Stream-Eingangs (Wechsel der Quelle) am Sender kann die Sendewarteschlange kurzzeitig anwachsen. PS1 bewältigt dies reibungslos: Die ältesten Pakete werden stillschweigend verworfen, während Nummerierung (*seqID*) und Zeitstempel bei den Empfängern durchgängig bleiben — die Empfänger holen die Verluste über den regulären Wiederholungsmechanismus (retr) auf, ohne die Verbindung neu zu initialisieren. Die Anzahl der auf diese Weise verworfenen Pakete ist in der erweiterten Statistik des PS1-Ausgangs sichtbar.

Since the connection is initiated from the side of the receiver, the transmitter requires authentication, the receivers are registered in the peers section. Login and password required.

### SRT-Protokoll

Offenes Protokoll von Haivision. Basiert auf UDT, ist weit verbreitet und bietet gute Eigenschaften zur Kompensation von Paketverlusten.

Anwendungsfälle:

- Peer zwischen **Perfect Streamer**-Instanzen. **Am Sender** — wird im Output konfiguriert, Listen-Modus (Standard). In diesem Modus können mehrere Empfänger verbunden werden. Zur Autorisierung müssen in **Peer** Logins für die Empfänger eingetragen werden. **Am Empfänger** — wird im Input konfiguriert. Host und Port des Senders werden angegeben, ebenso Login und Passwort. Zur Übergabe von Login und Passwort verwendet der SRT-Streamer die streamid im Format `login|password`.
- Peer zwischen **Perfect Streamer** und beliebigen SRT-Streamern von Drittanbietern. **Am Sender** lässt sich der SRT-Client-Modus einrichten, indem listen deaktiviert wird. Die SRT-streamid wird bei Bedarf im Feld login eingetragen. Für den listen-Modus ist die Autorisierung per IP-Adresse verfügbar — sie wird im Feld login bei **Peer** eingetragen. **Am Empfänger** lässt sich der listen-Modus aktivieren, die SRT-streamid im Feld login angeben sowie der Host festlegen, von dem der Empfang erlaubt ist.

Betrieb im Listener-Modus: Empfang und Weitergabe eines TV-Streams mit Angabe des Empfangsports.

Die Ports im Listen-Modus müssen eindeutig sein.

Stream-Verschlüsselung (Crypto protection) ist verfügbar, AES-128 wird verwendet. Zum Aktivieren auf beiden Seiten **Crypt Passphrase** als gemeinsamen Schlüssel eintragen.

Verwendet der Sender den listen-Modus (Standard), wird die Verbindung vom Empfänger initiiert; der Sender erfordert Authentifizierung, und die Empfänger werden in peer registriert. Login und Passwort sind erforderlich.

Die SRT-Protokolloptionen entsprechen der Beschreibung unter [API-socket-options.md](https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md)

**Reorder (SRTO_LOSSMAXTTL)** — Der Wert, bis zu dem die Umordnungstoleranz ansteigen kann. Die Umordnungstoleranz ist die Anzahl der Pakete, die auf eine erkannte „Lücke“ in den Sequenznummern der eingehenden Pakete folgen müssen, bevor ein Verlustbericht gesendet wird (in der Hoffnung, dass die Lücke durch Paketumordnung und nicht durch Verlust verursacht wird). Der Wert der Umordnungstoleranz beginnt bei 0 und erhöht sich, wenn eine Paketumordnung erkannt wird. Dies geschieht, wenn ein „verspätetes“ Paket mit einer höheren Sequenznummer als das zuletzt empfangene, aber ohne Wiederholungs-Flag empfangen wird. Bei einer solchen Erkennung wird die Umordnungstoleranz auf den Wert des Intervalls zwischen der letzten Nummer und der Sequenznummer dieses Pakets gesetzt, jedoch nicht höher als der durch den Parameter SRTO_LOSSMAXTTL festgelegte Wert. Standardmäßig beträgt dieser Wert 0, was bedeutet, dass dieser Mechanismus deaktiviert ist. [SRTO_LOSSMAXTTL](https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md#SRTO_LOSSMAXTTL)

**Overhead (SRTO_OHEADBW, %)** — Overhead zur Bandbreitenwiederherstellung über die Eingangsrate hinaus (siehe SRTO_INPUTBW), in Prozent der Eingangsrate. Wirksam nur, wenn SRTO_MAXBW auf 0 gesetzt ist. Sender: vom Benutzer konfigurierbar; Standard: 25%.

*Empfehlungen:* Der Overhead dient dazu, zusätzliche Bandbreite bereitzustellen, falls ein Paket einen Teil der Bandbreite verbraucht hat, dann aber verloren ging und neu übertragen werden muss. Die effektive maximale Bandbreite sollte daher hinreichend höher als der Bitrate Ihres Streams sein, um Platz für Retransmissionen zu lassen, aber begrenzt, damit retransmittierte Pakete bei Verlust großer Paketgruppen nicht zu einem starken Anstieg der Bandbreitenauslastung führen. Setzen Sie keinen zu niedrigen Wert und vermeiden Sie 0, wenn SRTO_INPUTBW auf 0 gesetzt ist (automatisch). Andernfalls wird Ihr Stream bei jedem Anstieg der Paketverluste rasch unterbrochen. [SRTO_OHEADBW](https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md#SRTO_OHEADBW)

**Max Band (SRTO_MAXBW, bps)** — Diese Option ist nur wirksam, wenn SRTO_MAXBW gleich 0 ist (relativ). Sie steuert die maximale Bandbreite zusammen mit SRTO_OHEADBW nach der Formel: MAXBW = INPUTBW * (100 + OHEADBW) / 100. Ist diese Option auf 0 gesetzt (automatisch), wird der tatsächliche INPUTBW-Wert anhand der Rate des Eingangsstreams (in Fällen, in denen die Anwendung srt_send* aufruft) während der Übertragung geschätzt. Der Mindestwert der Schätzung ist durch SRTO_MININPUTBW begrenzt, d. h. INPUTBW = MAX(INPUTBW_ESTIMATE; MININPUTBW).

*Empfehlungen:* setzen Sie für diesen Parameter den erwarteten Bitrate Ihrer Übertragung und behalten Sie den Standardwert 25% für SRTO_OHEADBW bei. [SRTO_INPUTBW](https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md#srto_inputbw)

**Timeout (SRTO_CONNTIMEO, ms)** — Wert des Verbindungs-Timeouts in Millisekunden. Es ist die Zeit, während der das verbindende Objekt versucht, eine Verbindung herzustellen, und auf eine Antwort des entfernten Endpunkts wartet, bevor die Verbindung mit Fehlercode abgebrochen wird. [SRTO_CONNTIMEO](https://github.com/Haivision/srt/blob/master/docs/API/API-socket-options.md#SRTO_CONNTIMEO)

### Pro-MPEG / RTP+FEC Protokoll (COP3 / SMPTE 2022-1/2)

Lieferung von MPEG-TS über RTP mit vorwärts gerichteter Fehlerkorrektur (FEC, Forward Error Correction). Dasselbe Protokoll erscheint in Literatur und Geräten unter unterschiedlichen Namen:

- **Pro-MPEG / Pro-MPEG COP3** — Code of Practice #3 des Pro-MPEG-Forums, beschrieben im IEEE-Standard ([https://ieeexplore.ieee.org/document/6738329](https://ieeexplore.ieee.org/document/6738329));
- **RTP + FEC** — funktionaler Name (RTP-Stream plus FEC-Kanäle);
- **SMPTE 2022-1** — Column FEC (dasselbe Schema, als SMPTE-Standard veröffentlicht);
- **SMPTE 2022-2** — Row + Column FEC (zweidimensionale Matrix, in PSS implementiert).

Vorteil: niedrige Latenz. Nachteil: hoher zusätzlicher Datenverkehr (Overhead); zudem arbeitet es bei großen Paketverlusten (über 0,2 %) schlecht.

Dieses Protokoll basiert auf RTP mit zwei zusätzlichen FEC-Kanälen (Fehlerkorrekturcode). Die beiden FEC-Kanäle nutzen die Ports `port+2` und `port+4` — das ist beim Hinzufügen mehrerer Streams auf einen Host oder eine Multicast-Gruppe zu beachten.

Beim Sender werden RTP-Pakete in einer Matrix mit **Cols** Spalten und **Rows** Zeilen gruppiert. Beispiel für cols=8 und rows=4 (Standard):

| RTP01 | RTP02 | RTP03 | RTP04 | RTP05 | RTP06 | RTP07 | RTP08 | R1 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| RTP11 | RTP12 | RTP13 | RTP14 | RTP15 | RTP16 | RTP17 | RTP18 | R2 |
| RTP21 | RTP22 | RTP23 | RTP24 | RTP25 | RTP26 | RTP27 | RTP28 | R3 |
| RTP31 | RTP32 | RTP33 | RTP34 | RTP35 | RTP36 | RTP37 | RTP38 | R4 |
| C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 |  |

Die Pakete Rx und Cx bilden FEC-Daten zeilen- und spaltenweise. Je kleiner die Matrix, desto besser die Korrekturfähigkeit, aber desto höher der zusätzliche Verkehr. In diesem Beispiel kommen auf 32 RTP-Pakete des Streams 12 FEC-Pakete.

Stream-Verschlüsselung (Crypto protection) ist mit AES-128 verfügbar, jedoch nicht im Standard enthalten — die Kompatibilität mit Drittsoftware oder -hardware ist daher nicht garantiert.

Es gibt nicht-standardisierte Protokollerweiterungen:

**Multiplexing** — Multiplexen von RTP-Kanälen über einen einzigen UDP-Port. Kann die Netzwerkkonfiguration vereinfachen.

### RIST-Protokoll

Neues offenes Protokoll auf Basis von RTP/RTCP. Arbeitet nach Automatic Repeat reQuest (ARQ) ohne ACK, nur NACK — das garantiert hohe Effizienz.

Verwendet Unicast und Multicast.

Es sind die Profile Simple und Main implementiert. Simple nutzt zwei aufeinanderfolgende UDP-Ports — der konfigurierte Port muss gerade sein. Main verwendet einen einzigen RTP-Port mit Datenmultiplexing.

**On transmitter** — wird im output konfiguriert. Für Unicast werden Adresse und Port des Empfängers festgelegt. Für Multicast muss die Netzwerkschnittstelle angegeben werden, über die die Datenübertragung erfolgt. Für Multicast lässt sich außerdem eine Autorisierung der Empfänger über die IP-Adresse einrichten, indem Logins in **Peer** eingetragen werden.

**Am Empfänger** — in input konfigurieren. Für Unicast werden Empfangsport (listen) und zwingend die Netzwerkschnittstelle festgelegt. Für Multicast wird nur die Multicast-Gruppe samt Port angegeben.

RIST unterstützt mehrere separate Peers (Adressen). Mit einem Gewicht > 1 lässt sich Lastverteilung zwischen den Peers entsprechend dem Gewicht aktivieren.

Verwendet der Sender Multicast, kann es viele Empfänger geben. In diesem Fall ist eine Authentifizierung der Empfänger anhand der IP-Adresse möglich. Aktivieren Sie dazu die Authentifizierung in den Sendereinstellungen (standardmäßig deaktiviert) und fügen Sie den Client zur **Peer**-Liste hinzu; tragen Sie im Login-Feld die IP-Adresse ein.

### Andere Protokolle

Neben den **peer**-Protokollen stehen für Empfang und Senden von Streams weitere zur Verfügung:

| Protocol | Input | Output |
| --- | --- | --- |
| UDP | Yes | Yes |
| RTP | Yes | Yes |
| TCP | Yes | No |
| HLS | Yes | Yes |
| RTSP | Yes | No |
| RTMP | Yes | Yes |
| pipe | Yes | Yes |

**UDP** (Unicast oder Multicast) — Empfangen und Senden von MPEG-TS im UDP-Paket, bis zu 7 TS-Pakete pro UDP-Paket.

**RTP** (Unicast oder Multicast) — RFC-basiertes Standardprotokoll. Reordering-Wiederherstellung wird unterstützt.

**TCP** — Empfang von MPEG-TS über eine TCP-Verbindung im TCP-Client-Modus.

**HLS** — Empfang und Auslieferung von MPEG-TS über HTTP oder das Apple-Standard-HLS-Protokoll. Beim Empfang wird aus einer adaptiven Playlist die Variante mit der höchsten Bitrate ausgewählt. **HLS input** steht nur für **SPTS**-Streams zur Verfügung; für MPTS UDP / RTP / TCP / file oder einen DVB-Adapter verwenden.

**RTSP** — Empfang eines Videostreams aus RTSP-Quellen (IP-Kameras, RTSP-Server) auf Basis von FFmpeg avformat. PSS öffnet eine RTSP-Sitzung (`DESCRIBE` → `SETUP` → `PLAY`), liest die ES-Pakete, remuxt sie in einen internen MPEG-TS und speist die Stream-Pipeline. Der Annex-B-BSF (`h264_mp4toannexb` / `hevc_mp4toannexb`) wird automatisch eingebunden. Enthält die Quelle keine Audiospuren, wird eine stille MPEG-1-Layer-2-Spur (48 kHz, Stereo, 128 kbps) mit am Video gekoppeltem PTS hinzugefügt — dies ist für die Kompatibilität mit der Stream-Pipeline erforderlich, die mindestens eine Audiospur voraussetzt.

Einstellungen des **RTSP**-Inputs:

- **URI** — die vollständige URL der RTSP-Sitzung, z. B. `rtsp://cam.local/play1.sdp`.
- **Login**, **Password** — RTSP-Anmeldedaten, falls Basic-/Digest-Authentifizierung erforderlich ist.
- **User-Agent** — benutzerdefinierter User-Agent (optional).
- **Cookies** — `Cookie`-Header (optional, für Server mit Cookie-basierter Authentifizierung).
- **Transport** — RTP-Transport innerhalb von RTSP:

  - `UDP` — RTP über UDP (geringe Latenz, paketverlustanfällig);
  - `TCP` (Standard) — interleaved RTP innerhalb der RTSP-TCP-Sitzung; durchquert NAT und Firewalls;
  - `HTTP` — RTSP-over-HTTP-Tunnel zur Durchleitung durch reine HTTP-Proxys.
- **Timeout** — Öffnungs- und Lese-Timeout in Sekunden. Standardwert: 10.
- **Trace** — ausführliches Protokoll für Diagnosezwecke.

RTSP-input läuft in einem Prozess mit PSS und benötigt keine externe Binärdatei. Für Protokolle, die von den integrierten Mitteln nicht abgedeckt werden, bleibt die Umgehungslösung über **std** + eine externe Anwendung (ffmpeg, gstreamer) verfügbar.

Der RTSP-Input ist eine Single-Program-Quelle und steht daher **nur für SPTS-Streams** zur Verfügung.

**pipe** — Lesen aus und Schreiben in eine bestehende benannte Pipe (FIFO). Im Gegensatz zu **std** startet PSS keinen Kindprozess — der externe Producer / Consumer muss unabhängig gestartet werden und die Pipe auf seiner Seite offen halten. In den Einstellungen gibt **File Path** den Pfad zu einer bereits existierenden FIFO an (erzeugt z. B. mit dem Befehl `mkfifo`). PSS öffnet die FIFO im Read-Modus (für **input**) bzw. Write-Modus (für **output**). Verfügbar für SPTS und MPTS.

### Protokoll RTMP / RTMPS

**RTMP** (Real-Time Messaging Protocol) ist in **Perfect Streamer** sowohl für den Empfang (**input**) als auch für die Übertragung (**output**) verfügbar, auf Basis von FFmpeg avformat. Typische Szenarien sind das Veröffentlichen eines Fernsehkanals auf externen Plattformen (**YouTube**, **Facebook** und beliebige RTMP-Empfänger) sowie der Empfang eines Streams von einem RTMP-Server. Die sichere Variante **RTMPS** (RTMP über TLS) wird für beide Richtungen unterstützt; in diesem Fall fungiert Perfect Streamer als TLS-Client.

RTMP ist ein single-program-Protokoll, daher sind RTMP **input** und **output** **nur für SPTS-Streams** verfügbar.

**RTMP input (Empfang)**

Perfect Streamer verbindet sich als RTMP-Client mit der angegebenen Quelle `rtmp://` oder `rtmps://` und holt den Stream ab (Pull-Modus). Die empfangenen Elementarströme (Video **H.264** oder **HEVC**, Audio **AAC**) werden in das integrierte MPEG-TS remultiplext und in die Stream-Pipeline eingespeist. Einen Modus zum Warten auf eine eingehende Veröffentlichung (listen / push) gibt es nicht — den Empfang initiiert immer Perfect Streamer selbst.

**RTMP**-input-Einstellungen:

- **URL** — die vollständige Adresse der RTMP-Sitzung, zum Beispiel `rtmp://origin.example/live/streamkey`. Die Schemata `rtmp` und `rtmps` sind zulässig.
- **Bind Interface** — die lokale Schnittstelle oder IP-Adresse, von der aus die ausgehende Verbindung aufgebaut wird. Standardmäßig wird sie durch das Routing des Betriebssystems bestimmt.
- **Timeout** — Timeout für Verbindung und Lesen, von 1 bis 120 Sekunden. Standardwert 10.
- **TLS Verify** — bei `rtmps` das Serverzertifikat prüfen. Standardmäßig deaktiviert.
- **Trace** — ausführliches Protokoll zur Verbindungsdiagnose.

**RTMP output (Veröffentlichung)**

Perfect Streamer veröffentlicht einen SPTS-Stream an einen externen RTMP-Empfänger: Es verbindet sich als RTMP-Client mit der Veröffentlichungsadresse der Plattform und sendet den Stream (Publish-Modus). RTMP ist ein single-program-Format: Es wird ein Video (**H.264** oder **HEVC**) und höchstens eine **AAC**-Audiospur veröffentlicht. Hat die Quelle mehrere Spuren, werden je eine Video- und eine Audiospur ausgewählt; überzählige und inkompatible (zum Beispiel Nicht-AAC-Audio) werden nicht übertragen. Eine mehrspurige Quelle lässt sich vorab mit den Filtern **PID Accept** / **PID Reject** einschränken (siehe «Filterung und Modifikation von MPEG-TS»).

**HEVC**-Video wird über **Enhanced RTMP** veröffentlicht — aktiviert durch die Option **Allow HEVC**. Wenn die empfangende Plattform HEVC nicht unterstützt, deaktivieren Sie die Option: Dann wird nur **H.264** veröffentlicht.

Vor Beginn der Veröffentlichung wird der Stream analysiert. Gibt es kein kompatibles Video, wird nicht veröffentlicht. Das Analyseergebnis ist in der Stream-Statistik verfügbar: ein Kennzeichen der Veröffentlichungsfähigkeit, die ausgewählten Video- und Audiocodecs, die Liste der verworfenen Spuren und der Verbindungszustand.

**RTMP**-output-Einstellungen:

- **URL** — die Veröffentlichungsadresse der Plattform, zum Beispiel `rtmp://a.rtmp.youtube.com/live2/<stream-key>`. Die Schemata `rtmp` und `rtmps` sind zulässig.
- **Bind Interface** — die lokale Schnittstelle oder IP-Adresse der ausgehenden Verbindung. Standard — das Routing des Betriebssystems.
- **Timeout** — Timeout für Verbindung und Übertragung, von 1 bis 120 Sekunden. Standardwert 10.
- **Allow HEVC** — Veröffentlichung von **HEVC**-Video über **Enhanced RTMP** zulassen. Standardmäßig aktiviert; bei Deaktivierung wird nur **H.264** übertragen.
- **TLS Verify** — bei `rtmps` das Serverzertifikat prüfen. Standardmäßig deaktiviert.
- **Trace** — ausführliches Protokoll zur Diagnose der Veröffentlichung.

Beispiele für Veröffentlichungsadressen:

- **YouTube** — `rtmp://a.rtmp.youtube.com/live2/<stream-key>`
- **Facebook** — `rtmps://live-api-s.facebook.com:443/rtmp/<stream-key>`

### Streams mit Dateien und Geräten

Für **input** und **output** steht das Protokoll **file/device** für Dateien und Geräte zur Verfügung.

**output file/device** — Aufzeichnung in eine Datei oder Ausgabe an ein Gerät. Eine Datei-Aufzeichnung kann zur ts-Datei-Aufnahme und nachfolgenden Diagnose mit anderen Analyzern erforderlich sein. Geräte-Ausgabe — beliebiges Gerät (auch SDI), das in **/dev** registriert ist.

**input file/device** — Loop-Wiedergabe von Video aus einer TS-Datei.

**Bei Arbeit mit Dateien wird der vollständige Dateipfad im Feld *File Path* angegeben:**
  /catalog/stream.ts.

Bei Geräten wird zusätzlich das Flag *Is Device* aktiviert.

### Liste erlaubter Streams und Peer-Beschränkung

Zur Einschränkung des Zugriffs auf TV-Streams seitens des Clients (**Peer**) in den Modi SRT Listen, PS1, HLS und HTTP ist im Programm die Funktionalität einer Liste erlaubter Streams implementiert. In den **Peer**-Einstellungen am Sender wird die Liste der verfügbaren Sender festgelegt. Dazu im Feld *Stream Access* aus der allgemeinen Senderliste des Servers nur diejenigen Sender hinzufügen, die diesem **Peer** ausgegeben werden sollen. Standardmäßig sind bei leerer Liste alle Sender verfügbar.

Für **Peer** lassen sich Zeit- und Verbindungslimits pro Transportprotokoll setzen.

**Anonymous peer**

Standardmäßig hat das Peer-Login den Wert *anonymous*. Ein anonymer Peer erlaubt die Stream-Verteilung ohne Bindung an IP oder Login/Passwort. Es gelten Einschränkungen hinsichtlich der Anzahl ausgegebener Streams je Transportprotokoll, des Ablaufdatums und der Liste erlaubter Streams.

Es ist möglich, einen individuellen Peer per Login (Name) und Passwort anzulegen.

Für die Peer-Autorisierung per IP muss die Option „Login Is IP“ aktiviert werden.

Autorisierungsoptionen:

- Nach einzelner IP
- Nach IP-Bereich, z. B. „192.168.1.10-192.168.1.20“
- Kombinierte Variante, Syntax der IP-Listen: `ip[-ip2][,...]`

### Anbindung von Drittanwendungen

Zur Unterstützung anderer Protokolle, die mit den eingebauten Mitteln nicht abgedeckt sind, lassen sich Streams über externe Konsolenanwendungen empfangen und übertragen. Dafür gibt es ein eigenes **std**-Protokoll für **input** und **output**. Der MPEG-TS-Stream wird über die Standardein-/-ausgabe des Betriebssystems empfangen und übertragen.

In den Einstellungen werden die Konsolenanwendung (absoluter Pfad) und die Kommandozeile angegeben; Umgebungsvariablen sind ebenfalls möglich.

Bei **input** mit externer Anwendung müssen Meldungen auf stdout vermieden werden — Ausgabe nur auf stderr.

Für den **output** lässt sich Paketierung von bis zu 7 MPEG-TS-Paketen einstellen.

### Anforderungen an den Eingangsstream

Konformität mit ISO 13818-1, Single Program (SPTS) oder Multi Program Transport Stream (MPTS). MPTS-Besonderheiten unten; die folgenden Einstellungen beziehen sich auf SPTS.

Mindestens eine Tonspur ist erforderlich.

Streams ohne Video werden unterstützt, aktiviert über den **Radio**-Modus.

Verschlüsselte Streams werden unterstützt; dafür muss **Scrambled Stream** aktiviert werden.

Für die **Synchronisation** muss der Stream gültige PCR-Marken enthalten.

### Stream-Einstellungen

Einen eindeutigen Stream-Namen vergeben (lateinische Buchstaben, Ziffern, „_“, „-“). Zusätzlich kann ein Anzeigename gesetzt werden, der Kyrillisch und andere Sprachen unterstützt.

**Stream Timeout** — globales Stream-Timeout. Liefert kein gültiger Input innerhalb dieser Zeit, erfolgt ein vollständiger Neustart.

**Pause** — versetzt **stream** sowie alle **input** und **output** in den inaktiven Zustand. Neu hinzugefügte **stream**s sowie **input**s/**output**s sind standardmäßig pausiert und inaktiv.

Das Programm prüft den Eingangsstream am **input** auf Gültigkeit. Schlägt die Prüfung fehl, gilt der **input** als fehlerhaft.

**Check Interval** — Intervall für erneute Stream-Prüfung.

MPEG-TS-Filtereinstellungen:

**Remove All Unnecessary Data** — entfernt alle nicht notwendigen Daten außer PAT/PMT, Video und Audio, mit Ausnahme der in den separaten Filtern festgelegten Elemente (siehe unten)

**Remove SDT** — SDT-Daten entfernen (Kanalname, Anbieter usw.).

**Remove EIT/EPG** — EPG-Daten entfernen.

**Remove Teletext** — Teletext entfernen.

**Remove Subtitles** — Untertitel entfernen.

Bitratensteuerung des Streams:

**Bitrate mode** — Bitratensteuerungsmodus.

1. Origin (default) — der Stream wird unverändert weitergegeben.
2. VBR — entfernt NULL-Pakete und minimiert so die Bitrate. Nur für OTT-Distribution aktivieren.
3. CBR auto — aktiviert Bitratenausgleich durch NULL-Pakete (Stuffing). Die Ziel-Bitrate richtet sich nach der maximalen Eingangsbitrate.
4. CBR set stuffing bitrate — die gewünschte Bitrate explizit setzen. Liegt sie unter der Eingangsbitrate, wird wie im CBR-Auto-Modus angeglichen.

Bei aktivem CBR entspricht die PCR Accuracy der TR 101 290; das PCR-Intervall bleibt wie im Originalstream.

Stream-Korrekturen:

**Fix PAT/PMT interval** — passt den Intervall an TR 101 290 an, indem zusätzliche PAT/PMT eingefügt werden.

### Quellenredundanz

Mehrere **input**s können als Liste angegeben werden, aber nur einer ist aktiv. Fällt der aktive **input** aus, wird der nächste der Liste versucht — zyklisch.

Wird im **stream** **Fallback Check** aktiviert, erfolgt während des Betriebs eines Backup-**input** (nicht des ersten in der Liste) eine erneute Prüfung der höher gelisteten Einträge im Intervall **Check Interval**. Ist der Stream bei der Wiederholungsprüfung gültig, schaltet **stream** auf ihn um.

Da die Reihenfolge der **input**s relevant ist, kann sie geändert werden. Ein pausierter **input** wird im Betrieb nicht berücksichtigt.

### Filtern und Modifizieren von MPEG-TS

Standardmäßig wird der MPEG-TS-Stream unverändert weitergegeben.

Für jeden **input** stehen folgende Filteroptionen für den MPEG-TS-Stream zur Verfügung:

**PID Accept** — Liste erlaubter PIDs. Bei leerer Liste ist alles erlaubt außer **PID Reject**.

**PID Reject** — Liste verbotener PIDs. Hat Vorrang vor **PID Accept**.

Die PID kann geändert werden. Dafür werden die Listen **PID Old** und **PID New** befüllt.

**Mapping PID and Languages** — Neuzuordnung der Sprache von Audiospuren.

**Default Language** — Standardsprache festlegen, falls die Tonspur keine Sprache hat.

Für den **stream** können neue MPEG-TS-Daten (SDT-Tabelle) gesetzt werden:

- MPEG-TS Network ID
- Service Name
- Provider Name
- Language

## MPTS-Streams

MPTS-Stream — ein MPEG-TS-Stream mit mehreren Diensten; jeder Dienst besitzt eine eindeutige Programmnummer (PNR). Wird für die DVB-Ausstrahlung verwendet.

Für MPTS-Streams stehen keine Filteroptionen zur Verfügung. Die Streams werden unverändert weitergegeben.

**RTSP** kann keine Quelle eines MPTS-Streams sein — es ist ein single-program-Protokoll, das nur als SPTS-Quelle anwendbar ist.

Die Mosaic-Funktion ist standardmäßig aus. Auf schwachen CPUs nicht aktivieren — kann zusätzlichen Jitter erzeugen.

In der Stream-Diagnose werden Daten für jedes Programm separat sowie eine Gesamtstatistik angezeigt.

![MPTS-Pipeline](../_images/pipeline_mpts.svg)

*Architektur des MPTS-Streams: Durchleitung ohne dienstebezogene Filterung; für eine dienstebezogene Verarbeitung (OTT, DVR, Filterung) wird der Demultiplexer-Input eines SPTS-Streams verwendet.*

### Demultiplexer

Extrahiert einzelne Streams aus einem MPTS-Stream. Hierzu im SPTS-Stream einen Input vom Typ demux hinzufügen, die Quelle wählen und den Dienst per PNR auswählen. Ist die Quell-MPTS aktiv, steht bei der PNR-Auswahl eine Liste zur Verfügung; andernfalls ist die PNR manuell einzugeben.

### Multiplexer

Stellt aus einzelnen SPTS-Streams einen MPTS-Stream zusammen. So konfigurieren Sie ihn:

- MPTS-Stream erstellen.
- Einen input vom Typ muxer hinzufügen. Die Bitrate zum Ausrichten des CBR-Streams festlegen (Stuffing, TR 101 290 und T-STD compliance). Wird 0 (Standard) gesetzt, findet kein Alignment statt — die Bitrate entspricht den Eingangsstreams. Dort können auch einige MPEG-TS-Parameter angegeben werden; für die meisten Anwendungen sind die Standardwerte ausreichend.
- Im Quell-Stream einen Output vom Typ muxer hinzufügen. Servicename und ggf. Providername eintragen. Bei Nicht-Latein-Schrift in den MPEG-TS-Stream-Einstellungen die Sprache wählen.
- Für alle Quellen wiederholen.

Der Multiplexer erzeugt für den Stream SDT, NIT und TDT/TOT (Zeitmarken). EIT (EPG) stammt aus den Quellströmen. PIDs werden neu vergeben.

## Teststreams

Test Stream generator — ein Testsignal (Testbild). Ermöglicht das Erzeugen generierter Videostreams als Platzhalter für die Ausstrahlung oder als Ersatz bei Ausfällen der Hauptstreams. Bildtyp, Ton sowie eingeblendeter Text und Uhrzeit lassen sich festlegen.

Die Konfiguration erfolgt durch Aktivieren des passenden `input`-Typs am Stream. Videoformat, Auflösung, Bitrate, Lautstärke und Audiofrequenz u. a. lassen sich einstellen.

Die Liste der verfügbaren Test Streams findet sich im linken Seitenmenü der Anwendung.

## OTT-Dienst

Liefert Streams über HTTP-basierte Protokolle — **HLS** (über MPEG-TS), **MPEG-DASH** und **Low-Latency HLS** (über CMAF — fragmentiertes MP4, seit Version 1.13) sowie **MPEG-TS over HTTP**. Unterstützt werden HTTPS (SSL) und [HTTP/3 (QUIC)](ott.md#http3). Die Auslieferung wird auf der Registerkarte **OTT** der **Stream**-Einstellungen aktiviert.

Die Verbindungs-URLs haben das Format:

- [http://host:port/http/stream/login/password](http://host:port/http/stream/login/password) — Autorisierung per Login und Passwort
- [http://host:port/http/stream/login](http://host:port/http/stream/login) — Autorisierung per Login (Token)
- [http://host:port/http/stream/](http://host:port/http/stream/) — Autorisierung per IP

**host** und **port** werden in den **http server**-Einstellungen festgelegt.

**stream** — **ID** des Streams. Nicht zu verwechseln mit der Reihenfolge in der Stream-Liste. Die **ID** wird im Kopf der Stream-Statistikseite und in der Spalte *ID* der Stream-Liste angezeigt; sie wird bei der Stream-Erstellung festgelegt und ändert sich nie.

Analog für **HLS**, **DASH** und **Low-Latency HLS** (die letzten beiden — nur im *OTT/HLS/LL-HLS/LL-Dash*, siehe unten):

- [http://host:port/hls/stream/login/password](http://host:port/hls/stream/login/password)
- [http://host:port/hls/stream/login](http://host:port/hls/stream/login)
- [http://host:port/hls/stream/](http://host:port/hls/stream/)
- [http://host:port/dash/stream/login/password](http://host:port/dash/stream/login/password)
- [http://host:port/dash/stream/login](http://host:port/dash/stream/login)
- [http://host:port/dash/stream/](http://host:port/dash/stream/)
- [http://host:port/llhls/stream/login/password](http://host:port/llhls/stream/login/password)
- [http://host:port/llhls/stream/login](http://host:port/llhls/stream/login)
- [http://host:port/llhls/stream/](http://host:port/llhls/stream/)

Auf der Stream-Statistik-Seite werden die URLs der verbundenen Protokolle (als Vorlage) und ihr aktueller Status angezeigt. Nicht autorisierter Zugriff ist verboten — Clients müssen in **Peers** eingetragen sein.

Für **HLS** und **DASH** sind in der URL zusätzliche Parameter verfügbar (optional):

[URL]?a=1&s=40&m=40&v=5&h3=1

- **a**: 1 — absoluter Pfad in der Playlist, 0 — relativer Pfad (Standard).
- **s**: Länge der dynamischen Playlist (Sekunden), Standard 40 s.
- **m**: Mindestlänge der dynamischen Playlist (s); Standard 40 s. Maximale Länge der dynamischen Playlist 60 s. Liegt die aktuelle Chunk-Buffer-Größe unter der im Request geforderten Mindestgröße, wird HTTP 404 zurückgegeben. So startet HLS stets mit einem gefüllten Chunk-Buffer auf dem Server.
- **v**: die in der Playlist ausgegebene HLS-Protokollversion. Standardmäßig hängt der Wert vom HLS-Modus ab (siehe unten): *OTT/HLS* und *OTT/HLS/LL-HLS/LL-Dash* — *6*, *Peering/HLS* — *3*. Ein expliziter Wert in der URL überschreibt den Standard. Ein Wechsel der Version kann für die Kompatibilität mit einem bestimmten HLS-Client erforderlich sein.
- **h3**: Opt-in für HTTP/3 (QUIC) für diese OTT-Sitzung. Veranlasst den Server, im Response einen `Alt-Svc`-Header auszugeben; ein kompatibler Player / Browser wechselt daraufhin auf QUIC. Siehe Abschnitt [HTTP/3 (QUIC)](ott.md#http3) weiter unten.

Für die Kompatibilität mit einigen HLS-Clients kann der Dateiname index.m3u8 an die URL angehängt werden, z. B. [http://host:port/hls/stream/login/password/index.m3u8](http://host:port/hls/stream/login/password/index.m3u8).

Der Auslieferungsmodus wird über die Stream-Einstellung *OTT HLS* festgelegt (Registerkarte **OTT**): *Peering/HLS*, *OTT/HLS* oder *OTT/HLS/LL-HLS/LL-Dash*.

*Peering/HLS* — ein Modus mit einfacher Aufteilung in Segmente (Chunks). Empfohlen für das Peering (die Distribution) von Streams. Es wird nur **HLS** über MPEG-TS (`/hls`) ausgeliefert. Standardmäßig wird die Playlist als *EXT-X-VERSION:3* ausgegeben, um mit Peer-Clients kompatibel zu sein.

*OTT/HLS* — ein Modus mit einer Segmentaufteilung, die auf einen schnellen Player-Start beim OTT-Broadcasting optimiert ist. In diesem Modus ist die CPU-Last höher; er wird für das Broadcasting empfohlen. Es wird **HLS** über MPEG-TS (`/hls`) ausgeliefert. Standardmäßig wird die Playlist als *EXT-X-VERSION:6* mit *EXT-X-INDEPENDENT-SEGMENTS* und dem Attribut *CHARACTERISTICS* in *EXT-X-MEDIA TYPE=SUBTITLES* ausgegeben (Apple HLS, hls.js, Safari, dash.js/Shaka). Benötigt ein bestimmter Client einen früheren Wert, setzen Sie ihn über den Query-Parameter `?v=` (siehe die Parameterliste oben).

*OTT/HLS/LL-HLS/LL-Dash* — ein Auslieferungsmodus über **CMAF** (fragmentiertes MP4, *fMP4*; seit Version 1.13). Der Stream erzeugt fMP4-Segmente (`.m4s` + gemeinsame `init.mp4`, `mimeType="video/mp4"`), auf deren Basis ausgeliefert werden:

- **MPEG-DASH** unter `/dash` — jetzt über CMAF und **nicht** über MPEG-TS;
- **Low-Latency HLS** unter `/llhls` (siehe [Low-Latency HLS](ott.md#llhls) unten);
- bei aktivierter Stream-Einstellung *Enable TS Chunk* (Standard *true*) — zusätzlich Legacy-**HLS** über MPEG-TS (`/hls`), wie im *OTT/HLS*; bei *false* werden nur fMP4-Segmente ausgeliefert, was Festplatte und CPU schont.

Die **HLS**-Playlist im *OTT/HLS/LL-HLS/LL-Dash* ist standardmäßig ebenfalls *EXT-X-VERSION:6*.

> **Bemerkung**
>
> **MPEG-DASH** und **Low-Latency HLS** sind nur im *OTT/HLS/LL-HLS/LL-Dash* verfügbar. Im *OTT/HLS* und *Peering/HLS* wird ausschließlich **HLS** über MPEG-TS ausgeliefert.

Für den HTTP-Server kann SSL (HTTPS) aktiviert werden — in den Server-Einstellungen.

**Chunk Min Interval und Chunk Max Interval**

Im OTT-Modus wird der Stream auf PAT/PMT/SPS/PPS/IFrame analysiert, und die Chunks werden nach dem Kriterium des schnellen Player-Starts geschnitten. Die Analyse beginnt bei *min interval*, und falls die Daten aus irgendeinem Grund nicht gefunden werden, wird der Chunk bei *max interval* zwangsweise geschnitten.

**GOP-aligned segments**

Im *OTT/HLS* richtet der Segmenter die Chunk-Grenzen an Random-Access-Punkten aus und unterscheidet zwischen *IDR* und einem gewöhnlichen *I-frame*. Sendet die Quelle mit *closed-GOP* (IDR vorhanden), beginnt jedes *.ts*-TS-Segment garantiert mit *SPS / PPS / IDR* (bei HEVC — zusätzlich *VPS*) — einem echten Einstiegspunkt, an dem der Player den Stream öffnet. Ist die Quelle *open-GOP* / ohne IDR, dient der nächstgelegene I-frame als Grenze (das bisherige Verhalten). Der Segmenter schneidet den „Schwanz“ des vorherigen GOP — die *P / B*-Slices — aus dem Fenster zwischen dem führenden PAT und dem ersten SPS des Chunks heraus. Das:

- beseitigt das anfängliche Schwarzbild beim Start einer VOD-Sitzung (`?t=` / `?epg=`) in *hls.js*, *Safari* und *VLC*: der MSE-Decoder erhält das korrekte Header-Set der NAL-Einheiten bereits im ersten Segment und startet sofort;
- bringt die Ausgabe in Übereinstimmung mit *HLS RFC 8216 §3* („Each Media Segment MUST contain a SPS and a PPS that decode its first Access Unit“);
- macht die *EXT-X-INDEPENDENT-SEGMENTS*-Erklärung in einer *EXT-X-VERSION:6*-Playlist korrekt.

Steuerung über die Stream-Einstellung *gop-aligned-segment* (Standard *true*). Wirkt nur bei *HLS = OTT/HLS*: im *Peering/HLS* und bei MPTS-Streams bleibt das Verhalten unverändert. PSI (PAT / PMT) und Audio bleiben vollständig erhalten; einziger Nebeneffekt ist ein einbildiger *continuity_counter*-Sprung auf der Video-PID an der Grenze zweier benachbarter Chunks, was für HLS / DASH MSE eine legitime Decode-Boundary ist.

Bei der Ausrichtung an Einstiegspunkten kann die tatsächliche Chunk-Dauer über *Chunk Min Interval* hinaus aufgerundet werden. Daher spiegelt der Wert *EXT-X-TARGETDURATION* in der Live-Playlist die **maximale tatsächliche** Segmentdauer wider (Abschnitt 4.3.3.1 von *RFC 8216*) statt des konfigurierten Minimums. Das hält das Manifest innerhalb des Standards: *hls.js* und kompatible Player verkürzen das Aktualisierungsintervall der Playlist nicht und lösen keine falschen *bufferStalledError* aus.

**Low-Latency HLS (/llhls)**

Im *OTT/HLS/LL-HLS/LL-Dash* steht neben `/dash` ein separater **Low-Latency HLS**-Endpoint zur Verfügung — Auslieferung mit reduzierter Latenz auf denselben CMAF-fMP4-Segmenten:

- [http://host:port/llhls/stream/login/password](http://host:port/llhls/stream/login/password)
- [http://host:port/llhls/stream/login](http://host:port/llhls/stream/login)
- [http://host:port/llhls/stream/](http://host:port/llhls/stream/)

Die Medien-Playlist wird in *partielle Segmente* (*parts*, `#EXT-X-PART`-Direktiven) aufgeteilt: Der Player beginnt die Wiedergabe, ohne auf das fertige vollständige Segment zu warten. Verwendet werden ein blockierendes Neuladen der Playlist (der Server hält die Anfrage zurück, bis der nächste part bereit ist) und der Preload-Hinweis `#EXT-X-PRELOAD-HINT`.

Die Ziel-Dauer eines part wird über die Stream-Einstellung *Part Target Duration* festgelegt (ms; Standard 500, Bereich 100–5000). Der Wert wird im laufenden Betrieb übernommen, ohne den Stream neu zu starten, und muss kleiner als *Chunk Min Interval* sein.

**HLS / DASH / LL-HLS Adaptive Multistream**

HLS Adaptive Multistream wird seit Version 1.10 unterstützt, DASH Adaptive Multistream seit Version 1.12 (seit Version 1.13 — über CMAF), Low-Latency HLS Adaptive seit Version 1.13.

Für adaptive Streams wird eine separate Playlist konfiguriert. Dazu ist Folgendes nötig:

- OTT-Auslieferung bei den Streams aktivieren, die in die adaptive Playlist aufgenommen werden: *OTT/HLS* — für adaptives **HLS** über MPEG-TS; *OTT/HLS/LL-HLS/LL-Dash* — für adaptives **DASH** / **Low-Latency HLS** über CMAF.
- Im Hauptmenü erscheint ein Bereich für adaptive Streams. Dort muss ein Stream hinzugefügt und alle Streams angegeben werden, die in diese Playlist aufgenommen werden sollen.
- Für Streams kann ein Bitrate-Parameter gesetzt werden. Standardmäßig ist er 0 — die Bitrate wird aus dem gemessenen Wert übernommen; andernfalls kann sie explizit gesetzt werden.

Für adaptive Playlists gilt eine andere URL:

- [http://host:port/hls/adaptive/stream/login/password](http://host:port/hls/adaptive/stream/login/password)
- [http://host:port/hls/adaptive/stream/login](http://host:port/hls/adaptive/stream/login)
- [http://host:port/hls/adaptive/stream/](http://host:port/hls/adaptive/stream/)
- [http://host:port/dash/adaptive/stream/login/password](http://host:port/dash/adaptive/stream/login/password)
- [http://host:port/dash/adaptive/stream/login](http://host:port/dash/adaptive/stream/login)
- [http://host:port/dash/adaptive/stream/](http://host:port/dash/adaptive/stream/)
- [http://host:port/llhls/adaptive/stream/login/password](http://host:port/llhls/adaptive/stream/login/password)
- [http://host:port/llhls/adaptive/stream/login](http://host:port/llhls/adaptive/stream/login)
- [http://host:port/llhls/adaptive/stream/](http://host:port/llhls/adaptive/stream/)

Peers (Clients) können — wie bei normalen Streams — Zugriffsbeschränkungen auf adaptive Streams haben. Eine Berechtigung für einen adaptiven Stream schließt die Berechtigung für alle enthaltenen Substreams ein.

## HTTP/3 (QUIC)

Seit Version 1.13.1.438 enthält **Perfect Streamer** einen integrierten **HTTP/3**-Server für die OTT-Auslieferung von HLS, MPEG-DASH und Low-Latency HLS über **QUIC** (RFC 9000 + RFC 9114). Der Stack ist **ngtcp2** (QUIC v1) + **nghttp3** (HTTP/3 frame layer); die TLS-Infrastruktur verwendet dasselbe Zertifikat wie der HTTPS-Listener.

QUIC bedient ausschließlich OTT-Routen:

- `/` — Root-Redirect,
- `/hls/...`, `/dash/...` und `/llhls/...` — Master-Playlists / MPD,
- `/h<sessID>/...` — Per-Session-URL (Media-Playlists, Segmente, VTT),
- `/http/<stream>/...` — Raw-MPEG-TS über HTTP.

Low-Latency HLS und DASH werden über QUIC inkrementell (chunked) ausgeliefert: *parts* gehen an den Client, sobald sie bereit sind, ohne auf das vollständige Segment zu warten.

Administrative Pfade (`/data`, `/config`, `/xmltv`, `/db/`, `/login`, `/logout`, `/restart`) liefern über QUIC **404** — die Admin-API verbleibt auf HTTPS / HTTP-TCP. Dies ist eine bewusste Einschränkung zur Reduzierung der Angriffsfläche des QUIC-Listeners.

### Aktivierung

Die QUIC-Einstellungen befinden sich im Abschnitt **Configuration / HTTP server** (Knoten `/config/http-server`):

- **HTTP/3 Enable** (`http3-enable`) — globales Flag zur Aktivierung des QUIC-Listeners. Standardwert: **off**. Die Aktivierung öffnet einen UDP-Socket an `http3-port`; die Deaktivierung schließt ihn.
- **HTTP/3 Port** (`http3-port`) — UDP-Port des QUIC-Listeners. Standardwert: **43984**. QUIC läuft auf UDP, HTTPS auf TCP; die Ports können übereinstimmen oder abweichen — ein Konflikt zwischen ihnen besteht nicht.
- **HTTP/3 0-RTT Enable** (`http3-zero-rtt-enable`) — erlaubt den 0-RTT-Handshake (RFC 9001 §4.6.1) bei wiederaufgenommenen Verbindungen desselben Clients. Reduziert die Start-Latenz; das Replay-Risiko ist bei nicht idempotenten Anfragen zu berücksichtigen (für rein lesende OTT-Last unbedenklich). Standardwert: **on**.

Konfigurationsänderungen werden im laufenden Betrieb angewendet, ohne Neustart des Dienstes.

Zertifikat und Schlüssel werden vom HTTPS-Listener übernommen — es gibt keine separate Zertifikatskonfiguration für HTTP/3. Ist HTTPS nicht konfiguriert (`ssl-enable=false`), liefert die Aktivierung von HTTP/3 nichts — der Handshake scheitert.

### Der Parameter `?h3` — Opt-in pro Sitzung

Ein Browser verbindet sich nicht direkt mit einem HTTP/3-Endpunkt — er geht zunächst über HTTPS/TCP, empfängt im Response den Header `Alt-Svc: h3=":<port>"; ma=86400` (RFC 7838) und schaltet erst nachfolgende Anfragen an diesen Origin auf QUIC um.

In Perfect Streamer ist die Ausgabe von `Alt-Svc` ein **Opt-in pro OTT-Sitzung**. Der Server kündigt QUIC nicht jedem Client unterschiedslos an: Der Client muss HTTP/3 explizit über den Query-Parameter `?h3` auf der HLS-/DASH-Master-URL anfordern.

| Wert in der URL | Opt-in-Wert | `Alt-Svc` im Response |
| --- | --- | --- |
| Parameter fehlt | off (default) | nein |
| `?h3` (ohne Wert) | on | ja |
| `?h3=1`, `?h3=on`, `?h3=yes`, `?h3=true` | on | ja |
| `?h3=0`, `?h3=off`, `?h3=no`, `?h3=false` | explicit off | nein (wie bei fehlend) |

Sobald der Client die Session-URL `/h<sess>/...` erhalten hat, wird das `wantH3`-Flag in der Sitzung gespeichert — alle nachfolgenden Media-Playlist-Refreshs, Segment-GETs und VTT-Chunk-GETs unter dieser Session-URL erhalten **ebenfalls** `Alt-Svc`, auch wenn `?h3` in der jeweiligen Anfrage nicht vorhanden ist. Ohne Opt-in auf dem Master entsteht kein Sticky-Verhalten.

`Alt-Svc`-Gating:

1. Der QUIC-Listener ist global aktiviert (`http3-enable=true`); andernfalls wird die Ankündigung selbst bei gesetztem `?h3` unterdrückt.
2. Die Anfrage kam **nicht** über QUIC — bei Anfragen, die bereits über H3 laufen, ist `Alt-Svc` sinnlos und wird nicht ausgegeben.
3. Per-Session- oder Per-URL-`wantH3=true` (siehe Tabelle oben).
4. Der Admin- und der EPG-Server geben niemals `Alt-Svc` aus — sie verfügen über keinen QUIC-Listener.

Dieses Verhalten ist mit RFC 7838 §3 konform — der Server entscheidet selbst, bei welchen Antworten `Alt-Svc` ausgegeben wird.

### Szenario der QUIC-Umschaltung im Browser

Typischer Ablauf (Chrome / Firefox / Safari):

1. Der Player öffnet die Master-URL mit explizitem Opt-in:

   ```
   https://stream.example.com:41982/hls/test1/login/password/index.m3u8?h3=1
   ```
2. Die erste Anfrage erfolgt über HTTPS/TCP. Im Response gibt der Server `Alt-Svc: h3=":43984"; ma=86400` aus.
3. Der Browser merkt sich die Zuordnung `stream.example.com:41982 → h3=":43984"`. In Chrome lässt sie sich auf der Seite `chrome://net-internals/#alt-svc` einsehen; in Firefox — `about:networking#http3`.
4. Bei nachfolgenden Anfragen an denselben Origin (Playlist-Refresh, Segment-GETs, VTT) öffnet der Browser eine QUIC-Verbindung auf `udp/43984` und kommuniziert weiter über HTTP/3. In den DevTools → Network wird die Spalte **Protocol** für diese Anfragen `h3`.

Lässt sich die QUIC-Verbindung nicht aufbauen (UDP-Port am Firewall blockiert, Zertifikat im System nicht vertrauenswürdig), bleibt der Browser transparent auf HTTPS/TCP — der Stream läuft funktional ohne Unterbrechung weiter.

### Client-Accounting und Monitoring

Die reale IP-Adresse eines QUIC-Clients im Accounting aktiver Peers (`/data/http-clients`, Limits konkurrierender Verbindungen, IP-basierte ACLs) ist die **tatsächliche Peer-Adresse** der QUIC-Verbindung und nicht der Loopback der internen Backend-Brücke.

Das Attribut `ott-type` in `/data/http-clients` ist zusammengesetzt, im Format `<PROTO>/<scheme>`:

- `PROTO` — OTT-Protokoll: `HLS`, `DASH` oder `HTTP`.
- `scheme` — der tatsächliche Netzwerktransport: `http`, `https` oder `quic`.

Beispiele: `HLS/quic`, `DASH/https`, `HTTP/http`. Das Admin-UI zeigt sowohl das OTT-Protokoll als auch den Netzwerktransport jedes Clients in einer einzigen Spalte.

Das OTT-Sitzungs-Timeout beträgt **60 Sekunden**, unabhängig vom Transport. Wechselt ein Client zwischen Transporten, wird das Schema ausschließlich entlang der Prioritätskette `http` → `https` → `quic` aufgewertet. Ein paralleler Rückfall auf eine weniger gesicherte Verbindung überschreibt den aktuellen Typ nicht — im Accounting verbleibt der sicherste beobachtete Transport.

### Player-Kompatibilität

HTTP/3 für OTT unterstützen:

- **iOS AVPlayer / Safari** — nativ, über `Alt-Svc`; 0-RTT wird unterstützt.
- **Chrome, Firefox, Edge, Brave** — über `Alt-Svc`; HLS wird mit hls.js, DASH mit dash.js / Shaka wiedergegeben; der Player-Traffic erbt HTTP/3 vom Browser-fetch-API.
- **Android ExoPlayer** — über die Cronet-QUIC-Engine (nicht der Standardtransport; erfordert clientseitige Konfiguration).

**VLC** (libVLC) unterstützt HTTP/3 nicht — auf diesen Clients funktioniert der Stream weiter über HTTPS/TCP, ohne den QUIC-Vorteil.

Der praktische Vorteil von QUIC gegenüber HTTPS/TCP zeigt sich am deutlichsten in Mobilfunknetzen / Wi-Fi / verlustbehafteten Netzen:

- kein Head-of-Line-Blocking auf TCP-Ebene — ein Verlust in einem HTTP-Stream blockiert die übrigen nicht;
- 0-RTT-Handshake bei wiederaufgenommenen Verbindungen desselben Clients;
- stabilere ABR-Bitrate bei Paketverlusten von 1–5 %.

In verwalteten Netzen / Unternehmens-Wi-Fi fällt der Gewinn meist bescheiden aus — 5–10 % bei der Startup-Latenz und nahezu null im Steady-State. Die CPU-Last auf dem Server ist bei QUIC höher als bei Kernel-TCP — der Preis für TLS und Transport im User Space.

## Caching-Modell für OTT HLS und DASH

Der Server liefert Antworten in drei Kategorien, die sich in Inhaltslebensdauer und Cache-Eignung in Zwischenknoten (Reverse Proxy, CDN, Client-Cache) unterscheiden.

### 1. Caching-Modell

#### 1.1. Ressourcen und HTTP-Header

| Ressource | URL | Content-Type | Cache-Control |
| --- | --- | --- | --- |
| TS-Segment (HLS) | `/h<sess>/<keyHex>.ts`, `/h<sess>/sub/<pid>/<keyHex>.vtt` | `video/mp2t` | `public, max-age=60, immutable` |
| fMP4-Segment (DASH / LL-HLS) | `/h<sess>/init.mp4`, `/h<sess>/<key\|N>.m4s` | `video/mp4` | `public, max-age=60, immutable` |
| DASH MPD | `/h<sess>/index.mpd` | `application/dash+xml; charset=utf-8` | `public, max-age=1` |
| HLS master | `/hls/<stream>/<login>/<pass>/index.m3u8` | `application/vnd.apple.mpegurl` | `public, max-age=1` |
| HLS media | `/h<sess>/index.m3u8`, `/h<sess>/sub/<pid>/index.m3u8` | `application/vnd.apple.mpegurl` | `public, max-age=1` |
| 302 Redirect | `/dash/<stream>/<login>/<pass>/index.mpd` | — | `no-cache, no-store` |
| Raw TS | `/http/<stream>/<login>/<pass>` | `video/mp2t` | nicht gesetzt; nicht gecacht |

#### 1.2. Segmenteigenschaften

Der hexadezimale Segment-Identifier in der URL (`<keyHex>` in den Pfaden `/h<sess>/<keyHex>.ts`) wird als CRC64 über die Segment-Startzeit und die Stream-ID gebildet und ist global eindeutig. Die Segment-URL adressiert unveränderliche Inhalte — bei wiederholten Anfragen an dieselbe URL wird ein identischer Bytestrom zurückgegeben (solange das Segment innerhalb des Schiebefensters bleibt).

Die Direktive `immutable` unterdrückt die bedingte Revalidierung durch den Client (`If-None-Match`, `If-Modified-Since`). `max-age=60` ist mit dem typischen `timeShiftBufferDepth=40s` kompatibel.

CMAF-fMP4-Segmente (`.m4s`) und die gemeinsame `init.mp4` für **DASH** / **Low-Latency HLS** werden analog adressiert und nach demselben Modell zwischengespeichert (`immutable`, `max-age=60`). Partielle Segmente (*parts*) von LL-HLS sind Byte-Range-Anfragen in dieselbe `.m4s`, sodass sie keinen separaten Cache-Eintrag bilden.

#### 1.3. Eigenschaften der Manifeste

`max-age=1` begrenzt die obere Schranke der Inhalts-Veralterung im Cache auf eine Sekunde. Zusammen mit `proxy_cache_lock on` (nginx) werden Anfragenspitzen am Manifest zu einer einzigen Origin-Anfrage pro Sekunde zusammengefasst.

#### 1.4. Inhaltsvariabilität

Bei `absPath=0` (Standard; ohne URL-Parameter `a`) enthalten HLS-media- und DASH-MPD-Manifeste keinen Sitzungs-Identifier im Body. Der Manifest-Inhalt ist zwischen Sitzungen identisch, die zur selben (stream, param)-Kombination gehören. Dadurch kann ein Reverse-Proxy-Cache bei normalisiertem Cache-Key einen einzigen Eintrag sitzungsübergreifend wiederverwenden.

Bei `absPath=1` (URL-Parameter `a=1`) enthält der Manifest-Body absolute URLs einschließlich Schema, Host und Sitzungs-Identifier. Der Inhalt wird sitzungsspezifisch; eine sitzungsübergreifende Cache-Wiederverwendung ist nicht möglich.

### 2. Client-Verhalten

| Klient | Manifest-Refresh-URL | Auswirkung auf die Sitzungszahl |
| --- | --- | --- |
| VLC 3.x HLS | `/h<sess>/index.m3u8` | Eine Sitzung pro Wiedergabe |
| VLC 3.x DASH | `/dash/<stream>/.../index.mpd` | Wird per Session-Reuse behandelt (siehe 3.3) |
| ffmpeg 5.x HLS | `/h<sess>/index.m3u8` | Eine Sitzung pro Wiedergabe |
| ffmpeg 5.x DASH | `/dash/<stream>/.../index.mpd` (Wiederholungsschleife) | Wird per Session-Reuse behandelt (siehe 3.3) |
| dash.js, hls.js | `/h<sess>/...` über `<Location>` / Session-URL | Eine Sitzung pro Wiedergabe |

### 3. Spezielle Mechanismen

#### 3.1. HTTP 302 Redirect für DASH

Eine Anfrage der Form `/dash/<stream>/<login>/<pass>/index.mpd` liefert die Antwort `302 Found` mit dem Header `Location: /h<sess>/index.mpd`. Der Antwort-Body ist leer. Authentifizierung und Sitzungs-Allokation finden in der Phase der Redirect-Verarbeitung statt.

Clients, die Redirect-Caching unterstützen, greifen in nachfolgenden Anfragen direkt auf die Sitzungs-URL zu. Clients ohne Unterstützung wiederholen die Redirect-Anfrage. Die Kosten der Redirect-Wiederverarbeitung beschränken sich auf Authentifizierungsprüfung und Session-Reuse-Operationen.

#### 3.2. Session reuse für DASH

Bei der Verarbeitung einer `/dash/.../index.mpd`-Anfrage desselben Logins an denselben Stream (mit demselben *adaptive*-Merkmal) findet der Server eine bereits bestehende DASH-Sitzung und gibt deren Kennung erneut zurück. Es wird keine neue Sitzung erstellt; ein Platz im Limit gleichzeitiger Verbindungen wird nicht verbraucht.

Gilt nur für DASH. Für HLS ist kein eigener Reuse-Mechanismus erforderlich: HLS-Clients aktualisieren die Media-Playlist über die Session-URL und erzeugen bei jedem Refresh keine neue Sitzung.

#### 3.3. Segmentwiederverwendung zwischen Sitzungen

Der Pfad `/h<sess>/<keyHex>.ts` ist unabhängig von `<sess>`, wenn `<keyHex>` auf Inhalt aufgelöst wird: `<keyHex>` identifiziert ein TS-Segment innerhalb eines Streams global eindeutig. Nginx mit einem normalisierten Cache-Key (Entfernen des Präfixes `/h<sess>/`) bedient jede Anfrage nach demselben `<keyHex>` aus einem einzigen Cache-Eintrag, unabhängig davon, welche Clients sie gestellt haben.

Dasselbe gilt für CMAF-fMP4-Segmente (`.m4s`) und `init.mp4`: Ihr Inhalt ist unveränderlich und wird innerhalb des Streams adressiert, sodass die Normalisierung des Cache-Keys dieselbe sitzungsübergreifende Deduplizierung im Cache ergibt.

### 4. Anfrageparameter

| Parameter | Standardwert | Auswirkung |
| --- | --- | --- |
| `a` | `0` | `1` — absolute URLs in den Manifesten; `0` — relative |
| `s` | `40` | `timeShiftBufferDepth` in Sekunden |
| `m` | `40` | Mindestfensterlänge für die Manifest-Ausgabe |
| `v` | `6` für *OTT/HLS* und *OTT/HLS/LL-HLS/LL-Dash*, `3` für *Peering/HLS* | `#EXT-X-VERSION` in HLS (von DASH ignoriert); ein expliziter Wert in der URL überschreibt den Default |
| `h3` | fehlt (off) | Opt-in für HTTP/3 (QUIC) — veranlasst den Server, `Alt-Svc` im Response auszugeben. Erkannte Werte: presence ⇒ on, `1`/`on`/`yes`/`true` ⇒ on, `0`/`off`/`no`/`false` ⇒ explicit off. Sticky auf der Session-URL `/h<sess>/...`. Siehe Abschnitt [HTTP/3 (QUIC)](ott.md#http3). |

Das Ändern eines Parameters per Query-String aktualisiert die in der Sitzung gespeicherten Werte beim nächsten erneuten Öffnen der Sitzung.

### 5. Lasteigenschaften

Die Origin-Last skaliert mit der Anzahl gleichzeitig beobachteter unterschiedlicher Streams. Die Erhöhung der Anzahl gleichzeitig denselben Stream beobachtender Clients erhöht die Anzahl der Origin-Anfragen nicht, sofern ein Reverse-Proxy-Cache mit normalisiertem Cache-Key vorhanden ist.

| Szenario | Origin-Request-Rate (Ref.) |
| --- | --- |
| 1 Client pro Stream X | MPD: 0.4 req/s, segment: 0.2 req/s |
| N Clients auf einem Stream X (Cache aktiv) | MPD: 1 req/s, segment: 0.2 req/s |
| N ffmpeg-Clients im Replay-Modus auf einem Stream | MPD: 1 req/s (mit `proxy_cache_lock`) |
| N Clients auf N verschiedene Streams | MPD: 0.4·N req/s, segment: 0.2·N req/s |

### 6. Nginx als zwischenspeichernder Reverse Proxy

#### 6.1. Basiskonfiguration

```nginx
proxy_cache_path /var/cache/nginx/pss_segments
    levels=1:2 keys_zone=pss_segments:100m
    max_size=20g inactive=30m use_temp_path=off;

proxy_cache_path /var/cache/nginx/pss_manifests
    levels=1:2 keys_zone=pss_manifests:10m
    max_size=256m inactive=5m use_temp_path=off;

upstream pss_backend {
    server 127.0.0.1:41972;
    keepalive 64;
}

map $uri $pss_cache_key {
    ~^/h[0-9a-f]{16}(?<tail>/.+\.(ts|m3u8))$  "stream:$tail";
    default                                     $uri;
}

server {
    listen 80;
    server_name stream.example.com;

    location ~* "^/h[0-9a-f]{16}(/[0-9]+)?/([0-9a-f]+\.(ts|m4s)|init\.mp4)$" {
        proxy_cache               pss_segments;
        proxy_cache_key           $pss_cache_key;
        proxy_cache_valid         200 60s;
        proxy_cache_valid         404 403 0s;
        proxy_cache_lock          on;
        proxy_cache_use_stale     updating error timeout;
        proxy_cache_revalidate    on;
        add_header                X-Cache-Status $upstream_cache_status;

        proxy_pass                http://pss_backend;
        proxy_http_version        1.1;
        proxy_set_header          Connection "";
        proxy_buffering           on;
    }

    location ~* "(^/h[0-9a-f]{16}(/[0-9]+)?/index\.(m3u8|mpd)$|^/(hls|dash)/.*\.(m3u8|mpd)$)" {
        proxy_cache               pss_manifests;
        proxy_cache_key           $pss_cache_key;
        proxy_cache_valid         200 1s;
        proxy_cache_valid         404 403 0s;
        proxy_cache_lock          on;
        proxy_cache_lock_timeout  2s;
        proxy_cache_use_stale     updating;
        add_header                X-Cache-Status $upstream_cache_status;

        proxy_pass                http://pss_backend;
        proxy_http_version        1.1;
        proxy_set_header          Connection "";
    }

    location / {
        proxy_pass                http://pss_backend;
        proxy_http_version        1.1;
        proxy_set_header          Connection "";
        proxy_set_header          X-Forwarded-Proto $scheme;
        proxy_set_header          X-Forwarded-Host  $host;
        proxy_buffering           off;
        proxy_read_timeout        3600s;
    }
}
```

#### 6.2. Zweck der Direktiven

| Direktive | Zweck |
| --- | --- |
| `proxy_cache_lock on` | Serialisiert Upstream-Anfragen bei parallelen Cache-Misses auf denselben Schlüssel |
| `proxy_cache_use_stale updating` | Liefert die veraltete Kopie an parallele Anfragen während der Cache-Aktualisierung |
| `proxy_cache_revalidate on` | Nutzt `If-Modified-Since` bei Cache Miss mit vorhandener Kopie |
| `proxy_cache_valid 404 403 0s` | Verbietet das Caching von Authorization-Fehlern und 404 |
| `keepalive 64` im Upstream | Verwaltet einen Pool persistenter Verbindungen zum Origin |
| `proxy_buffering on` | Für Segmente; aktiviert die Antwortpufferung in nginx |
| `proxy_buffering off` | Für den Bereich `/`; deaktiviert Buffering (Raw Streaming) |

#### 6.3. Berechnung von `max_size` für den Segment-Cache

Richtwert: `bitrate × timeShiftBufferDepth × distinct_streams × 2`

Beispiel: 10 Streams × 8 Mbps × 40 s × 2 ≈ 800 MB. Es wird empfohlen, einen 10-fachen Sicherheitsfaktor für Bitratenschwankungen einzuplanen.

#### 6.4. TLS-Terminierung

Der Perfect-Streamer-Server akzeptiert Verbindungen auf HTTP- und HTTPS-Ports. Bei TLS-Terminierung am nginx verwendet das Upstream den HTTP-Port. Die Weiterleitung der Header `X-Forwarded-Proto` und `X-Forwarded-Host` ist erforderlich für die korrekte Bildung absoluter URLs bei `absPath=1`.

```nginx
server {
    listen 443 ssl http2;
    server_name stream.example.com;

    ssl_certificate           /etc/letsencrypt/live/stream.example.com/fullchain.pem;
    ssl_certificate_key       /etc/letsencrypt/live/stream.example.com/privkey.pem;
    ssl_protocols             TLSv1.2 TLSv1.3;
    ssl_session_cache         shared:SSL:10m;
    ssl_session_timeout       1d;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location ... {
        proxy_pass                http://pss_backend;
        proxy_set_header          X-Forwarded-Proto https;
        proxy_set_header          X-Forwarded-Host  $host;
        proxy_set_header          Host              $host;
        # + caching directives from 6.1
    }
}

server {
    listen 80;
    server_name stream.example.com;
    return 301 https://$host$request_uri;
}
```

Bei HTTPS zwischen nginx und Origin gelten `proxy_ssl_verify` und `proxy_ssl_trusted_certificate`. Bei Loopback-Verbindungen ist Verschlüsselung überflüssig.

#### 6.5. Multi-host

Wenn ein nginx-Prozess mehrere `server_name` bedient, wird `$host` zum Cache-Schlüssel hinzugefügt, um Inhalte zu isolieren:

```nginx
map $uri $pss_cache_key {
    ~^/h[0-9a-f]{16}(?<tail>/.+\.(ts|m3u8))$  "$host:stream:$tail";
    default                                     "$host:$uri";
}
```

Die `keys_zone`-Größe wird mit 8000 Keys/MB berechnet. Für Multi-Host-Installationen mit Tausenden von Streams werden `keys_zone=...:300m` oder mehr empfohlen.

### 7. Client-seitiges Caching

`Cache-Control: immutable` wird von den Browsern Chrome/Firefox/Safari berücksichtigt. Der Client-Cache liefert das Segment bei wiederholtem Zugriff ohne bedingte Anfrage zurück (auch bei Rückwärts-Seek innerhalb des Player-Buffers).

Service Workers können basierend auf dem Cache-Control-Inhalt eine `cache-first`-Strategie anwenden. DASH-Player (dash.js, Shaka) nutzen MSE über `SourceBuffer`; ein in den Buffer eingelegtes Segment bleibt ohne erneute HTTP-Anfrage verfügbar, bis es das Schiebe-Fenster verlässt.

Für Cross-Domain-Anfragen erlaubt der Header `Access-Control-Allow-Origin: *` Caching in shared caches ohne `Vary: Origin`. Beim Wechsel des ACAO-Werts auf einen konkreten Origin wird `Vary: Origin` erforderlich, was die Effizienz des shared cache reduziert.

### 8. Bereitstellung über CDN

Perfect Streamer ist mit Pull-from-Origin-CDNs kompatibel (Cloudflare, Akamai, Fastly, BunnyCDN, Amazon CloudFront).

**Origin shield.** Empfohlen werden ein oder mehrere Shield-Knoten zwischen CDN-Edge und Origin, um die Origin-Anfragerate bei global verteilten Clients zu senken.

**Purge.** Content-addressed Segmente erfordern keinen Purge. Bei Stream-Metadaten-Änderungen (Codec, Auflösung) aktualisieren sich Manifeste innerhalb von `max-age=1` ohne expliziten Purge.

**Cache Warming.** Bei erwartetem Lastanstieg auf einen Stream darf das CDN von mehreren Standorten aus vor Sendebeginn vorgeheizt werden.

**Geo-Verteilung.** Segmente (`max-age=60`) eignen sich gut für geografisch verteiltes Caching. Manifeste (`max-age=1`) tolerieren bis zu eine Sekunde Lieferverzögerung — akzeptabel für non-low-latency live.

### 9. Überwachung

#### 9.1. `X-Cache-Status`

`add_header X-Cache-Status $upstream_cache_status;` in jeder gecachten Location ergänzen. Werte:

| Wert | Beschreibung |
| --- | --- |
| `HIT` | Antwort aus Cache |
| `MISS` | War nicht im Cache; vom Origin geholt und gespeichert |
| `EXPIRED` | Abgelaufen, erneuert |
| `UPDATING` | Stale-Kopie an parallele Anfrage während der Aktualisierung ausgeliefert |
| `STALE` | `use_stale` lieferte die abgelaufene Kopie (Origin nicht erreichbar) |
| `REVALIDATED` | Origin lieferte 304 Not Modified |
| `BYPASS` | `proxy_cache_bypass` wurde ausgelöst |

#### 9.2. Format des access-log

```nginx
log_format pss_cache '$remote_addr $status $request_method "$request" '
                     '$body_bytes_sent rt=$request_time ut=$upstream_response_time '
                     'cache=$upstream_cache_status key=$pss_cache_key';

server {
    access_log /var/log/nginx/pss.log pss_cache;
}
```

#### 9.3. Metriken

Das Modul `nginx-vts` exportiert Per-Zone-Metriken im Prometheus-Format:

```
GET /status/format/prometheus
```

Empfohlene Schwellwerte für Alerts:

| Metrik | Schwelle | Mögliche Ursache |
| --- | --- | --- |
| Segment HIT rate | < 90 % über 5 Minuten | Cache-Key-Normalisierung defekt; `max_size` zu klein |
| Manifest MISS rate | > 50 % über 1 Minute | `proxy_cache_lock` serialisiert die Anfragen nicht |
| Upstream response time p95 | > 500 ms über 1 Minute | Origin-Überlastung |
| Cache zone fill | > 90 % über 10 Minuten | Annäherung an `max_size`; LRU-Eviction wird geplant |

### 10. Diagnose

| Symptom | Wahrscheinliche Ursache | Lösung |
| --- | --- | --- |
| Niedrige Segment-HIT-Rate | `Vary: Origin` mit hoher Origin-Variabilität; defekte Normalisierung in `map` | Header und Regex in der `map`-Direktive prüfen |
| 404 bei Segmenten nach Verlassen des Fensters | Gecachter 404 bei Segment, das aus dem Sliding Window gefallen ist | `proxy_cache_valid 404 0s` in der Segments-Location ergänzen |
| Playback-Start-Verzögerung 2–5 s | `proxy_cache_lock_timeout` überschreitet die Ziel-Latenz | Auf 1–2 s senken; `proxy_cache_use_stale updating` aktivieren |
| Manifest aktualisiert nicht | `proxy_cache_valid` überschreibt `max-age` | `proxy_cache_valid 200 1s` explizit setzen |
| Wachsendes TIME_WAIT am Upstream | `keepalive` fehlt im Upstream-Block | `keepalive 64`, `proxy_http_version 1.1` und `proxy_set_header Connection ""` hinzufügen |
| 403 bei `/dash/.../<segment>.m4s` von ffmpeg | Der Client löst relative URLs gegen die Pre-Redirect-URL auf | Der Server gibt `<BaseURL>/h<sess>/</BaseURL>` aus (absolute Pfade); im aktuellen Build kompatibel |
| Lags, häufiges Rebuffering bei entfernten Clients | Niedriger effektiver TCP-Durchsatz aufgrund von Slow Start und Idle Restart bei großen RTTs (300 ms und mehr) | Tuning des Linux-Netzwerk-Stacks auf dem Origin: siehe 10.1 |

#### 10.1. TCP-Tuning des Origin für High-RTT-Clients

Das Problem tritt bei Clients mit großem RTT zum Origin (z. B. 300 ms und mehr) auf, wenn die Stream-Bitrate nahe an der Kanalkapazität liegt. Symptome im Player (VLC, ffmpeg, dash.js) — häufiges Rebuffering, Warnungen wie `ES_OUT_SET_PCR called too late` (mit Anstieg von `pts_delay`), `buffer deadlock prevented`, Stream-Abbrüche. Auf dem Server wirkt der Client dabei normal, Fehler gibt es keine, und der Durchsatz auf `/data/stream/...` entspricht dem Eingangsstrom.

Ursache:

- **TCP slow start.** Jede neue TCP-Verbindung beginnt mit einem Congestion Window von etwa 14 KB und vergrößert es über mehrere RTTs. Bei einem RTT von 300 ms dauert das Erreichen des vollen Fensters 2-3 Sekunden. In dieser Zeit wird ein HLS/DASH-Segment von 5 s Dauer (4-6 MB) merklich langsamer als in Echtzeit heruntergeladen.
- **TCP idle restart.** Zwischen Segmentanfragen pausiert ein HLS-Pull-Modell-Client 4-5 s. Standardmäßig setzt der Linux-Kernel nach einer solchen Pause das Congestion Window der Verbindung auf das Initial cwnd zurück (Verhalten `net.ipv4.tcp_slow_start_after_idle=1`). Folglich beginnt die Keep-Alive-Verbindung beim nächsten GET die Übertragung erneut aus dem Slow Start — auch bei einer bereits aufgewärmten Sitzung.

Eine zusätzliche Verschärfung — das standardmäßige CUBIC-Congestion-Control kommt mit langen RTTs und Paketverlusten auf zwischengeschalteten Netzabschnitten schlecht zurecht.

Lösung — zwei sysctl-Parameter auf dem Origin:

```bash
# Keep congestion window across idle pauses inside keep-alive sessions.
sysctl -w net.ipv4.tcp_slow_start_after_idle=0

# Use BBR instead of CUBIC: better behaviour on long-RTT paths
# with mild packet loss; paces sending instead of bursting.
modprobe tcp_bbr
sysctl -w net.ipv4.tcp_congestion_control=bbr
```

Für eine dauerhafte Anwendung:

```bash
cat > /etc/sysctl.d/99-pss-net.conf <<EOF
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_congestion_control    = bbr
EOF
sysctl --system
```

Den Haupteffekt liefert der erste Parameter (`tcp_slow_start_after_idle=0`). Er beseitigt direkt den erneuten Slow Start zwischen Segment-Anfragen innerhalb einer Keep-Alive-Verbindung. Der zweite (BBR) bietet zusätzliche Robustheit und gilt für alle neuen Verbindungen.

Das Tuning erfordert keinen Neustart von Perfect Streamer und gilt unmittelbar nach `sysctl -w` für alle neuen TCP-Verbindungen. Bestehende Verbindungen behalten das Congestion Control, mit dem sie aufgebaut wurden.

### 11. Sicherheit

#### 11.1. Session URL

Eine URL der Form `/h<sess>/...` erfüllt die Funktion eines Sitzungs-Tokens — eine erneute Authentifizierung ist nicht nötig. Die Lebensdauer ist durch das idle timeout begrenzt (Wert 30 s). Bei Inaktivität wird die Sitzung von der cleaner-Aufgabe entfernt.

Anforderungen:

- HTTPS für alle OTT-Pfade (`/hls/`, `/dash/`, `/h<sess>/`) in Produktion
- Die Session-ID im `Location`-Header der 302 wird nicht gecacht (`no-cache, no-store`)

#### 11.2. Rate limiting

```nginx
limit_req_zone $binary_remote_addr zone=dash_top:10m  rate=5r/s;
limit_req_zone $binary_remote_addr zone=hls_top:10m   rate=5r/s;
limit_req_zone $binary_remote_addr zone=llhls_top:10m rate=5r/s;

server {
    location /dash/ {
        limit_req zone=dash_top burst=20 nodelay;
        proxy_pass http://pss_backend;
    }
    location /hls/ {
        limit_req zone=hls_top burst=20 nodelay;
        proxy_pass http://pss_backend;
    }
    location /llhls/ {
        limit_req zone=llhls_top burst=20 nodelay;
        proxy_pass http://pss_backend;
    }
}
```

Session-URLs (`/h<sess>/`) benötigen kein Rate Limiting — die Verarbeitung ist günstig, Antworten werden gecacht.

#### 11.3. Caching von Fehlerantworten

```nginx
proxy_cache_valid 200 60s;
proxy_cache_valid 301 302 0s;
proxy_cache_valid 404 403 0s;
proxy_cache_valid any 1s;
```

Verbietet das Caching von Redirects (eindeutiges sess in Location) und von Antworten mit Auth- oder Not-Found-Fehlern.

#### 11.4. Einschränkung des Netzwerkzugriffs auf den Origin

Port 41972 (41982 für HTTPS) muss für externen Verkehr geschlossen sein. Zulässige Konfigurationen:

1. Perfect Streamer an `127.0.0.1` binden (bei lokalem nginx)
2. Firewall-Regel:

```
iptables -A INPUT -p tcp --dport 41972 ! -s 10.0.0.0/8 -j DROP
```

### 12. Integration mit Middleware

#### 12.1. Das prefix-login-Modell

Perfect Streamer kann die Nutzeridentifikation über Prefix-Login an Middleware/Billing-Systeme delegieren. Ein externer Connector zum Billing-System ist im aktuellen Release nicht enthalten.

Konfiguration des Embedded-Benutzers:

```
{
  "id": 9,
  "login": "sub",
  "password": "xxx",
  "is-prefix": true,
  "max-conn-http-hls": 1,
  "accept-stream": [ ... ]
}
```

Mit `is-prefix: true` akzeptiert der Server URLs mit Logins der Form `<prefix><billing_user_id>`:

```
/dash/test1/sub42/xxx/index.mpd
/hls/test1/sub43/xxx/index.m3u8
```

#### 12.2. Statistikformat

```
<clients>
  <client login-id="-1974387287" login="sub" match-login="sub42"
          sess-id="11331..." ott-type="dash" stream-id="10000" .../>
  <client login-id="-2147031294" login="sub" match-login="sub43"
          sess-id="11132..." ott-type="dash" stream-id="10000" .../>
</clients>
```

Das Feld `login-id` enthält den Hash des URL-Logins. `login` ist der konfigurierte Wert. `match-login` ist der vom Client genutzte URL-Login.

#### 12.3. Einschränkungen von prefix-login

- **Gemeinsames Passwort.** Alle Teilnehmer des Prefix-Pools nutzen einen einzigen Passwortwert. Eine Kompromittierung des Passworts gewährt Zugriff auf jedes `<prefix><string>`.
- **ACL-Granularität.** `accept-stream` gilt für den gesamten Prefix-Pool; eine subscriber-spezifische ACL gibt es ohne externes Billing nicht.
- **Passwort-Rotation.** Eine Passwortänderung trennt alle aktiven Teilnehmer. Für eine schrittweise Umstellung sind vorübergehend zwei Prefix-Logins erforderlich.

### 13. WebVTT-Untertitel

Die Untertitelquelle ist DVB Teletext / DVB Subtitling aus dem Eingangs-MPEG-TS. In den Abschnitten **Media Information** oder **Original Media Information** müssen Teletext-Untertitel-Spuren vorhanden sein. Im Abschnitt **Analyzer** kann zusätzlich überprüft werden, dass Pakete der entsprechenden PIDs aktiv sind.

Für OTT HLS/DASH muss der OTT-Modus aktiviert sein (im *Peering/HLS* sind WebVTT-Untertitel nicht verfügbar). Im Abschnitt **Output # OTT** muss der Chunk-Zähler **OTT WebVTT buffer chunk count** einen Wert ungleich null aufweisen.

Zur Diagnose der Untertitel **Analyze** und **Trace** am Stream aktivieren. Beim Stream-Start sollte im Stream-Log Folgendes erscheinen:

```
Start Teletext subtitle decoder
[ttxsubdec] ttx: pid=331 magazine=8 page=0x88 lang=***
```

Im Folgenden wird der dekodierte Untertiteltext in das Log geschrieben.

#### 13.1. URL der VTT-Segmente

| Schema | URL | Inhalt |
| --- | --- | --- |
| HLS master | `/hls/.../index.m3u8` | `#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",...,URI="/h<sess>/sub/<pid>/index.m3u8"` |
| HLS subtitle playlist | `/h<sess>/sub/<pid>/index.m3u8` | Liste `<keyHex>.vtt` mit `#EXTINF` |
| HLS-VTT-Segment | `/h<sess>/sub/<pid>/<keyHex>.vtt` | VTT mit HLS-spezifischer X-TIMESTAMP-MAP |
| DASH MPD AdaptationSet | in `index.mpd` | `contentType="text" mimeType="text/vtt"` + `<SegmentTemplate media="$Number$.vtt">` |
| DASH-VTT-Segment | `/h<sess>/sub/<pid>/<seq>.vtt` | VTT mit DASH-spezifischer X-TIMESTAMP-MAP |

`<keyHex>` ist ein 16-stelliger Hex-CRC64 aus Segment-Startzeit, Stream-ID und Untertitelspur-PID. `<seq>` ist die dezimale laufende Nummer eines Untertitel-Stream-Chunks (die Untertitel-Nummerierung ist von der TS-Chunk-Nummerierung unabhängig).

## DVR / Archiv

Ab Version 1.13 bietet **Perfect Streamer** einen integrierten DVR — ein persistentes Stream-Archiv auf der Platte, das parallel zur normalen OTT-Ausgabe (HLS / DASH) läuft. Das Archiv wird automatisch geschrieben, ohne separaten Prozess, und über dieselben OTT-URLs wie Live wiedergegeben — Unterschied ist nur der Query-Parameter.

**Funktionen:**

- Aufzeichnung jedes OTT-Streams ins Archiv auf dem gewählten Speicher.
- HLS-, DASH- und Low-Latency-HLS-Wiedergabe des Archivs (VOD) über dieselben URLs wie live (DASH und LL-HLS — über CMAF, im *OTT/HLS/LL-HLS/LL-Dash*).
- Untertitel-Unterstützung (WebVTT) — neben TS-Chunks geschrieben.
- Mehrere Speicher — ein Stream wird an einen gebunden; verschiedene Streams können auf verschiedene Platten schreiben.
- Automatische Bereinigung nach Aufbewahrungszeit und Speicherauslastung.
- EPG-aligned VOD — Archivausgabe per EPG-Ereignis-Bezug.
- Adaptives VOD — wird für adaptive Gruppen unterstützt.

**DVR erfordert keine separate Lizenz.** Aktivierung pro Stream durch Hinzufügen einer Speicherbindung.

DVR ersetzt das Live-Broadcasting nicht. Hat ein Stream ein Archiv, erhält der Client eine Live-Playlist mit identischem Verhalten wie ohne DVR. Das Archiv beginnt erst dann zu spielen, wenn der Client den VOD-Modus explizit über einen URL-Query-Parameter anfordert (siehe unten).

### Speicher-Konfiguration

Ein Speicher ist ein Eintrag im Abschnitt **Configuration / DVR Storage**. Jeder Eintrag beschreibt ein Verzeichnis auf der Platte, in das PSS Archivdateien schreibt. Ein Stream nutzt einen Speicher.

Beim Hinzufügen eines Speichers werden konfiguriert:

**Name** — Anzeigename.

**Dir Path** — Pfad zum Verzeichnis auf der Platte. Nach Erstellung des Eintrags ist der Pfad **nicht änderbar** — um das Archiv zu verschieben, Eintrag löschen und neuen mit neuem Pfad anlegen. Bestehende Dateien werden beim Löschen **auf der Platte nicht angerührt**.

**Max Usage**, % — Schwellenwert der Auslastung (Standard 90 %). Bei Überschreitung startet die size-based Bereinigung (siehe unten). Minimum 1 %, Maximum 100 %.

**Cleanup Interval**, Sek — Periode der Bereinigungsaufgabe (Standard 10 Sek). Bei jedem Tick wird zunächst alles älter als die Aufbewahrungstiefe entfernt; danach — bei **Max Usage**-Überschreitung — alte Chunks.

**Disk Pressure Grace**, Sek — wie lange **Used %** **Max Usage** durchgehend überschreiten muss, bevor **Size-based cleanup** startet (Standard 60 Sek). Filtert kurze Spitzen.

**Disk Pressure Cut**, Sek — Obergrenze pro Bereinigungs-Tick: wie viele Videosekunden pro Stream auf einmal gelöscht werden dürfen (Standard 300 Sek). Der Rest geht in den nächsten Tick.

**Disk Emergency Bytes** — Schwellenwert für freien Speicher, unter dem der Speicher in *Error* übergeht und Aufzeichnung stoppt (Standard 2 GiB). Auto-Wiederherstellung bei freiem Platz ≥ 2 × diesem Wert.

**Alarm Disk-Full Hysteresis**, % — der Abstand unterhalb von **Max Usage**, bis zu dem die größenbasierte Bereinigung den Füllstand absenkt, damit **Used %** nicht direkt an der Schwelle schwankt (standardmäßig 2 %).

Die meisten Standardwerte passen für typische Installationen; Anpassungen sind meist nur für **Max Usage** und **Dir Path** nötig.

**Pro Platte ist es sinnvoll, einen Speicher anzulegen.** Werden auf derselben Platte mehrere Einträge mit verschiedenen Unterordnern angegeben, konkurrieren sie um freien Platz — die Platte ist gemeinsam, die Bereinigung läuft jedoch je Speicher.

### Stream an Speicher binden

In den **Stream / OTT**-Einstellungen erscheint ein **DVR**-Abschnitt:

**Storage** — Dropdown verfügbarer Speicher; *0* bedeutet „Archiv für diesen Stream deaktiviert“.

**Storage Hours** — Archivtiefe für diesen Stream, in Stunden, von 1 bis 2160 (bis zu 90 Tage). Chunks, die älter als dieser Wert sind, werden bei jedem Tick der Bereinigungsaufgabe (**Rolling cleanup**) gelöscht.

**Storage Min Hour** — untere Schutzschwelle (Stunden). Die Bereinigung löscht nie Chunks jünger als dieser Wert, auch nicht unter **Max Usage**-Druck. Verwenden, wenn die Geschäftslogik eine garantierte frische Aufzeichnung verlangt, z. B. „letzte 2 Stunden immer vorhanden“.

**Storage** zur Laufzeit ändern:

- *0* setzen — deaktiviert das Archiv; vorhandene Chunks bleiben **erhalten**, neue werden nicht geschrieben. VOD-Sitzungen liefern fortan 404;
- Auswahl eines anderen Speichers — der Stream wird vom alten gelöst und schreibt in den neuen. Dateien vom alten Speicher werden nicht migriert.

Änderungen an **Storage Hours** und **Storage Min Hour** wirken sofort — beim nächsten Tick verwendet die Bereinigung den neuen Wert.

Nach Konfiguration schreibt der Stream automatisch das Archiv, sobald er in den Zustand *Running* wechselt.

### VOD: Archivwiedergabe

Das Archiv wird über **dieselben URLs** wie Live HLS / DASH wiedergegeben (siehe Abschnitt *OTT-Service*) — nur der Query-Parameter unterscheidet sich.

#### URLs und Parameter

URLs für HLS, DASH und Low-Latency HLS (DASH und LL-HLS — über CMAF, erfordern den Modus *OTT/HLS/LL-HLS/LL-Dash*):

- [http://host:port/hls/stream/login/password/index.m3u8](http://host:port/hls/stream/login/password/index.m3u8)
- [http://host:port/dash/stream/login/password/index.mpd](http://host:port/dash/stream/login/password/index.mpd)
- [http://host:port/llhls/stream/login/password/index.m3u8](http://host:port/llhls/stream/login/password/index.m3u8)

Ohne Query-Parameter — normales Live mit **Sliding Window**. Mit *t*-Parameter — VOD-Modus.

| Parameter | Zweck |
| --- | --- |
| `t=<epoch>` | VOD-Startzeit (Unix epoch, Sek). *t=0* — ab Archivanfang. Das Vorhandensein von *t* (auch *t=0*) aktiviert den VOD-Modus. |
| `d=<sec>` | VOD-Fensterdauer, Sek. *d=0* oder Parameter fehlt — „bis zum aktuellen Moment“. Nur sinnvoll zusammen mit *t*. |
| `epg=<epoch>` | EPG-aligned VOD: Der Server findet selbst das zum angegebenen Moment aktive EPG-Ereignis und nimmt dessen *start* und *duration* als Fenstergrenzen. Inkompatibel mit *t* und *d* (server-seitige Ersetzung). Siehe unten. |
| `a`, `s`, `m`, `v` | Standardparameter für Live (siehe *OTT-Service*); `s` und `m` werden im VOD-Modus ignoriert. |

Verhalten nach *t* und *d*:

| t | d | Fenster | 404-Bedingung |
| --- | --- | --- | --- |
| nein | — | live (Sliding Window) | — |
| 0 | keiner / 0 | [Archivanfang, jetzt] | DVR nicht gebunden |
| 0 | > 0 | [Archivanfang, +d] | DVR nicht gebunden |
| > 0 | keiner / 0 | [t, jetzt] | DVR nicht gebunden |
| > 0 | > 0 | [t, t+d] | DVR nicht gebunden |

Grenzen-Normalisierung:

- *t* vor Archivanfang — start wird automatisch auf den ersten verfügbaren Chunk gezogen (Cleanup könnte schon getrimmt haben). Das ist **kein 404** — der Rest wird ausgeliefert.
- *t* in der Zukunft oder Fenster komplett vor dem Archiv — leere, aber gültige Playlist (HLS: nur Header + `EXT-X-ENDLIST`; DASH: `@type="static"`, `mediaPresentationDuration="PT0S"`).
- Chunks werden strikt anhand des Chunk-Startzeitpunkts ausgewählt, der in das halboffene Intervall *[t, t+d)* fällt — es gibt keine Teilsegmente.

VOD auf einem Stream ohne Archiv (oder dessen Speicher in *Error* ist) — **404** sofort auf Master-Playlist / MPD. Keine Sitzung wird angelegt.

Fehlertexte im 404-Body (in Server-Logs und HTTP-Body sichtbar):

- `VOD: stream not running` — Stream ist im Konfig, aber nicht in *Running*.
- `VOD: no DVR archive` — kein Speicher am Stream eingestellt oder Speicher in *Error*.
- `VOD: DVR detached` — Stream wurde zwischen Anfragen vom Speicher gelöst.
- `VOD: EPG event not found` — kein Ereignis für `?epg=` gefunden.

VOD-HLS-Playlist — **geschlossen** (Player sieht Dauer und kann spulen):

```
#EXTM3U
#EXT-X-VERSION:6
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:5.000,
...
#EXT-X-ENDLIST
```

In der Live-Playlist fehlen diese Marker — das ist der einzige Unterschied.

VOD-DASH-MPD — **statisch**: `@type="static"`, festes `mediaPresentationDuration`, explizite `<SegmentURL>`. Live-DASH bleibt `@type="dynamic"`.

Hat das gewählte Intervall **Lücken** im Archiv (z. B. durch Aufzeichnungs-Neustart oder mittendrin getrimmten Cleanup), wird das DASH-MPD automatisch in mehrere `<Period>` aufgeteilt — eines pro durchgehende Spur. Player (VLC, dashjs, Shaka) spulen über die Periodengrenze ohne Sonderkonfiguration.

#### EPG-aligned VOD

Ist der Stream mit einer EPG-Quelle verbunden (Felder **EPG Source** und **EPG Channel** in den Stream-Einstellungen), kann der Client das Archiv **anhand eines Zeitpunkts innerhalb eines EPG-Ereignisses** anfordern:

- [http://host:port/hls/stream/login/password/index.m3u8?epg=1778500000](http://host:port/hls/stream/login/password/index.m3u8?epg=1778500000)
- [http://host:port/dash/stream/login/password/index.mpd?epg=1778500000](http://host:port/dash/stream/login/password/index.mpd?epg=1778500000)

Der Server findet das beim angegebenen *epoch* aktive EPG-Ereignis und setzt dessen *start* und *duration* als VOD-Fenstergrenzen ein. Nützlich für Programmkataloge: Die UI kennt die Ereigniszeit, muss aber keine genauen Sek-/ms-Grenzen berechnen.

Ist der Stream nicht an EPG gebunden oder gibt es zu diesem Moment kein Ereignis — **404 ``VOD: EPG event not found``**. *t* und *d* werden bei vorhandenem *epg* ignoriert.

#### Adaptive Streams

Adaptive Gruppen (siehe [HLS Adaptive Multistream](ott.md#adaptive)) unterstützen dieselben VOD-Parameter:

- [http://host:port/hls/adaptive/group/login/password/index.m3u8?t=0](http://host:port/hls/adaptive/group/login/password/index.m3u8?t=0)
- [http://host:port/dash/adaptive/group/login/password/index.mpd?t=0](http://host:port/dash/adaptive/group/login/password/index.mpd?t=0)

In die Master-Playlist (HLS) gelangen nur Varianten mit konfiguriertem DVR-Speicher. Varianten ohne DVR werden übersprungen (im Log `VOD: variant N has no DVR`); der Aufbau aus den verbleibenden Varianten läuft weiter.

In der DASH-Variante wird jede Qualität zu einem eigenen `<Representation>` innerhalb gemeinsamer `<Period>`; der Player kann zwischen Qualitäten ohne Manifest-Reopen wechseln.

#### Player-Verhalten

VOD-HLS / DASH aus dem Archiv läuft in Standard-Playern: VLC, hls.js, dashjs, Shaka, ffmpeg. Timeline-Spulen funktioniert.

Fordert der Player ein Segment an, das der Cleanup bereits entfernt hat, gibt der Server **404 für dieses Segment** zurück (nicht 500). VLC, hls.js, dashjs überspringen das Segment und spielen vom nächsten weiter.

Zusätzliche Möglichkeiten:

- **Schutz aktiver Sitzungen**: Solange eine VOD-Sitzung offen ist, löscht der Cleanup keine Chunks im Fenster (siehe unten).
- **Live-edge bridge**: Fordert ein Client in einer VOD-Sitzung ein Segment jenseits der rechten Grenze des VOD-Fensters an — z. B. nach Erreichen des Archivendes vorwärts in der Timeline — liefert der Server das Segment automatisch aus dem Live-Speicher. Keine Weiterleitungen und keine erneute Autorisierung.
- **Playlist-Cache**: Auf wiederholte Anfragen derselben VOD `index.m3u8` / `index.mpd` liefert der Server eine byteidentische Antwort — ohne Neuaufbau. Geeignet für CDN vor PSS.

### Untertitel im Archiv

Trägt der Stream Untertitel (DVB Subtitling, Teletext oder WebVTT) und ist die Option **OTT WebVTT** aktiviert, werden Untertitel parallel zu den TS-Chunks archiviert — als `.vtt`-Dateien neben `.ts`.

Im Live-Modus enthält die Master-Playlist `EXT-X-MEDIA:TYPE=SUBTITLES`; im VOD-Modus liefert der Server eine **VOD-Untertitel-Playlist** (mit `ENDLIST`) und `.vtt`-Segmente unter denselben URLs.

Hinweise:

- **HLS**: Der `X-TIMESTAMP-MAP`-Header bleibt am Anfang jeder `.vtt` (per HLS-Spezifikation erforderlich).
- **DASH**: Der `X-TIMESTAMP-MAP`-Header wird im Flug entfernt (an absolute PCR gebunden, kollidiert mit dem DASH-Anker; sonst zeigt VLC leere Untertitel).
- In adaptiver Gruppe: Schreibt der aktive Sub-Stream keine Untertitel, liefern VTT-Segmente 404. Auf der nächsten Variante kann der Player wieder Untertitel erhalten.

Untertitel lassen sich entweder per **OTT WebVTT**-Option am Stream deaktivieren oder indem HLS im Modus *OTT/HLS* gar nicht aktiviert wird.

### Bereinigung und Retention

PSS verwendet vier Bereinigungsstrategien, die bei jedem Tick der Bereinigungsaufgabe laufen (Standard alle 10 Sek):

1. **Rolling cleanup** (pro Stream): Pro Stream werden Chunks älter als **Storage Hours** gelöscht. Läuft immer, auch bei halbleerer Platte.
2. **Size-based cleanup** (speicherweit): Wenn **Used %** durchgehend für **Disk Pressure Grace** Sek **Max Usage** überschreitet, werden die ältesten Chunks **proportional** über alle Streams des Speichers getrimmt. Pro Tick **Disk Pressure Cut** Sek Video je Stream. Nie Chunks jünger als **Storage Min Hour**.
3. **Emergency disk-full cut**: Fällt der freie Speicher unter **Disk Emergency Bytes**, läuft die Bereinigung aggressiv und kann auch sitzungsgeschützte Chunks löschen. Aufzeichnung stoppt, bis der freie Platz mit Hysterese × 2 wiederhergestellt ist.
4. **Orphan scan**: Auf der Platte können Dateien verbleiben, die nicht im Index erfasst sind (nach einem plötzlichen Stopp von PSS). Der Collector durchläuft die Stream-Unterverzeichnisse und löscht solche „vergessenen“ Dateien — der erste Lauf kurz nach dem Start des Dienstes, danach einmal pro Stunde und bei Auslösen von Disk Pressure. Als Schutz vor einem Race mit dem Schreiben werden Dateien jünger als 60 Sek. übersprungen.

**Hinweis.** Bereinigungsaufgaben laufen im Hintergrund; in der Regel ist kein Eingreifen nötig. Wächst das Archiv schneller als es getrimmt wird, **Storage Hours** auf den Streams senken oder **Disk Pressure Cut** erhöhen.

### Schutz aktiver VOD-Sitzungen

Wenn ein Client eine VOD-Sitzung öffnet, wird auf jedem beteiligten Speicher ein „Schutzslot“ mit Fenster-Startzeit registriert. **Rolling**- und **size-based**-Cleanup berühren Chunks im Fenster offener Sitzung nicht. Der Slot wird beim Schließen (FIN, Timeout) automatisch freigegeben.

Das heißt:

- Hält ein Client eine VOD-Sitzung lange, kann er garantiert zu jedem Moment im Fenster spulen — Chunks „verschwinden nicht unter ihm“.
- Die Bereinigung per **Max Usage** kann **Used %** vorübergehend nicht auf die Schwelle drücken, solange eine Sitzung aktiv ist; verlässt der Client den Stream, holt die Bereinigung auf.
- **Emergency disk-full cut** und **Storage Min Hour** umgehen den Schutz: Ist die Platte fast leer, werden Chunks gelöscht und der Client erhält 404 für die betroffenen Segmente (der Player überspringt).
- Nach PSS-Neustart verschwinden Schutzslots — Bereinigung läuft sofort weiter.

### Mehrere Speicher

Beliebig viele **DVR Storage**-Einträge sind möglich — je Verzeichnis / Platte einer. Streams werden unabhängig an verschiedene Speicher gebunden. Bereinigung und Schwellen (**Max Usage**, **Disk Pressure**) wirken per-storage.

Anwendungsfälle:

- **Tiering nach Wert**: SSD-Speicher für Premium-Kanäle mit großer Tiefe, HDD-Speicher hoher Kapazität für den Rest.
- **Eigene Platte für das Archiv**: damit die DVR-Aufzeichnung nicht mit Systemplatte oder Temp-Dateien konkurriert.
- **Projekttrennung**: eine Platte für Kanalpaket Nr. 1, eine andere für Nr. 2 — vereinfacht Migration und Audit.

Das Ändern der Stream-Bindung (Feld **Storage** in **Stream / OTT**) schaltet die Aufzeichnung im Betrieb um. Alte Dateien bleiben auf der bisherigen Platte unangetastet — sie lassen sich manuell löschen oder durch Rückumstellung wieder anbinden.

### Speicherzustand und Monitoring

Im Abschnitt **Data / DVR Storage List** (sowie über API `GET /data/dvr-storage-list`) werden pro Speicher angezeigt:

- **State** — *Ready* / *Error* (plus den Zwischenzustand *Not Ready* bei einem gerade hinzugefügten Storage).
- **Total / Free / Used Bytes** — freier und belegter Speicherplatz auf der Platte.
- **Used %** — aktueller Auslastungsanteil.
- **Archived Bytes** — Gesamtgröße der im Index aller gebundenen Streams erfassten Chunks (ohne Orphan-Dateien).
- **Pressure Since Sec** — Moment (epoch), zu dem **Used %** im aktuellen Druckepisode erstmals **Max Usage** überschritten hat; *0* heißt „kein Druck jetzt“.
- **Active Task** — die gerade laufende Wartungs-Hintergrundoperation: *gc-orphans* (Entfernen verwaister Dateien), *disk-pressure-trim* (Kürzen unter Plattendruck) oder *none*, sowie wie viele Sekunden sie bereits läuft. Kurzlebige (< 2 s) Operationen flackern in der UI nicht.
- **Last cleanup** — eine Zusammenfassung der letzten Bereinigungsläufe: wie lange der vorherige Einsammelvorgang verwaister Dateien zurückliegt (und wie viele davon gelöscht wurden) sowie das letzte Kürzen unter Speicherplatzdruck (wie viel Platz freigegeben wurde).
- **Attached streams** — die Liste der angebundenen Streams; für jeden werden der Name, der Indikator einer laufenden Aufzeichnung (*active*), die konfigurierte Archivtiefe (*retention-hours*) und der tatsächliche Füllstand (*archived-sec* und *archived-bytes*) angezeigt. Die Summe der *archived-bytes* über alle Streams entspricht den **Archived Bytes** des gesamten Speichers.

Zustände:

- *Ready* — das Verzeichnis ist verfügbar, Aufzeichnung und Bereinigung laufen normal.
- *Error* — das Speicherverzeichnis ist nicht verfügbar (ausgehängt, fehlende Rechte, Dateisystemfehler) oder es ist kritisch wenig freier Platz auf der Platte; die Aufzeichnung ist gestoppt. Die Wiederherstellung erfolgt automatisch — sobald das Verzeichnis wieder verfügbar ist und genügend freier Platz vorhanden ist.

Ist der Speicher in *Error* gegangen, prüfen Sie, ob das Archivverzeichnis eingehängt ist und ob auf der Platte freier Speicher vorhanden ist — PSS verlässt diesen Zustand nicht von selbst, solange das Problem nicht physisch behoben ist.

Archivfüllstand im Zeitverlauf:

- Auf Stream-Ebene (**Data / Stream**) steht die Metrik **storage-gap-percent** zur Verfügung — der Anteil zeitlicher Lücken im angesammelten Archiv: *0 %* bedeutet durchgehende Aufzeichnung, höhere Werte bedeuten, dass es Lücken im Archiv gibt (Neustarts der Quelle, durch die Bereinigung gekürzte Abschnitte).
- Der Endpoint `GET /data/dvrstat` liefert ein Histogramm der Archivfüllung nach Zeitintervallen mit Markierung von Aufnahmeereignissen (Start/Stopp der Quelle, **PMT**-Wechsel, Scrambling-Umschaltungen, Index-Neuaufbau, Bereinigungslauf) und Untertitel-Aktivität — zur Darstellung der DVR-Archiv-Zeitleiste in der Admin-Oberfläche.

### Schutz vor versehentlichem Datenverlust

Mehrere Operationen der Admin-Oberfläche führen zum **Verlust des aufgezeichneten Archivs** oder **Ende der VOD-Auslieferung**. Vor der Ausführung zeigt die Admin-UI eine modale Bestätigung:

- **DVR-Storage löschen** — alle gebundenen Streams verlieren VOD-Zugriff; Dateien bleiben auf der Platte, sind aber ohne Eintrag über PSS nicht erreichbar.
- **Stream auf anderen Speicher umstellen** — VOD über das alte Archiv funktioniert nicht mehr.
- **Stream vom Speicher lösen** (Storage = 0) — gleicher Effekt.
- **Max Usage senken** — kann size-based Bereinigung auslösen und alte Chunks löschen.
- **Storage Hours / Storage Min Hour senken** — kann Teile des Archivs aus dem Rolling-Fenster drängen.

Dies ist eine bewusste Produktentscheidung: Die Operation ist möglich, aber mit Bestätigung, und gelöschte Dateien bleiben auf der Platte (Restore aus Backup möglich). Das Archiv auf separatem Fileserver / RAID senkt das Risiko irreversiblen Verlusts deutlich.

### Einschränkungen der aktuellen Version

- Eine vom Client geöffnete VOD-Sitzung **verfolgt** die Live-Edge **nicht** — wenn während der Sitzung neue Chunks ins Archiv kamen, muss der Client die Playlist neu anfordern (Standard aller HLS / DASH Player).
- Pro Platte ist ein Speicher sinnvoll — mehrere Einträge mit verschiedenen Unterordnern konkurrieren um freien Platz.
- Segmente werden per Hash adressiert (HLS über MPEG-TS) oder über die Vorlage `$Number$.m4s` mit gemeinsamer `init.mp4` (live DASH über CMAF) / über explizite `<SegmentURL>` auf `.m4s` (VOD DASH). Eine Änderung der Chunk-Größe zwischen live und VOD erfordert kein erneutes Öffnen der URL.
- Der Orphan-Scanner läuft stündlich; zur Beschleunigung PSS neu starten.

## Stream-Operationen

**Löschen**. Zum Entfernen eines Streams in seine Einstellungen gehen und auf *Delete stream* klicken.

**Klonen**. Zum Klonen eines Streams in seine Einstellungen gehen und auf *Clone stream* klicken.

**Sortierung**. Klicken Sie auf die Schaltfläche *Sort* im Stream-Listen-Fenster, um die Stream-Sortierung zu konfigurieren. Geben Sie anschließend die gewünschte Reihenfolge an, indem Sie die Streams in der Liste nach oben oder unten ziehen. Klicken Sie auf *Save order*, um die festgelegte Sortierreihenfolge zu speichern, oder auf *Cancel*, um die Änderungen zu verwerfen.

**Listenfilterung**. Klicken Sie auf das Such-Symbol und geben Sie den Filter ein, um die Stream-Liste zu filtern. Mit dem Zurück-Pfeil heben Sie den Filter auf.

**Gruppen-Operationen**. Im Filter-Modus der Stream-Liste lassen sich mehrere Streams durch Aktivieren der Kontrollkästchen in der linken Spalte auswählen. Sind Streams ausgewählt, werden die Schaltflächen *Löschen* und *Klonen* für die ausgewählten Streams verfügbar.

### Export und Import von Streams per Python-Skript

Konfigurations-Export/-Import läuft über eine .m3u-Playlist und ein Python-Skript.

Python 3 muss unter /usr/bin/python3 verfügbar sein.

### Stream-Export und -Import über die Weboberfläche

Beim Klick auf die Schaltfläche *Playlist* in der Stream-Liste öffnet sich der Dialog zum Export der Streams in eine m3u8-Playlist. Exportiert werden nur die unter Berücksichtigung der angewandten Filter aktuell angezeigten Streams.

Einstellungen:

- **Host / IP** — Servername oder -adresse, die zum Aufbau der Stream-URLs verwendet wird.
- **Protocols** — Auswahl der Protokolltypen für den Export.
- **Login** — Auswahl des Kontos, das zur Bildung der Stream-URLs verwendet wird.
- **Use Display Names** — **Display name** des Streams anstelle des **Stream name** in der m3u8-Datei verwenden.

Die Playlist wird beim Klick auf **Download** als m3u8-Datei heruntergeladen.

Beim Klick auf die Schaltfläche **Import Playlist** wechselt der Dialog in den Modus zum Importieren von Streams aus einer m3u8-Playlist. Beim Import werden bestehende Streams nicht entfernt. Bei allen importierten Streams wird die Importzeit im Feld **Note** vermerkt.

Einstellungen:

- **Playlist** — Auswahl der Playlist-Datei für den Import.
- **Create Outputs** — Protokollauswahl für die automatische Erstellung von Ausgängen für importierte Streams.
- **Output Ports From** — Startportnummer für die Generierung der Ausgänge.
- **Output IP** — Auswahl der Schnittstelle, an die die Ausgangsstreams gebunden werden.
- **Tags** — Tags, mit denen importierte Streams markiert werden. Nützlich für Gruppenoperationen, z. B. das Löschen aller importierten Streams.

Ist im Feld **Playlist** eine zu importierende Datei angegeben, wird die Schaltfläche **Load Playlist** aktiv. Beim Klick auf **Load Playlist** wird die Playlist vorab geladen und das Parsing-Ergebnis in einer Tabelle angezeigt. Nach dem Parsing wird die Schaltfläche **Import Streams** aktiv — bei deren Klick werden die Streams importiert und Outputs für sie generiert.

## Berichte und Diagnose

Im Bereich **Streams** werden alle **stream**s als Tabelle angezeigt. Die **Pause**-Aktion steht den Rollen **admin** und **restricted admin** zur Verfügung.

Für jeden **stream** stehen detaillierte Statistiken und Berichte zur Verfügung.

**Peers** — Liste aktiver Empfänger (Clients). Für jeden gibt es separate Statistiken.

### Stream-Analyse

Der Streamer-Analyzer misst und analysiert verschiedene Parameter des MPEG-TS-Streams, was eine Qualitätsbewertung ermöglicht.

**Input speed** — Stream-Rate (Bitrate) in `kbps`. Wird nach der Filterung anhand der PCR-Marken gemessen. Wird als Diagramm dargestellt, das auch die Ausgangsbitrate nach dem Synchronisierer zeigt.

**Raw data speed** — die Empfangsdatenrate über das gewählte Protokoll. **Overhead** — der prozentuale Aufschlag für den Protokolloverhead.

**CC errors** — Paketverluste (CC, Discontinuity). Es werden Intervallzähler sowie ein kumulativer Zähler über die Stream-Uptime angezeigt. Zusätzlich wird eine grafische Historie dargestellt.

**Scrambled** — Zähler verschlüsselter MPEG-TS-ES-Pakete. Werte ≠ 0 bedeuten Ausfälle beim Entschlüsseln verschlüsselter Kanäle.

**Sync by** — Synchronisationsquelle — PCR.

**PCR interval** — Intervall zwischen PCR-Marken. Empfohlen: nicht mehr als 50 ms.

**PCR jitter** — Maß für die Synchronisationsgenauigkeit des Ausgangsstreams; gemessen als Differenz zwischen PCR und Echtzeit.

**Analyze PCR PMT Gap** — wird über eine eigene Einstellung aktiviert. Analysiert wird die Abweichung zwischen PCR und PTS/DTS für jeden ES. Die Historie wird grafisch dargestellt. Eine zu große Abweichung kann auf Playern mit kleinem Synchronisationspuffer zu Problemen führen. Die Analyse wird über die Einstellung **Analyze PAT/PMT/KF** aktiviert.

**PAT interval** und **PMT interval** — es wird das Intervall zwischen PAT-(PMT-)Tabellen gemessen. Empfohlen: nicht mehr als 500 ms. Die Analyse wird über die Einstellung **Analyze PAT/PMT/KF** aktiviert.

**Key Frame interval** (GOP-Intervall) — das Intervall zwischen Keyframes (GOP-Anfängen). Für Player, die an beliebiger Stelle in den Stream einsteigen, werden maximal 1 s empfohlen. Die Analyse wird über die Einstellung **Analyze PAT/PMT/KF** aktiviert.

**IDR interval** — das Intervall zwischen IDR-Frames, die echte Random-Access-Punkte sind (*SPS / PPS / IDR*). Es wird nur angezeigt, wenn die Quelle mit IDR sendet. Stimmt **IDR interval** ungefähr mit **Key Frame interval** überein, hat der Stream *closed-GOP*: Jeder GOP beginnt mit einem vollständigen Einstiegspunkt, und der Player kann ab jedem Segment starten. Fehlt die Metrik, ist der Stream *open-GOP* ohne IDR — es gibt Keyframes, sie sind aber keine vollständigen Einstiegspunkte. Aus diesem Verhältnis ist der GOP-Strukturtyp der Quelle sofort ersichtlich. Die Analyse wird über die Einstellung **Analyze PAT/PMT/KF** aktiviert.

**PAT/KF interval** — misst das mittlere Intervall zwischen Anfang und Ende der PAT/PMT/SPS/PPS/KF-Sequenz. Davon hängt die Startzeit der Wiedergabe für Player ab, die an beliebiger Stelle in den Stream einsteigen. Gemessen wird ab dem Beginn des KF im Stream; die tatsächliche Startzeit des Players ist daher länger. Die Analyse wird über die Einstellung **Analyze PAT/PMT/KF** aktiviert.

Aktivieren von **Analyze PAT/PMT/KF** schaltet den Stream-Analyzer dauerhaft ein, was die CPU-Last erhöht.

### Jitter-Steuerung

Für die Jitter-Steuerung gibt es im Stream mehrere Einstellungen:

- **Jitter Compensation Delay (ms)** — Funktion zur Kompensation des Netzwerk-Jitters; legt die Buffergröße fest. Ausführliche Beschreibung: [https://forum.pstreamer.tv/viewtopic.php?t=25](https://forum.pstreamer.tv/viewtopic.php?t=25)
- **Jitter Auto sync** — setzt 2000 ms für TCP-basierte Protokolle (HTTP, HLS); für UDP-Protokolle bleibt der Wert bei 500 ms.
- **Limit PCR gap (ms)** — prüft, wie weit der PCR springen darf; bei Überschreitung erfolgt eine Resynchronisation.

Treten nach dem Transkoder *PCR Accuracy*-Fehler auf, müssen Sie für den codierten Stream über *stuffing* eine gleichmäßige Bitrate auf folgendem Pfad festlegen: „**input** — **transcoder** — **Align Total Bitrate**“. Die Geschwindigkeit muss garantiert höher als die Bitrate von Video und Audio gewählt werden.

### PCR drift

Die Metrik *PCR drift* misst die systematische Abweichung der Quelltaktfrequenz gegenüber der Echtzeit und wird in *ppm* (parts-per-million) angegeben. Im Gegensatz zum momentanen *PCR jitter*, der das Zittern einzelner Zeitstempel erfasst, charakterisiert *PCR drift* gerade den Langzeit-Drift des Encoder-Quarzes — eine stetige Abweichung, die sich über Minuten und Stunden akkumuliert.

Die Messung erfolgt durch lineare Regression über Paare (kumulierte *PCR*-Zeit in Sekunden, Empfangszeit in Sekunden). Das Messrauschen sinkt mit wachsendem Fenster wie `1/T^1.5`, sodass ein belastbares Urteil nur über lange Intervalle möglich ist. Das Fenster wird automatisch bis auf 300 *Sekunden* erweitert (Abnahmefenster des Produkts; der Standard **TR 101 290** legt das Beobachtungsintervall nicht fest), danach wird eine gleitende Neuverankerung mit einem Limit von 6 *Stunden* angewendet — dadurch wird ein Präzisionsverlust durch Überlauf der `float64`-Mantisse ausgeschlossen.

In der **XML**-Statistik des Streams werden exportiert:

- `pcr-drift-ppm` — aktuelles Regressionsurteil;
- `pcr-drift-window-s` — Länge des Fensters, über das das Urteil berechnet wurde;
- `pcr-drift-samples` — Anzahl der Punkte in der Regression.

Die in **ISO/IEC 13818-1** §2.4.2.1 festgelegte Toleranz beträgt ±30 *ppm*. Bei anhaltender Überschreitung (siehe Abschnitt zu Alarmen unten) wird ein separates Attribut `pcr-drift-alert` gesetzt, und nach 300 *Sekunden* ununterbrochener Verletzung — `pcr-drift-alert-acceptance` (Abnahmestufe; das 300-Sekunden-Fenster ist ein Produktwert, im Sinne der Langzeitmessungen von **TR 101 290**).

Die Auslagerung von *PCR drift* in ein eigenes Attribut (statt in das gemeinsame `tr101290-alert`) ist beabsichtigt: Quarzdrift ist ein Problem des Upstream-Encoders, nicht des Empfängers, und der Betreiber muss es getrennt sehen, nicht mit Transportintegritätsfehlern vermischt.

### PCR accuracy

Die Metrik *PCR accuracy* (Abschnitt P2.3 in **TR 101 290**) misst die Genauigkeit der Einfügung von *PCR*-Zeitstempeln in den Transportstrom. Laut Spezifikation muss der Wert jedes *PCR* mit dem tatsächlichen Zeitpunkt seines Durchgangs durch den Multiplexer mit einer Abweichung von höchstens ±500 *ns* übereinstimmen.

Die Messung wird als *Residual* einer linearen Regression auf einem gleitenden Fenster von 30 *s* ermittelt. Die Fenstergröße ist so gewählt, dass der gesamte *PCR*-Wiederholzyklus (≤40 *ms* laut Spezifikation) mit großem Reservepuffer erfasst wird, ohne die schnelle Reaktion auf eine Verschlechterung der Einfügequalität zu opfern.

In der **XML**-Statistik des Streams wird `pcr-accuracy-max-ns` exportiert — der Spitzenwert des Residuals über das Fenster. Bei Überschreitung von ±500 *ns* wird dem gemeinsamen Attribut `tr101290-alert` das Token `pcr-acc` hinzugefügt.

Die Messung ist nur für **CBR**-Multiplexe sinnvoll: bei **VBR** können die Intervalle zwischen *PCR* vom errechneten linearen Trend abweichen, ohne den Standard zu verletzen. Der Analysator deaktiviert das Token `pcr-acc` automatisch für Streams, die als *vbr* klassifiziert sind (siehe Abschnitt zum Modus-Detektor).

### PCR-Drift-Kompensator

Liegt der *PCR drift* der Quelle innerhalb der **ISO/IEC 13818-1**-Toleranz, ist er jedoch nicht null, so „läuft“ der Ausgangsstrom langsam gegenüber der Netzwerkzeit des Empfängers weg. Bei einem Player mit kleinem Puffer äußert sich dies als akkumulierender Synchronisationsverlust oder periodische Wiedergaberuckler.

Der Kompensator beseitigt den Drift ohne harte Resynchronisation: ausgehende Pakete erhalten einen Mikroversatz von ~11.1 *µs* (Dauer eines einzelnen 188-Byte-*TS*-Pakets bei 100 *Mbps*), und die Entscheidung „ist ein Versatz nötig“ wird anhand der tatsächlichen Differenz zwischen Netzwerkzeit und *PCR*-Takt getroffen. Harte Resyncs (`Limit PCR gap`) bleiben als Fallback für Strom­abbrüche bestehen — der Kompensator arbeitet auf einer feineren Skala und erhält die Kontinuität.

Einstellungen unter **stream**:

- *Sync Drift Compensation* — aktiviert/deaktiviert den Kompensator. Standardmäßig aktiviert.
- *Sync Drift Soft Window* — weiches Fenster, innerhalb dessen Korrekturen einzeln angewendet werden (1–60000 *ms*, Standard 500). Die obere Grenze ist durch `8 × Sync Disc Window` begrenzt, ab dem ein harter Resync als fällig betrachtet wird.

In der **XML**-Statistik wird `slew-adjust-count` exportiert — der Zähler der Korrekturen seit `reset-stat`. Ein abrupter Anstieg dieses Zählers bedeutet, dass die Quelle entweder ihren Toleranzbereich verlassen hat oder einen instabilen Quarz besitzt.

### T-STD Video-Pufferanalysator

Das **T-STD**-Modell (*Target Decoder*) ist in **ISO/IEC 13818-1** §2.4.2 beschrieben. Es ist das Referenzmodell des Empfänger-Puffers: wird es eingehalten, ist garantiert, dass der Strom auf jedem konformen Hardware-Decoder wiedergegeben werden kann.

Der Analysator modelliert den `MBn`-Puffer (Multiplex-Buffer für Video) und prüft, dass:

- der Puffer nicht überläuft (overflow → der Encoder muss Frames verwerfen);
- der Puffer nicht leerläuft (underflow → der Decoder zeigt eine Pause oder Artefakte).

Wird durch die Einstellung *Analyze T-STD video* aktiviert (standardmäßig deaktiviert — verursacht CPU-Last im heißen Verarbeitungspfad).

Unterstützte Video-ES-Typen (`stream_type`):

- `0x01` / `0x02` — MPEG-2 video, Kapazität 750 *KB*;
- `0x10` — MPEG-4 part 2, Kapazität 750 *KB*;
- `0x1B` — H.264 / AVC, Kapazität 3 *MB*;
- `0x24` / `0x25` — HEVC, Kapazität 3.75 *MB*.

Die Abflussrate (*drain rate*) wird adaptiv an die tatsächliche Video-Bitrate angepasst: verwendet wird ein exponentiell gewichteter gleitender Mittelwert (EMA) mit `α=0.2` über ein 5-*s*-Fenster. Ohne Adaption liefert das Modell auf jedem *VBR*-Video schnell falsche Underflows.

Der Puffer wird nach **PCR**-Takten (90 *kHz*) entleert, nicht nach der Hostsystemzeit — dies macht die Analyse robust gegenüber Betriebssystem-Pausen und CPU-Lastschwankungen. Die Host-Echtzeit wird ausschließlich zur Verifikation des Drift-Kompensators verwendet, nicht für T-STD.

In der **XML**-Statistik werden exportiert:

- `tstd-video-cap` — Modellkapazität in Bytes;
- `tstd-video-drain-bps` — aktuelle adaptierte Abflussrate, *bytes/s*;
- `tstd-video-overflows` — Überlaufzähler seit `reset-stat`;
- `tstd-video-underflows` — Unterlaufzähler;
- `tstd-video-max-fill` — Spitzenfüllung in Bytes.

Bei jedem von Null verschiedenen Zähler (`overflows > 0` oder `underflows > 0`) wird dem gemeinsamen `tr101290-alert` das Token `tstd-video` hinzugefügt. Wie bei `pcr-acc` gilt das Token nur für **CBR**-Streams — bei einem **VBR**-Multiplex sind transiente Puffereinbrüche zulässig.

### Multiplex-Bitratenmodus-Detektor

Ein Teil der **TR 101 290**-Tests ist nur im **CBR**-Modus (konstante Multiplex-Bitrate) sinnvoll. Um keine falschen Alarme an **VBR**-Kanälen auszulösen, bestimmt der Analysator automatisch den Quellmodus und exportiert das Urteil in das Attribut `bitrate-mode-detected`.

Algorithmus: Vergleich der mittleren Bitraten über zwei Fenster — 5 *s* und 60 *s*. Übersteigt die Abweichung 20 %, wird der Strom als `vbr` markiert; bleibt sie darunter, als `cbr`. Bis genügend Statistik akkumuliert ist (~70 *s* ab Start oder `reset-stat`), ist das Urteil `unknown` und die **CBR**-only Tests (`pcr-acc`, `tstd-video`) sind vorübergehend unterdrückt.

Attributwerte:

- `cbr` — konstante Bitrate, alle Tests aktiv;
- `vbr` — variable Bitrate, `pcr-acc` und `tstd-video` sind deaktiviert;
- `unknown` — unzureichende Daten, **CBR**-only Tests sind ausgesetzt.

### TR 101 290 Alarme

Das Sammelattribut `tr101290-alert` vereint mehrere Konformitätsdetektoren nach **TR 101 290**. Spricht zum aktuellen Zeitpunkt mindestens einer an, enthält das Attribut eine durch Leerzeichen getrennte Token-Liste; spricht keiner an, fehlt das Attribut im **XML**.

Mögliche Token und ihre Bedeutung:

- `pcr-int` — Überschreitung des Intervalls zwischen *PCR* (Abschnitt 5.2.4 von **TR 101 290**, Grenzwert 40 *ms*, Empfehlung ≤25 *ms*);
- `pcr-acc` — Überschreitung der *PCR*-Einfügegenauigkeit (Abschnitt 5.2.5, ±500 *ns*, **CBR**-only);
- `pcr-disc` — ein harter Resync über *PCR* wurde erkannt (Abschnitt 5.2.4, `PCR_discontinuity_indicator_error`);
- `pat-int` — Überschreitung des *PAT*-Intervalls (Abschnitt 5.1.3, Grenzwert 500 *ms*, erfordert *Analyze PAT/PMT/KF*);
- `pmt-int` — Überschreitung des *PMT*-Intervalls (Abschnitt 5.1.5, Grenzwert 500 *ms*, erfordert *Analyze PAT/PMT/KF*);
- `tstd-video` — ein Über- oder Unterlauf des **T-STD**-Modells wurde erkannt (Abschnitt 5.3.16, erfordert *Analyze T-STD video*, **CBR**-only).

Um ein Flackern der Anzeige bei kurzen Spitzen zu vermeiden, wird eine Debounce-Logik angewendet: ein Token gelangt nur dann in `tr101290-alert`, wenn es mindestens 30 der letzten 60 Sekunden lang ausgelöst hat. Dies gilt einheitlich für alle Token.

Zusätzlich wird `tr101290-alert-acceptance` exportiert — eine Kopie des Attributs, in die ein Token erst nach 300 *Sekunden* ununterbrochener Verletzung gelangt (Abnahmestufe; das Intervall ist ein Produktwert, der Standard **TR 101 290** normiert es nicht). Es ist die „endgültige“ Sammelmetrik für die technische Abnahme des Kanals.

Die `pcr-drift`-Anzeige wird absichtlich in ein eigenes `pcr-drift-alert` (+ `pcr-drift-alert-acceptance`) ausgelagert. Quarzdrift ist ein Problem des Upstream-Encoders, sie ist unabhängig von der Transportintegrität und muss separat klassifiziert werden.

### KI-Reklamationsassistent

Falls bei einem Kanal *TR 101 290*- oder *PCR drift*-Alarme ausgelöst werden, kann der Betreiber mit einem einzigen Befehl einen fertigen englischsprachigen Prompt für einen KI-Chat abrufen, der ein formelles Reklamationsschreiben an den Upstream-Stream-Anbieter formuliert.

Endpoint: `GET /data/stream/<id>/ai-complaint-prompt`. Verfügbar mit der Rolle *Viewer* (wie alle GET-Anfragen unter `/data/*`). Liefert `text/plain; charset=utf-8`. Für **MPTS** wird 404 zurückgegeben — die Metriken *T-STD* / *PCR drift* sind nur auf **SPTS** anwendbar.

Der Prompt ist mit jedem modernen KI-Chat kompatibel: „ChatGPT“, „Claude“, „DeepSeek“, „Qwen“, „Doubao“. Der Ton des Schreibens ist sachlich, ohne Anschuldigungen; am Ende des Prompts wird die Sprache des fertigen Dokuments wählbar gemacht: Englisch, Russisch, vereinfachtes Chinesisch oder jede andere nach Wahl des Betreibers.

Inhalt des Prompts:

- Name und gemessene Parameter des Streams;
- die Liste aller aktiven *TR 101 290*- und *PCR drift*-Token mit den zugehörigen Standardverweisen;
- Zahlenwerte und Schwellwerte;
- die Auswirkung jedes Fehlers auf der Decoder-Seite.

Was **nicht** in den Prompt aufgenommen wird: Streamname, ID, Quell-URI. Diese Felder werden durch den Platzhalter `<Stream Designation>` ersetzt — der Betreiber trägt sie vor dem Versand selbst ein, damit sie nicht an einen Drittanbieter-KI-Dienst gelangen.

### System Monitor

Überwachung der wichtigsten Systemparameter.

### Mosaic

Funktion zur Screenshot-Aufnahme aus dem Eingangsstream. Wird pro **stream** einzeln aktiviert.

Falls nicht benötigt, kann sie zur Ressourcenschonung im Bereich **Settings/Server Settings** vollständig deaktiviert werden.

## Verwaltung

Im Bereich **Configuration/Administration/Administrators List** werden Benutzer für den Zugriff auf die Weboberfläche (eingebauter HTTP-Server) angelegt.

Benutzern werden Rollen zugewiesen:

**admin** — Vollzugriff.

**restricted admin** — Einstellungen sind nicht verfügbar; es kann nur **pause** geändert werden.

**viewer** — Zugriff nur im Lesemodus.

### Sicherung der Einstellungen

Für ein Backup der Einstellungen den Inhalt des Ordners */opt/pss/config* sichern.

Zur Wiederherstellung den Dienst stoppen und den Inhalt von */opt/pss/config* ersetzen.

### Verhalten beim Start und bei Konfigurationsfehlern

Beim Start versucht der Dienst nacheinander, die Konfigurationsdateien aus dem Verzeichnis */opt/pss/config* zu laden:

1. *pss.json* — Hauptkonfigurationsdatei.
2. *pss_back.json* — Sicherungskopie der vorherigen funktionierenden Konfiguration.
3. *pss_default.json* — Standardkonfiguration, die mit dem Paket ausgeliefert wird.

Es wird die erste erfolgreich geladene Datei verwendet. Sind alle drei Dateien nicht vorhanden oder beschädigt, startet der Dienst mit leeren Einstellungen; in diesem Fall sind ein manuelles Setzen des Administratorpassworts und ein Neustart erforderlich.

**Strukturell beschädigte Konfigurationsdatei.** Wenn *pss.json* einen JSON-Syntaxfehler, unbekannte Schlüssel, einen falschen Werttyp oder eine Eindeutigkeitsverletzung enthält (zum Beispiel zwei Streams mit derselben *id*), verschiebt der Dienst die beschädigte Datei in das Archiv */opt/pss/config/bad/* unter einem Namen der Form *pss_YYYYMMDD_HHMMSS.json* (Datum und Uhrzeit des Starts). Anschließend setzt der Dienst die Ladeversuche in gewohnter Reihenfolge fort und speichert die funktionierende Konfiguration aus *pss_back.json* oder *pss_default.json* erneut in *pss.json*. Details (Schlüsselname, Fehlerbeschreibung, Dateiname im Archiv) werden in das Dienstprotokoll geschrieben.

In das Archiv gelangt nur die Haupt-*pss.json*. Die Dateien *pss_back.json* und *pss_default.json* werden bei Beschädigung nicht archiviert — die Logeinträge reichen zur Diagnose aus, und die Dateien selbst bleiben an Ort und Stelle und können manuell korrigiert werden.

Existiert beim nächsten Laden im Verzeichnis */opt/pss/config/bad/* bereits eine Datei mit demselben Zeitstempel (zum Beispiel bei schnellen Neustarts innerhalb derselben Sekunde), wird sie überschrieben.

**Numerische Werte außerhalb des zulässigen Bereichs.** Enthält die Konfigurationsdatei einen numerischen Wert, der kleiner als das zulässige Minimum oder größer als das zulässige Maximum für diesen Parameter ist, verwirft der Dienst die Datei nicht vollständig. Stattdessen wird eine Warnung in das Protokoll geschrieben, die den Parameternamen, den gelesenen Wert und die angewandte Grenze nennt; der Wert selbst wird auf die nächstgelegene zulässige Bereichsgrenze (Minimum oder Maximum) gesetzt. Nach Abschluss des Ladens speichert der Dienst *pss.json* automatisch mit den korrigierten Werten erneut, sodass diese Warnungen bei einem erneuten Start nicht mehr erscheinen.

Dieses Verhalten gilt nur beim erstmaligen Laden der Konfigurationsdatei. Werden die Einstellungen über die Weboberfläche oder eine externe API geändert, werden Werte außerhalb des zulässigen Bereichs weiterhin mit einem Fehler abgelehnt — ohne automatische Korrektur.

### Anbindung externer Monitoring-Systeme

*pss-metrics* — universeller Metriken-Exporter für Perfect Streamer

Ein einzelnes Python-3-CLI-Skript, das Statistiken über die HTTP-API des PSS-Webservers abruft und die Ausgabe für die gängigsten Monitoring-Systeme formatiert:

> - Zabbix (UserParameter, Low-Level Discovery, zabbix_sender trapper)
> - Prometheus (Text-Expositionsformat für den textfile collector)
> - InfluxDB / Telegraf (Line Protocol oder JSON für den exec-Input)
> - Universelles JSON für beliebige Skripte und Nagios-artige Statusprüfungen

Der Exporter ist eine einzelne, in sich geschlossene Datei ohne Drittanbieter-Abhängigkeiten und nutzt ausschließlich die Standardbibliothek von Python 3.6+ (*urllib*, *xml.etree*, *json*, *argparse*).

#### Dateien

```
pss-metrics.py                    main CLI (executable)
userparameter_pss.conf.example    UserParameter template for Zabbix
```

#### Einrichtung

Der Exporter wird unter */opt/pss/monitoring/pss-metrics.py* ausgeliefert. Stellen Sie sicher, dass Python 3.6 oder neuer installiert ist:

```
# RHEL / Rocky / AlmaLinux
yum install -y python3

# Debian / Ubuntu
apt-get install -y python3
```

Es werden keine zusätzlichen Pakete benötigt.

#### Konfiguration

Standardmäßig verbindet sich *pss-metrics* mit *http://127.0.0.1:43971* und ermittelt den Port automatisch aus */opt/pss/config/pss.json* (oder */opt/pss/config/pss_default.json*). Parameter können über Umgebungsvariablen, die Datei */etc/pss-metrics.conf* (Format *Schlüssel=Wert*) oder Kommandozeilen-Flags überschrieben werden. Reihenfolge: CLI > env > Datei > Standardwerte.

Unterstützte Variablen:

```
PSS_URL          full URL, e.g. http://10.0.0.1:43971         (auto by default)
PSS_USER         web server login (if authorization is enabled)
PSS_PASS         web server password
PSS_TIMEOUT      HTTP timeout, in seconds                      (default 5)
PSS_CACHE_DIR    cache directory                               (default /run/pss-metrics)
PSS_CACHE_TTL    cache TTL, in seconds                         (default 10)
PSS_CA_BUNDLE    path to CA bundle for HTTPS
PSS_INSECURE     1 — disable TLS certificate verification
PSS_VERBOSE      1 — log requests to stderr
```

Cache: jeder Lauf führt höchstens einen HTTP-GET pro Endpoint innerhalb des TTL-Fensters aus. Bei TTL=10 s erzeugen selbst hunderte UserParameter-Abfragen pro Minute nur ~6 HTTP-Anfragen pro Minute an PSS.

#### Schnellstart

Statusprüfung (Nagios-Exit-Codes):

```
pss-metrics.py health
# OK: total=42 running=15 stopped=27 unhealthy=0 version=1.12.2.430d
```

Zabbix-LLD-Erkennung:

```
pss-metrics.py discover streams
pss-metrics.py discover inputs --running-only
pss-metrics.py discover outputs
```

Eine einzelne Metrik abrufen (für Zabbix UserParameter):

```
pss-metrics.py get summary.running
pss-metrics.py get stream.10031.bitrate
pss-metrics.py get input.10031.1.speed1
pss-metrics.py get output.10031.1.speed
pss-metrics.py get sysmon.cpu.self-usage
pss-metrics.py get server.server-version
```

Vollständiger Export:

```
pss-metrics.py dump --format=json
pss-metrics.py dump --format=prometheus
pss-metrics.py dump --format=influx
pss-metrics.py dump --format=zabbix-trapper --zabbix-host=streamer-01
```

#### Metrik-Pfade

*pss-metrics get* akzeptiert einen mit Punkten getrennten Pfad. Leere Ausgabe bedeutet „Wert fehlt“ (zum Beispiel existiert die Metrik nur für laufende Streams).

```
server.<attr>                e.g. server.server-version, server.uptime
summary.<key>                total | running | stopped | unhealthy |
                             input_bitrate_kbps | output_bitrate_kbps
sysmon.cpu.<attr>            self-usage | total-usage | cores
sysmon.memory.<attr>         self-usage-kb | available-kb | total-kb
sysmon.netbw.<iface>.<attr>  rx-bw | tx-bw  (interface name as in XML)
stream.<id>.<attr>           any <stream> attribute
input.<sid>.<iid>.<attr>     any <input> attribute
output.<sid>.<oid>.<attr>    any <output> attribute
```

Nützliche Stream-Attribute (aus /data/stream/detail):

```
stream:  state, state-str, bitrate, thread-usage, mpts
input:   speed1, recv-bytes, recv-packets, recv-err,
         stat-disc, stat-disc1, stat-scrambled, stat-scrambled1,
         health-state-good, health-status, check-status
output:  speed, sent-bytes, sent-packets, sent-err, uri, type
```

#### Integration mit Zabbix

Zwei Szenarien werden unterstützt — wählen Sie das für Ihre Umgebung passende.

1. Statischer UserParameter + LLD (Zabbix-Agent v1 / v2)

   Kopieren Sie *userparameter_pss.conf.example* nach */etc/zabbix/zabbix_agentd.d/pss.conf*, starten Sie *zabbix-agent* neu und importieren Sie auf dem Server ein Template mit LLD-Prototypen, die die *pss.discover*-Schlüssel verwenden. Beispielbindungen:

   ```
   UserParameter=pss.discover[*],/opt/pss/monitoring/pss-metrics.py discover $1
   UserParameter=pss.get[*],/opt/pss/monitoring/pss-metrics.py get $1
   UserParameter=pss.health,/opt/pss/monitoring/pss-metrics.py health
   ```

   Auf dem Zabbix-Server:

   ```
   Discovery rule key:        pss.discover[streams]
   Item prototype keys:       pss.get[input.{#STREAM_ID}.1.speed1]
                              pss.get[stream.{#STREAM_ID}.bitrate]
                              pss.get[summary.unhealthy]
   ```
2. Trapper (Push) über zabbix_sender

   Per Timer (cron / systemd) ausführen und die Ausgabe in eine Pipe leiten:

   ```
   /opt/pss/monitoring/pss-metrics.py dump --format=zabbix-trapper \
       --zabbix-host="$(hostname)" \
     | zabbix_sender -z zabbix.example.com -i -
   ```

#### Integration mit Prometheus

Zwei Varianten.

1. Textfile collector (empfohlen für One-Shot-Umgebungen).

   Periodischen Export per systemd-timer oder cron ausführen:

   ```
   */1 * * * * /opt/pss/monitoring/pss-metrics.py dump --format=prometheus \
       > /var/lib/node_exporter/textfile_collector/pss.prom.$$ \
       && mv /var/lib/node_exporter/textfile_collector/pss.prom.$$ \
             /var/lib/node_exporter/textfile_collector/pss.prom
   ```

   *node_exporter* stellt die Datei über *–collector.textfile.directory* bereit.
2. Direkter Scrape über einen kleinen Wrapper (z. B. *socat* + *pss-metrics dump*) oder einen beliebigen HTTP-Proxy eines Drittanbieters.

#### Integration mit Telegraf / InfluxDB

Telegraf *inputs.exec*:

```
[[inputs.exec]]
  commands = ["/opt/pss/monitoring/pss-metrics.py dump --format=influx"]
  interval = "10s"
  timeout  = "5s"
  data_format = "influx"
```

Für den JSON-Parser verwenden Sie *–format=json* und konfigurieren *data_format = „json“* mit Feldpfaden.

#### HTTPS und Authentifizierung

Wenn der PSS-Webserver hinter HTTPS läuft oder durch ein Passwort geschützt ist:

```
PSS_URL=https://streamer.example.com:8443 \
PSS_USER=monitor PSS_PASS=secret \
pss-metrics.py health
```

Selbstsignierte Zertifikate: setzen Sie *PSS_INSECURE=1* (nicht empfohlen) oder geben Sie *PSS_CA_BUNDLE=/path/to/ca.pem* an.

#### Rückgabewerte

*pss-metrics* folgt der Nagios-Konvention:

```
0  OK
1  WARNING       (e.g. running streams report unhealthy)
2  CRITICAL      (PSS is unreachable)
3  UNKNOWN       (invalid arguments / internal error)
```

*get* und *dump* geben eine leere Zeile aus und beenden mit Code 0, wenn die angeforderte Entität fehlt — dies entspricht der Erwartung von Zabbix, dass ein leerer Wert als „NOT_SUPPORTED“ und nicht als Agent-Fehler interpretiert wird.

#### Diagnose

```bash
pss-metrics.py -v health        # log every HTTP request to stderr
pss-metrics.py --cache-ttl=0 …  # bypass cache while debugging
rm -rf /run/pss-metrics         # purge cache
```

### Let’s Encrypt und certbot für HTTPS

Ab Version 1.9.2.340 unterstützt Perfect Streamer die automatische Erneuerung von Let’s-Encrypt-Zertifikaten für HTTPS in Web Server, HTTP Server und EPG Server.

### certbot für RHEL einrichten.

Einschränkung:

- Der TCP-Port 80 muss frei sein und es muss eine öffentliche IP mit Public-Domain-Name vergeben sein.
- Alle HTTPS-Server nutzen denselben Hostnamen (Web Server, HTTP Server, EPG Server).

Installation von certbot ([https://certbot.eff.org/instructions?ws=other&os=snap](https://certbot.eff.org/instructions?ws=other&os=snap)):

```sh
sudo yum install snapd
sudo systemctl enable --now snapd.socket
sudo ln -s /var/lib/snapd/snap /snap
sudo snap install certbot --classic
```

certbot konfigurieren:

```sh
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot certonly --standalone
```

certbot-Prüfung:

```sh
sudo certbot renew --dry-run
```

certbot-Timer prüfen:

```sh
systemctl list-timers | grep certbot
```

In der Perfect-Streamer-Adminoberfläche HTTPS für Web Server, HTTP Server und EPG Server aktivieren.

Hook-Skript erstellen ([https://pstreamer.tv/distrib/scripts/cert_update.zip](https://pstreamer.tv/distrib/scripts/cert_update.zip)) und es unter folgendem Pfad ablegen:

```sh
/opt/pss/scripts/cert_update.sh
```

Dort kann bei Bedarf der Hostname eingetragen werden; standardmäßig wird er aus /etc/hostname übernommen.

Datei ausführbar machen.

```sh
chmod +x /opt/pss/scripts/cert_update.sh
```

Skriptausführung prüfen, es sollten keine Fehler auftreten:

```sh
/opt/pss/scripts/cert_update.sh
```

Die HTTPS-Einstellungen sollten greifen; die Statusänderung wird in den Logs angezeigt.

Hook-Datei in certbot hinzufügen:

```sh
certbot renew --deploy-hook "/opt/pss/scripts/cert_update.sh"
```

certbot erneut prüfen:

```sh
sudo certbot renew --dry-run
```

### certbot für Debian/Ubuntu einrichten.

Die Einrichtung unter Debian läuft analog zu RHEL; kurze Beschreibung am Beispiel Ubuntu 24.04.2 LTS.

certbot installieren:

```sh
apt install certbot
certbot certonly
```

Skript ausführbar machen:

```sh
chmod +x /opt/pss/scripts/cert_update.sh
```

Skript ausführen:

```sh
/opt/pss/scripts/cert_update.sh
```

Ausgabe:

```sh
Select domain name (your domain name)
```

Prüfen, ob die Zertifikate erneuert wurden:

```sh
ls -lat /opt/pss/config/cert/
total 44
-rw------- 1 root root  241 May 26 07:52 epgserver.key
-rw------- 1 root root  241 May 26 07:52 httpserver.key
-rw------- 1 root root  241 May 26 07:52 webserver.key
-rw-r--r-- 1 root root 1338 May 26 07:52 epgserver.crt
-rw-r--r-- 1 root root 1338 May 26 07:52 httpserver.crt
-rw-r--r-- 1 root root 1338 May 26 07:52 webserver.crt
```

Das Datum muss aktuell sein.

## DVB-Adapter

Perfect Streamer unterstützt jeden im System installierten DVB-Adapter. Unterstützte Standards: DVB-S, DVB-S2, DVB-T, DVB-T2, DVB-C, ATSC. Zusätzlich implementiert: T2-MI-Dekapselung (ETSI TS 102 773) und BISS-1 / BISS-E-Entschlüsselung.

Die Hauptvoraussetzung ist ein korrekt installierter und funktionierender Adaptertreiber im System.

Der Bereich DVB erscheint nur, wenn im System gültige DVB-Adapter vorhanden sind. Eine Rekonfiguration zur Laufzeit wird nicht unterstützt; ein Neustart des Streamers ist erforderlich.

### Adapteranbindung

Um einen neuen DVB-Adapter hinzuzufügen, wechseln Sie in den entsprechenden Abschnitt und fügen Sie den Adapter hinzu:

- Adaptername festlegen.
- Einen Adapter aus der Liste der im System verfügbaren auswählen.
- Modus **Stream** wählen.
- Den Typ des Übertragungssystems angeben: **DVB-S**, **DVB-S2**, **DVB-T**, **DVB-T2**, **DVB-C**.
- Empfangsparameter angeben: **Trägerfrequenz**, **Polarisation**, **Symbolrate**, **FEC**, **Modulation**, **DVB-Stream-ID**, **Oszillatorfrequenz**, **High-Band-Oszillator**, **High-Band-Grenze**, **DiSEqC-1.0-Modus** sowie weitere typabhängige Parameter.

Optional kann die Aufzeichnung von EIT in die EPG-Datenbank aktiviert werden (**EIT in EPG-DB schreiben**).

Das Hinzufügen für jeden im System vorhandenen Adapter wiederholen.

### DVB-Scan

Damit die Empfangsparameter (Frequenz, Polarisation, Symbolrate, FEC, Modulation) nicht manuell eingegeben werden müssen, enthält Perfect Streamer einen integrierten Transponder-Scanner. Der Scanner durchläuft die Transponder des ausgewählten Satelliten (DVB-S/S2) oder regionalen Bandes (DVB-C, DVB-T/T2), führt das Tuning und den Empfang jedes einzelnen durch, sammelt die PSI/SI-Tabellen (PAT, PMT, SDT) und erstellt eine abschließende Liste der Multiplexe mit ihren Programmen. Jeder gefundene Multiplex kann mit einer Schaltfläche zur DVB-Adapterliste hinzugefügt werden.

Der Scanner belegt den physischen Adapter vollständig; für den Start wird ein Adapter benötigt, der weder vom Betriebssystem-Kernel noch von einem aktiven DVB-Adaptereintrag in Perfect Streamer verwendet wird.

#### Verfügbare Adapter

Beim Öffnen des Scan-Bildschirms in der Administrationsoberfläche wird eine Liste der physischen DVB-Adapter des Systems mit dem jeweiligen Belegungsstatus angezeigt:

- **free** — der Adapter steht für das Scannen zur Verfügung.
- **kernel** — das Gerät wird von einem anderen Betriebssystemprozess belegt.
- **pss-id-N** — der Adapter wird bereits durch einen DVB-Adaptereintrag in Perfect Streamer mit der angegebenen Kennung verwendet. Solange dieser Eintrag aktiv ist, kann der Scanner darauf nicht gestartet werden. Um den Adapter vorübergehend freizugeben, muss der vorhandene DVB-Adaptereintrag pausiert werden (das Kennzeichen **Pause** in seinen Einstellungen).

#### Transponderlisten

Der Scanner orientiert sich an Referenzlisten im Enigma2-Format: der Satellitenliste `satellites.xml` und den regionalen Listen `cables.xml` / `terrestrial.xml`. Jede Datei enthält eine Reihe von Transpondern für eine bekannte Orbitalposition oder ein regionales DVB-T/C-Band (näheres auf der Projektseite [oe-alliance-tuxbox-common](https://github.com/oe-alliance/oe-alliance-tuxbox-common)).

Die Dateien befinden sich im Verzeichnis `sat/` relativ zu `pss.json` (standardmäßig `/etc/pss/sat/`). Sie sind im Lieferumfang von Perfect Streamer enthalten und werden beim Öffnen des Scan-Bildschirms geladen. Bei Bedarf können sie durch Ersetzen der jeweiligen XML-Datei aktualisiert werden.

In der Administrationsoberfläche sind die Referenzlisten als drei Listen dargestellt:

- **Satellit** — Orbitalpositionen (zum Beispiel *Hot Bird 13.0°E*, *Astra 19.2°E*).
- **Kabelregion** — Land oder DVB-C-Anbieter.
- **Terrestrische Region** — DVB-T/T2-Region.

Ist der benötigte Satellit oder die Region nicht in den Referenzlisten enthalten, kann das XML aktualisiert oder stattdessen der **Blindscan** verwendet werden (siehe unten).

#### Start

Im Scan-Dialog werden nacheinander folgende Angaben festgelegt:

- Ein freier physischer Adapter.
- Delivery-System-Typ: **DVB-S**, **DVB-S2**, **DVB-T**, **DVB-T2** oder **DVB-C**.
- Quelle:

  - für DVB-S/S2 — eine Orbitalposition aus der Satellitenliste und LNB-Parameter (Lokaloszillator-Frequenzen LO1 und LO2, Grenzfrequenz des oberen Bandes, DiSEqC-Port);
  - für DVB-C — eine Kabelregion;
  - für DVB-T/T2 — eine terrestrische Region.

Nach Drücken von **Start** wird der Scan im Hintergrund ausgeführt. Der Fortschritt wird in der Administrationsoberfläche angezeigt:

- der Fortschritt in Prozent, bezogen auf die Anzahl der verarbeiteten Transponder;
- die aktuelle Frequenz und Polarisation;
- die Zähler *Multiplexe gefunden* und *Programme gefunden*;
- ein Baum der bereits gefundenen Multiplexe, der bis zu den Programmen aufgeklappt werden kann.

Der Scanner ist eine streamerweite gemeinsame Ressource: Es läuft jeweils höchstens ein Scan gleichzeitig. Wird während eines laufenden Scans ein neuer gestartet, wird der vorherige automatisch abgebrochen. Die Schaltfläche **Abbrechen** bricht den Scan ab und leert die angesammelte Liste.

Die Scan-Dauer hängt von der Anzahl der Transponder in den ausgewählten Referenzlisten ab (typischerweise bis zu 5 Sekunden pro Transponder: bis zu 2 Sekunden für den Signal-Lock und bis zu 5 Sekunden für die Erfassung der PSI). Typische Werte:

- DVB-S Hot Bird 13.0°E — etwa 2 Minuten (44 Transponder).
- DVB-S Astra 19.2°E — etwa 1,5 Minuten.
- DVB-T europäische Region — weniger als eine Minute.

#### Ergebnis

Das Scan-Ergebnis wird als Baum **Multiplex → Programme** angezeigt.

Multiplex-Parameter:

- Frequenz, Polarisation, Symbolrate;
- FEC, Modulation, Delivery System;
- Transportstrom-Kennung (**TSID**);
- die Frontend-Messwerte zum Zeitpunkt des Endes der PSI-Erfassung — **SNR**, **Signal**, **BER**;
- die Zähler *pmt-total* / *pmt-recv* — wie viele PMT-Tabellen von der PAT angekündigt wurden und wie viele innerhalb des Timeouts tatsächlich erfasst wurden.

Programmparameter:

- **PNR** (program_number) — die Service-Kennung innerhalb des Multiplexes;
- **Name** und **Anbieter** — aus der SDT-Tabelle, in UTF-8;
- **scrambled** — der Verschlüsselungs-Indikator. Seine Quelle wird in der folgenden Reihenfolge bestimmt: das Bit `free_CA_mode` aus der SDT (Deklaration des Broadcasters) → die Transport-Scrambling-Flags aus der PMT. Treffen weder SDT noch PMT innerhalb des Timeouts ein, ist der Standardwert 0 („unverschlüsselt“); der tatsächliche Zustand wird beim Empfangsversuch festgestellt;
- **video-pid**, **audio-pid**, **pcr-pid** — die wichtigsten Elementarströme des Dienstes.

#### Anwendung des Ergebnisses

Der im Baum ausgewählte Multiplex wird mit einer einzigen Schaltfläche zur DVB-Adapterliste hinzugefügt. Es wird ein neuer Eintrag mit den Parametern aus dem Scan-Ergebnis (Frequenz, Polarisation, Symbolrate, FEC, Modulation, Delivery System) sowie den zu Beginn des Scans angegebenen LNB-Parametern und dem Paar *adapter/device* erstellt. Der Adaptername wird vom Benutzer festgelegt; zusätzliche Parameter (BISS-Schlüssel für verschlüsselte Kanäle, T2-MI, gemeinsam genutztes LNB usw.) werden nach dem Anlegen des Eintrags in dessen Einstellungen ergänzt.

Programme aus den gefundenen Multiplexen werden separat angewandt — durch Anlegen eines Streams (**Stream**) mit einer **demuxer**-Eingangsquelle, adressiert über die PNR (siehe Abschnitt *Anbindung eines SPTS-Streams an einen DVB-Multiplex-Dienst*).

#### Blindscan

Der Blindmodus wird verwendet, wenn:

- der benötigte Satellit nicht in den Referenzlisten enthalten ist (nicht standardisierte Orbitalposition, lokaler Uplink);
- die regionalen DVB-T/C-Referenzdaten unzureichend oder für einen bestimmten Standort veraltet sind;
- ein Bandabschnitt unabhängig von der Liste bekannter Transponder erneut überprüft werden soll.

In diesem Modus greift der Scanner nicht auf die Referenzlisten zu, sondern erzeugt eine Liste der Transponder aus einem Frequenzraster. Standardmäßig werden die folgenden typischen Bereiche verwendet:

- DVB-S/S2 Ku — 10700..12750 MHz, Schrittweite 4 MHz, beide Polarisationen (H und V), typische Symbolraten 22000 / 27500 / 30000 kSym/s.
- DVB-C — 47000..862000 kHz, Schrittweite 8000 kHz, QAM-64 und QAM-256, typische Symbolraten 6875 / 6900 / 6952 kSym/s.
- DVB-T/T2 — 174000..862000 kHz, Schrittweite 8000 kHz.

Ein vollständiger Blinddurchlauf des Ku-Bands dauert in der Größenordnung von 100 Minuten (mehrere tausend Abstimmpunkte). In der Praxis wird der Bereich in der Administrationsoberfläche manuell eingeschränkt — minimale/maximale Frequenz und Rasterschritt. Ein Durchlauf von 11700..11800 MHz mit 4 MHz Schritt für ein einzelnes LNB-Band dauert beispielsweise etwa 5 Minuten.

Das Ergebnisformat eines Blindscans ist mit dem eines normalen Scans identisch. Besonderheiten:

- die Felder **FEC** und **Modulation** der gefundenen Multiplexe werden auf dem Wert **AUTO** festgesetzt — der Scanner ermittelt ihre genauen Werte nicht;
- Das Delivery System entspricht dem angeforderten (DVB-S, DVB-S2, …). Für gemischte Netze wird empfohlen, zwei Durchläufe durchzuführen — DVB-S und DVB-S2 getrennt.

Das Anwenden eines Multiplexes aus einem Blindscan erfolgt auf dieselbe Weise wie aus einem normalen Scan — über die Schaltfläche zum Hinzufügen zur DVB-Adapterliste. Die Felder **FEC** und **Modulation** werden in der Regel auf AUTO belassen und bei Bedarf nach einem stabilen Signal-Lock auf dem jeweiligen Transponder verfeinert.

### Größe des Kernel-Empfangspuffers

Der Parameter **buffer-size** (Ganzzahl, Standard 512) legt die Größe des Kernel-DVB-Demux-Ringpuffers in Blöcken zu 65536 Byte fest.

- `512` (32 MB) — empfohlener Standardwert. Deckt Volltransponder-Szenarien DVB-S/S2 (>= 33 Mbit/s) mit einem oder mehreren MPTS-Verbrauchern ab. Ausgewählt anhand von Stand-Tests mit einem TBS-Adapter unter voller Transponderlast.
- `8..64` (512 KB .. 4 MB) — zulässig für eingebettete Systeme mit begrenztem RAM oder für Adapter im Scanner-/Femon-Modus mit geringem Datenverkehr.
- `0` — Treiber-Standard beibehalten (in der Regel 8…32 KB). Geeignet nur für sehr gering belastete Szenarien. Bei Streams über 10 Mbit/s treten Verluste auf.

Wenn eine Meldung folgender Form im Protokoll erscheint:

```
DVB adapter X/Y dvr buffer overflow (NN so far, KK pids);
raise 'buffer-size' or reduce pid filter
```

**buffer-size** erhöhen oder die Anzahl der den Filter passierenden PIDs verringern (z. B. den MPTS-Ausgang weglassen, falls nicht benötigt).

Speicherbedarf: Wert N verbraucht N × 64 KB Kernel-Speicher pro Adapter. Bei vielen Adaptern (8 und mehr) ist dies zu berücksichtigen (8 × 32 MB = 256 MB).

### SPTS-Stream an einen DVB-Multiplex-Dienst anschließen

Beim Hinzufügen eines neuen SPTS-Kanals zum `input` des Streams wählen Sie:

- Typ: **demuxer**.
- Quelle: **DVB-Adapter**.
- Auf dem DVB-Adapter erstellter Multiplex, nach Adaptername.
- PNR — wird aus der Popup-Liste der im Multiplex erkannten Dienste ausgewählt oder manuell eingegeben.

### Zugriffsrechte für DVB-Geräte

Wenn DVB-Adapter in Perfect Streamer nicht angezeigt werden, gehen Sie wie folgt vor:

```sh
sudo nano /etc/udev/rules.d/99-dvb-permissions.rules
SUBSYSTEM=="dvb", GROUP="video", MODE="0660"

sudo usermod -aG video pss
sudo chown -R root:video /dev/dvb/*
sudo reboot
```

### T2-MI-Dekapselung

T2-MI (T2-Modulator Interface, ETSI TS 102 773) ist ein Format für den Transport von DVB-T2-Streams über DVB-S2 multistream. Der äußere DVB-S/S2-Transponder trägt eine oder mehrere T2-MI-carrier-PIDs, von denen jede BBFRAMEs mit einem oder mehreren PLPs (Physical Layer Pipe) kapselt. Nach der Dekapselung wird aus dem BBFRAME der innere MPEG-TS mit Programmen und PSI/SI-Tabellen extrahiert.

Die Perfect-Streamer-Implementierung arbeitet im **Multi-Carrier-Modus**: ein einzelner physischer Adapter liefert gleichzeitig den äußeren DVB-S/S2-Multiplex **und** alle dekapselten T2-MI-Carrier (einen pro carrier-PID, der im PMT des äußeren Streams gefunden wurde).

#### Konfigurationsparameter

**t2mi-mode** (Int, 0..2, Standard 0) — Dekapselungsmodus:

- `0` — Deaktiviert. Der äußere MPEG-TS wird unverändert weitergeleitet. Wenn ein T2-MI-Deskriptor (tag 0x51) im PMT erkannt wird, wird ein einmaliger Hinweis protokolliert.
- `1` — Manuell. Die Dekapselung ist dauerhaft aktiviert. Wenn `t2mi-pid` ungleich 0 ist, wird beim Start ein carrier auf dieser PID vorab erstellt. Weitere carrier werden weiterhin automatisch über das PMT erkannt.
- `2` — Auto. Carrier werden automatisch aus dem PMT des äußeren Multiplexes für alle ES erkannt, die wie T2-MI aussehen (Deskriptor 0x51 oder einzelnes ES mit stream_type=0x06 auf einem Dienst ohne andere A/V-ES). Werden keine carrier gefunden, arbeitet der Adapter als regulärer DVB-Multiplex.

**t2mi-pid** (Int, 0..8191, Standard 0) — PID für das Vorab-Erstellen eines carrier beim Start, vor Eintreffen des PMT:

- `0` — keine Vorab-Erstellung. Carrier werden aus dem PMT erkannt (empfohlen für Auto-Modus `2`).
- `1..8191` — carrier auf dieser PID vorab erstellen. Zusätzliche im PMT gefundene T2-MI-ES erhalten dennoch eigene carrier.

Im Multi-Carrier-Modus ist der Parameter `t2mi-pid` **kein** Selektor für einen einzelnen carrier — jedes erkannte T2-MI-ES erhält einen eigenen carrier mit eigenem Dekapselator. Der Parameter ermöglicht eine frühe Initialisierung für eine bekannte PID.

**t2mi-plp** (Int, 0..255, Standard 0) — Kennung des PLP, das aus jedem T2-MI-carrier auf dem Adapter extrahiert wird. Wird auf **alle** carrier angewendet — eine Überschreibung pro carrier wird in der aktuellen Version nicht unterstützt. Wenn im Betrieb verschiedene carrier unterschiedliche PLPs tragen, sollte man:

- ein für alle carrier gemeinsames PLP angeben, oder
- separate Adapter für verschiedene PLPs mittels `lnb-sharing` konfigurieren.

Dies ist die Kennung des BBFRAME-Feldes `plp_id`, **nicht** der DVB-S2-multistream-ISI (der über den Parameter `dvb-stream-id` festgelegt wird). Es handelt sich um verschiedene Kennungen auf verschiedenen Ebenen.

Diagnose der PLP-Auswahl:

- Fünf Sekunden nach dem Start eines carrier wird, falls für das konfigurierte PLP kein BBFrame empfangen wurde, aber andere PLPs zu sehen sind, eine Warnung mit der Liste der beobachteten `plp_id` protokolliert.

**t2mi-tsid** (Int, -1..255, Standard -1) — für zukünftige Nutzung reserviert. Selektor der T2-MI-Stream-Kennung, wenn mehrere T2-MI-Streams eine carrier-PID teilen. In der aktuellen Version ignoriert.

#### Zusammengesetzte PNR — SPTS-Anbindung aus T2-MI

Ein Adapter kann mehrere logische Multiplexe bereitstellen:

- `carrier-id = 0` — äußerer DVB-S/S2-Multiplex (reguläre A/V-Dienste).
- `carrier-id = 1..N` — dekapselte T2-MI-carrier (einer pro äußerem T2-MI-ES).

### BISS-Entschlüsselung

Die Entschlüsselung verschlüsselter DVB-Streams nach BISS-1 (Modus E1) und BISS-E (Modus E2) wird unterstützt. Anwendbar auf die Übertragungssysteme DVB-S, DVB-S2, DVB-T, DVB-T2.

Die Implementierung erlaubt es, mehrere Entschlüsseler gleichzeitig auf einem Adapter aktiv zu halten:

- Nach **PNR** im äußeren Multiplex (regulärer Dienst).
- Nach `plp_id` zur Entschlüsselung der T2-MI carrier-PID **vor** der Dekapselung (erforderlich für verschlüsselte `multistream`-Streams — andernfalls verwirft der Dekapselator jedes verschlüsselte Paket, siehe Zähler `<t2mi scrambledDropped>`).

## EPG

### EPG/XMLTV-Import

EPG-Daten werden aus verschiedenen Quellen in der EPG-Datenbank gesammelt:

- EIT aus empfangenen Streams (SPTS und MPTS). Aktivierbar über Stream: Extract EIT to EPG Database.
- Import im XMLTV-Format aus verschiedenen externen Quellen. Die Quellen werden in den Einstellungen Configuration/EPG/EPG Sources festgelegt. Unterstützt werden EPG-Quellen in Form eines Links auf eine Web-Ressource sowie lokal aus einer Datei unter Angabe des vollständigen Pfads.

Die EPG-Event-Aufbewahrungsdauer wird über den Parameter EPG storage period (days) gesetzt.

Auto-Clean-Datenbank — Programme ohne Events werden entfernt.

Im EPG-Bereich werden EPG-Quellen und zugehörige Daten angezeigt. Für jeden Kanal (EPG Channels List) lassen sich festlegen:

- Channel Name — Name, der beim Export auf dem XMLTV-Server verwendet wird.
- Time Zone — die Zeitzone kann korrigiert werden, falls beim Import keine Bindung an UTC erfolgte.
- EPG Channel Sets — Kanal an ein Channel Set binden (siehe unten).
- Icon — URL des Kanal-Icons (*http://example.com/mychannel/myicon.png*).

### EIT-Generator

EIT-Daten aus der EPG Database können in einem SPTS Stream generiert werden. Setzen Sie dazu in den Stream-Einstellungen **EPG Source ID** und wählen Sie **EPG Channel ID**. Dabei wird die SDT zwingend generiert, auch wenn sie in der Quelle nicht vorhanden ist. Setzen Sie **SDT Data** korrekt.

Wird dieser Stream im Multiplexer eingesetzt, kann der Service Name separat in der output/muxer-Einstellung überschrieben werden.

## EPG-Server (XMLTV)

Ein separater integrierter HTTP-Server in Perfect Streamer liefert das vollständige XMLTV für einen vorgegebenen Kanalsatz. Der Endpunkt ist für Middleware und Player gedacht, die das Programm lokal vorhalten und es selten aktualisieren — als ganze Datei, typischerweise ein- bis zweimal täglich.

Der Server und seine Clients werden unter **Configuration/EPG/EPG Server** konfiguriert.

### URL und Authentifizierung

Der Dienst wird von einem **separaten** HTTP-Server `epg-server` bereitgestellt (nicht von dem für `/data/*`). Standardmäßig lauscht er auf den Ports `10444` (HTTP) und `10445` (HTTPS); Ports und SSL werden unter `/config/epg-server` konfiguriert.

Routen:

| URL | Content-Type | Verhalten |
| --- | --- | --- |
| `GET /xmltv` | `text/xml` | Bei `Accept-Encoding: gzip` wird die Antwort spontan komprimiert (`Content-Encoding: gzip`), sonst als unkomprimiertes XML ausgeliefert. |
| `GET /xmltv.gz` | `application/octet-stream` | Liefert stets einen Gzip-Stream mit `Content-Disposition: inline; filename="xmltv.xml.gz"` — praktisch zum Speichern als Datei. |

Drei Authentifizierungsmethoden werden unterstützt:

- **HTTP Digest** (bevorzugt) — Konto aus `/config/epg-server/login`.
- **URL-Parameter** — `?l=<login>&p=<password>` (Synonyme: `login=…`, `password=…`).
- **Loopback** — eine Anfrage von `127.0.0.1` wird anonym behandelt. Praktisch für Skripte, die auf derselben Maschine laufen.

> **Warnung**
>
> Login und Passwort in der URL landen in den Access-Logs des Reverse-Proxys und im Browserverlauf. Für die öffentliche XMLTV-Verteilung verwenden Sie HTTP Digest oder lassen Sie Anfragen nur von privaten Adressen zu.

### Zugriff per channel-set

Jedes `epg-server/login`-Konto ist an **ein** `channel-set` aus `/config/epg-channel-set` gebunden. Das EPG in der Antwort enthält nur die Kanäle des angegebenen Sets. So kann eine einzige PSS-Installation unterschiedlichen Betreibern/Middleware unterschiedliches XMLTV ausliefern.

Grundeinrichtung in der UI:

1. Erstellen Sie unter **Configuration/EPG/EPG Channel Sets** eine Kanalgruppe und weisen Sie die gewünschten Kanäle bei den EPG-Quellen zu.
2. Legen Sie unter **Configuration/EPG/EPG Server Clients** ein Konto an und binden Sie die erstellte Gruppe daran. Ohne `channel-set`-Bindung erhält der Client ein leeres XMLTV.

Zusätzliche Einschränkungen für einen Login:

- `ip-addr` — wenn gesetzt und kein Platzhalter, erhält eine Anfrage von einer anderen IP `403 Forbidden`.
- `limit-day` — Unix-Epoche-Sek., nach der das Konto nicht mehr bedient wird (`403 Forbidden`). Praktisch für ein Abonnementmodell.
- `pause` — einen Login vorübergehend deaktivieren, ohne ihn zu löschen.

### Antwortformat

Der Antwortkörper ist ein XMLTV-Dokument mit der Wurzel `<tv>`. Die Struktur folgt dem gängigen [XMLTV DTD](http://wiki.xmltv.org/index.php/XMLTVFormat)-Schema:

```xml
<?xml version="1.0" encoding="utf-8"?>
<tv source-info-name="..." source-info-url="...">
  <channel id="channel.one">
    <display-name lang="en">Channel One</display-name>
    <icon src="https://.../channel-one.png"/>
  </channel>
  ...
  <programme start="20260504060000 +0300"
             stop ="20260504070000 +0300"
             channel="channel.one">
    <title lang="en">Morning News</title>
    <desc  lang="en">Overview of the day's events</desc>
    <rating system="MPAA"><value>PG</value></rating>
  </programme>
  ...
</tv>
```

Hinweise zu Feldern:

- Die Attribute `source-info-name` und `source-info-url` der `<tv>`-Wurzel werden aus den Feldern **EPG Source Name** und **EPG Source URL** unter **Configuration/EPG/EPG Server** gefüllt.
- Die Attribute `start` und `stop` werden im Format `YYYYMMDDhhmmss ±zone` angegeben (die Zeitzone stammt aus dem Feld `time_zone` des Kanals).
- Ein `<programme>` darf mehrere `<title>`/`<desc>` für verschiedene Sprachen enthalten. Das Attribut `lang` ist leer, wenn die Sprach-ID aus den EPG-Quelldaten nicht im Wörterbuch zugeordnet werden konnte (der Eintrag erscheint dennoch in der Ausgabe).
- Kanäle mit einem konfliktbehafteten `channel_id` (wenn dieselbe ID aus mehreren Quellen kommt) werden einmal angezeigt, die übrigen Quellen werden mit einer Warnung im Serverlog übersprungen.
- Nur Ereignisse mit `stop_time >= now` werden in die Ausgabe aufgenommen.

### HTTP-Header

Der Server sendet immer:

```
Cache-Control: no-cache, no-store, must-revalidate
Pragma:        no-cache
Expires:       0
Connection:    close
```

Für `/xmltv` zusätzlich — `Content-Encoding: gzip` bei vorhandenem `Accept-Encoding: gzip` in der Anfrage. Für `/xmltv.gz` — `Content-Disposition: inline; filename="xmltv.xml.gz"`.

Das Verbot des clientseitigen Cachens ist beabsichtigt: XMLTV ändert sich bei jedem EIT-Import oder Refresh externer XMLTV-Quellen, und der Player darf veraltete Daten nicht unbegrenzt vorhalten. Ein Edge-Cache (nginx) ist hingegen voll akzeptabel — siehe Abschnitt zur Leistung weiter unten.

### Server-Cache und sein Zurücksetzen

Das fertige XMLTV wird im Speicher des PSS-Prozesses gecacht:

- Ein Eintrag pro `channel-set`; intern werden beide Körpervarianten (raw und gzip) vorgehalten — wiederholte Anfragen mit `Accept-Encoding: gzip` oder `/xmltv.gz` komprimieren die Daten nicht erneut.
- Jeder Eintrag erhält einen `update-time`-Zähler. Jede EPG-Aktualisierung (EIT-Import, XMLTV-Quellen-Refresh) erhöht den Zähler, und der Cache wird bei der nächsten Anfrage neu aufgebaut.

Cache erzwungen leeren:

```
POST /xmltv/reset-cache
```

Die Route wird vom **Admin-Server** (Port `43971`/`43981`) bedient, nicht vom epg-server. Der Anfragekörper ist leer; die Antwort ist `200 OK` mit einem JSON-Umschlag.

### HTTP-Antwortcodes

| Code | Bedingung |
| --- | --- |
| `200 OK` | Anfrage verarbeitet. Der Körper ist ein XMLTV-Dokument (eventuell ein leeres `<tv></tv>` bei einem vorübergehenden DB-Fehler). |
| `401 Unauthorized` | Weder Digest noch `l/p`-Parameter haben die Prüfung bestanden (für nicht-Loopback-Anfragen). |
| `403 Forbidden` | Der Login existiert, aber die Anfrage kommt nicht von einer erlaubten IP, oder `limit-day` ist abgelaufen. |
| `404 Not Found` | Jede URL außer `/xmltv` und `/xmltv.gz`. |
| `405 Method Not Allowed` | Methode ist nicht GET. |

Der Fehlerkörper ist ein JSON-Umschlag mit festem Format:

```json
{"status": 401, "message": "Unauthorized"}
```

### Leistung und Skalierung

#### Server-Cache

Der Server-Cache bedient wiederholte Anfragen an dasselbe `channel-set` ohne SQLite-Zugriff — durch Kopieren des fertigen Körpers.

Der XMLTV-Aufbau „von Grund auf“ (Cache-Miss) ist teurer: pro Kanal ein eigenes SELECT über `channel_name`, pro Ereignis eines über `event_text` und `event_rating`. Anhaltspunkte zur Bauzeit:

| Ausgabegröße | Cache hit | Cache miss (build) |
| --- | --- | --- |
| 100 Kanäle / Tag | einige Dutzend ms | ~0,5–1 s |
| 500 Kanäle / Tag | ~50 ms | 2–5 s |
| 1000+ Kanäle / Woche | ~100–300 ms | 5–15 s |

Für die meiste Middleware ist es akzeptabel, XMLTV alle paar Stunden oder einmal täglich abzufragen.

#### Wann ein externer Reverse-Proxy (nginx) nötig ist

Im Gegensatz zu `/data/epg/channel` (kurze JSON-Antworten) ist XMLTV ein einziges großes Dokument pro `channel-set` und damit ideal für einen Edge-Cache:

- **Zehn bis Hunderte Clients pro channel-set** — der interne PSS-Cache reicht meist aus, wenn sie XMLTV stündlich bis täglich abfragen.
- **Tausende gleichzeitige Clients** — ein cachender Reverse-Proxy wird empfohlen. Das Ausliefern einer XMLTV-Datei an Hunderte/Tausende Anfragen ist in erster Linie eine Netzwerklast (Hunderte KB – einige MB pro Antwort), die man besser von PSS fernhält.
- **Geografisch verteilte Auslieferung** — ohne CDN/Edge-Cache geht es nicht, unabhängig von der Clientzahl.

PSS sendet `Cache-Control: no-cache`, daher muss nginx explizit angewiesen werden, den Upstream-Header zu ignorieren und einen eigenen TTL zu führen.

#### Beispielkonfiguration für nginx

```nginx
# /etc/nginx/conf.d/pss-xmltv.conf

proxy_cache_path /var/cache/nginx/pss-xmltv
                 levels=1:2
                 keys_zone=pss_xmltv:8m
                 max_size=4g
                 inactive=2h
                 use_temp_path=off;

upstream pss_epg {
    server 127.0.0.1:10444;
    keepalive 16;
}

server {
    listen 80;
    # listen 443 ssl http2;     # SSL termination makes sense here
    server_name epg-files.example.com;

    # Do not enable gzip on: /xmltv.gz is already compressed, and /xmltv
    # arrives gzip-encoded from PSS — re-compressing is pointless.

    location ~ ^/xmltv(\.gz)?$ {
        proxy_pass http://pss_epg;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # PSS returns no-cache; force-cache it at the edge.
        proxy_ignore_headers Cache-Control Expires Set-Cookie;
        proxy_hide_header   Cache-Control;
        proxy_hide_header   Pragma;
        proxy_hide_header   Expires;

        # Cache key = full URL including query (login/password in /xmltv?l=...&p=...)
        # produce distinct keys for different accounts with different channel-sets.
        proxy_cache_key     "$scheme$host$request_uri";

        proxy_cache         pss_xmltv;
        proxy_cache_valid   200      30m;       # XMLTV changes infrequently
        proxy_cache_valid   401 403  1m;
        proxy_cache_lock           on;          # coalesce cache misses
        proxy_cache_lock_timeout   60s;         # XMLTV build may take seconds
        proxy_cache_use_stale      error timeout updating
                                   http_500 http_502 http_503;

        # Large buffer: XMLTV for a big channel-set can reach
        # several megabytes.
        proxy_buffering      on;
        proxy_buffers        16 256k;
        proxy_buffer_size    256k;
        proxy_busy_buffers_size 1m;

        # Let the client cache for a short period.
        add_header Cache-Control "public, max-age=600";
        add_header X-Cache-Status $upstream_cache_status always;
    }
}
```

#### Erläuterungen und Empfehlungen

- **TTL ``proxy_cache_valid 200 30m``** — XMLTV ändert sich selten häufiger als alle halbe Stunde. Erfolgt die Synchronisation mit den Quellen stündlich oder seltener, kann der Wert auf 1 Stunde und mehr angehoben werden; ist Frische nach `POST /xmltv/reset-cache` wichtig, senken Sie ihn.
- **``proxy_cache_lock_timeout 60s``** — gegenüber `/data/epg/channel` erhöht (dort üblich sind 5 Sekunden), weil der Aufbau eines XMLTV für ein großes channel-set länger dauert.
- **Eine dedizierte ``keys_zone``** — selbst bei einer großen Installation sind eindeutige XMLTV-Schlüssel rar (Anzahl Logins × `channel-set`), 8 MB reichen mit Reserve. `max_size` wird nach dem XMLTV-Volumen, nicht nach der Schlüsselanzahl bemessen.
- **gzip auf nginx-Seite ist unnötig**: Für `/xmltv.gz` ist die Antwort bereits komprimiert, für `/xmltv` antwortet PSS selbst gzip-encoded bei vorhandenem `Accept-Encoding: gzip`.
- **HTTPS-Terminierung** auf nginx liefert bessere Leistung bei vielen gleichzeitigen TLS-Handshakes.

### Verwandte Endpunkte

- `POST /xmltv/reset-cache` — erzwungenes Leeren des serverseitigen XMLTV-Caches (auf dem Admin-Server `43971`/`43981`).
- `POST /data/epg/update?s=<src_id>` — erzwungene Aktualisierung einer externen XMLTV-Quelle; nach einer erfolgreichen Aktualisierung wird der serverseitige XMLTV-Cache automatisch geleert.
- `GET /data/epg/channel?…` — JSON-EPG-Ausgabe für einen Kanal pro Tag; siehe separaten Abschnitt.

Die vollständige Liste und ausführliche Beschreibung der HTTP-API finden Sie in `manual/http_data_api.txt`.

## EPG für OTT-Middleware

Der Server liefert Programmdaten (EPG) für den gewählten Tag und einen einzigen Kanal im JSON-Format. Der Endpunkt ist für OTT-Middleware-Backends gedacht, die den Programmplan aus Perfect Streamer aggregieren, um das Programm beim Endkunden aufzubauen.

### URL und Authentifizierung

Der Endpunkt wird vom integrierten Admin-Server bereitgestellt. Standardmäßig ist er unter den Ports `43971` (HTTP) und `43981` (HTTPS) erreichbar; die Ports werden unter **Settings/Server Settings** konfiguriert.

```
GET /data/epg/channel?src=<src_id>&ch=<channel_id>&lang=<lang>&t=<time>
```

Die Authentifizierung erfolgt per HTTP Digest, wie bei den übrigen `/data/*`. Für Middleware reicht ein Konto mit der Rolle `viewer` (nur lesen).

> **Bemerkung**
>
> Anfragen von der Loopback-Adresse (`127.0.0.1`) umgehen die HTTP-Digest-Prüfung — der Server behandelt sie als anonym. Das ist praktisch für lokale Skripte und Health-Checks von Middleware, die auf derselben Maschine wie Perfect Streamer läuft; für entfernte Zugriffe sind Zugangsdaten zwingend.

### Anfrageparameter

| Parameter | Typ | Pflicht | Standard | Beschreibung |
| --- | --- | --- | --- | --- |
| `src` | vorzeichenlose Ganzzahl | nein | `0` | EPG-Quelle. `0` — aus MPEG-TS-EIT der Eingangsströme importierte Daten. `1, 2, …` — Eintrags-ID aus `/config/epg/epg-source` (externe XMLTV-Quelle). |
| `ch` | Zeichenkette | **ja** | — | Kanal-Kennung aus der EPG-Datenbank: Wert des Feldes `channel_id` in der Tabelle `channel`. Die Liste der verfügbaren Kennungen lässt sich per SQL-Abfrage über `POST /data/epg/sql` ermitteln. |
| `lang` | Ganzzahl oder ISO 639 | nein | Systemstandardsprache | `0` — Systemstandardsprache; eine Ganzzahl > 0 — interne Sprach-ID (siehe `GET /schema/lang`); eine Zeichenkette — zwei- oder dreibuchstabiger ISO-639-Code, z. B. `eng`, `rus`, `fra`. |
| `t` | vorzeichenlose Ganzzahl (Unix-Epoche, Sek.) | nein | aktuelle Serverzeit | Beliebiger Zeitpunkt innerhalb des interessierenden Tages. Der Server liefert Ereignisse für den **UTC-Tag**, zu dem `t` gehört: Intervall `[t / 86400 · 86400, (t / 86400 + 1) · 86400)`. Für Daten des «nächsten Tages» reicht es, `86400` zur aktuellen Zeit zu addieren. |

> **Bemerkung**
>
> Der Tag wird in UTC gerechnet, nicht in der lokalen Zeitzone. Baut die Middleware den Zeitplan nach dem lokalen Kalendertag auf, fällt die UTC-Tagesgrenze nicht zwingend auf die lokale Mitternacht; in diesem Fall sind zwei Anfragen (für zwei benachbarte UTC-Tage) nötig, deren Ergebnisse über das Feld `start` zusammengeführt werden.

### Antwortformat

- `Content-Type`: `application/json`.
- Die HTTP-Header verbieten das Caching auf der Client-Seite und auf zwischengeschalteten Proxys:

  ```
  Cache-Control: no-cache, no-store, must-revalidate
  Pragma: no-cache
  Expires: 0
  ```
- Der Antwortkörper ist ein JSON mit der Ereignisliste für den Tag:

```json
{
  "event": [
    {"start": 1715000000, "end": 1715003400, "title": "Morning News", "desc": "Overview of the day's events"},
    {"start": 1715003400, "end": 1715007000, "title": "Weather Forecast", "desc": ""}
  ]
}
```

Ereignisfelder:

- `start`, `end` — Anfang und Ende der Sendung in Unix-Epoche (Sek.), UTC.
- `title` — Sendungstitel in der gewählten Sprache. Fehlt ein Titel in der angeforderten Sprache, greift der Server auf den Eintrag in der Systemstandardsprache zurück und danach auf jede andere verfügbare Sprache.
- `desc` — erweiterte Beschreibung. Kann ein leerer String sein, wenn die Datenbank keine separate Beschreibung für das Ereignis enthielt.

Besonderheiten der Ausgabe:

- Ereignisse mit leerem Titel und ohne Beschreibung sowie Ereignisse mit ungültigen Zeitstempeln werden aus der Ausgabe ausgeschlossen.
- Duplikate nach `start` werden ausgesondert: Für denselben Startzeitpunkt wird ein Datensatz mit der besten Sprachpriorität zurückgegeben (angefordert → System-Default → andere).
- Die Reihenfolge der Ereignisse im Array ist nicht garantiert — bei Bedarf sortiert die Middleware die Liste selbst nach dem Feld `start`.
- Wenn der Kanal nicht gefunden wird, die Quelle nicht existiert oder am gewählten Tag keine Ereignisse vorliegen, gibt der Server `200 OK` mit dem leeren Array `{"event":[]}` zurück — oder, im seltenen Fall einer fehlenden Quelle, mit leerem Körper. Die Middleware muss beide Varianten korrekt behandeln.

### Caching auf dem Server

Das fertige JSON wird vom Server unter dem Schlüssel `(channel_id, UTC-Datum, Sprache)` gecacht; wiederholte Anfragen für denselben Tag und Kanal werden daher ohne Datenbankzugriff bedient. Die Middleware muss am Cache nichts steuern.

Der Cache wird automatisch zurückgesetzt:

- beim Eintreffen neuer Ereignisse aus MPEG-TS-EIT der Eingangsströme (`src=0`);
- bei einer erfolgreichen Aktualisierung einer externen XMLTV-Quelle (`src > 0` — geplant oder erzwungen über `POST /data/epg/update?s=<src_id>`);
- wenn ein Tag aus dem EPG-Aufbewahrungsfenster fällt.

Ein separater Endpunkt zum erzwungenen Leeren des Caches ist weder vorgesehen noch erforderlich.

### HTTP-Antwortcodes

| Code | Bedingung |
| --- | --- |
| `200 OK` | Anfrage verarbeitet. Der Körper ist ein JSON mit der Ereignisliste (eventuell leer). |
| `400 Bad Request` | Der Parameter `ch` fehlt oder ist leer; oder `src`, `t` lassen sich nicht als vorzeichenlose Ganzzahl parsen; oder ein numerischer `lang` verweist auf eine nicht existierende Sprach-ID. |
| `401 Unauthorized` | Die HTTP-Digest-Authentifizierung fehlt oder ist ungültig (für Anfragen nicht von einer Loopback-Adresse). |

Sonstige Situationen (unbekanntes `src`, fehlender Kanal, keine Ereignisse am Tag) führen nicht zu 4xx — die Middleware erhält `200 OK` mit dem leeren Array `{"event":[]}`.

Bei einem Fehler ist der Antwortkörper ein JSON-Umschlag mit festem Format:

```json
{"status": 400, "message": "Bad Request"}
```

Das Feld `status` dupliziert den HTTP-Code; das Feld `message` enthält eine konkretere Fehlerursache auf Englisch (oder den Standard-HTTP-Statustext, falls keine weiteren Informationen vorliegen). Der `Content-Type` der Fehlerantwort ist `application/json`.

### Beispiel

```bash
curl -u middleware:secret --digest \
  'http://pss.example.com:43971/data/epg/channel?src=0&ch=12.0.1&lang=rus&t=1715000000'
```

Beispielantwort:

```json
{
  "event": [
    {"start": 1715000000, "end": 1715003400, "title": "Morning News", "desc": "Overview of the day's events"},
    {"start": 1715003400, "end": 1715007000, "title": "Weather Forecast", "desc": ""}
  ]
}
```

### Leistung und Skalierung

#### Perfect-Streamer-Server-Cache

Im PSS-Prozess gibt es einen In-Memory-LRU-Cache der Antworten mit dem Schlüssel `(channel_id, UTC-Datum, Sprache)` und einem Hard-Cap von 1024 Einträgen je EPG-Quelle. Bei typischer Last (zig bis hunderte Kanäle × 1–3 Sprachen × `keep-day` Tage) passen alle aktuellen Einträge vollständig in den Cache; wiederholte Anfragen werden ohne SQLite-Zugriff bedient.

Größenordnung (Debug-Build, lokales Loopback, ohne HTTPS):

| Szenario | Latenz (Einzelanfrage) | Durchsatz (P=8) |
| --- | --- | --- |
| Cache hit | ~0,3 ms | ~1100 Anfragen/s |
| Cache miss (SQL + JSON) | ~1,0–1,5 ms | ~1000 Anfragen/s |

In einem Release-Build und ohne Debug-Logging sind die Werte etwa 2- bis 3-mal besser. Bandbreite — etwa 14 KB pro Antwort für einen typischen Kanal pro Tag.

#### Wann ein externer Reverse-Proxy (nginx) nötig ist

Der Server-Cache beschleunigt wiederholte Anfragen für dasselbe `(channel, Tag, Sprache)`, aber jede Anfrage geht weiterhin durch den eingebauten PSS-HTTP-Server und belegt einen Thread aus dessen Pool. Bei vielen Clients ist es sinnvoll, das Caching an den Rand zu verlagern:

- **bis ~1.000 Online-Clients** — der interne Cache reicht meist aus, ein Reverse-Proxy ist nicht erforderlich.
- **Zehntausende und mehr** — ein cachender Reverse-Proxy (z. B. nginx) wird empfohlen. Ein Edge-Cache wickelt 99 % der Anfragen ohne PSS ab, dämpft Spitzen (Middleware-Start, massenhafte Player-Aktualisierung) und erlaubt es, die SSL-Terminierung auf einen separaten Knoten zu verlagern.
- **geografisch verteilte Auslieferung** — ein externes CDN/Proxy ist nötig, noch bevor die Clientzahl gezählt wird.

PSS sendet einen eigenen Header `Cache-Control: no-cache, no-store, must-revalidate`, damit Endclients das EPG nicht lange zwischenspeichern. Der Reverse-Proxy darf (und sollte) die Antwort selbst cachen — unten wird gezeigt, wie man nginx explizit anweist, den Upstream-`Cache-Control` zu ignorieren und einen eigenen TTL zu führen.

#### Beispielkonfiguration für nginx

Eine minimale Konfiguration für einen EPG-Edge-Cache, ausgelegt für Zehntausende Clients mit Polling-Intervall 1–5 Minuten:

```nginx
# /etc/nginx/conf.d/pss-epg.conf

proxy_cache_path /var/cache/nginx/pss-epg
                 levels=1:2
                 keys_zone=pss_epg:32m
                 max_size=2g
                 inactive=30m
                 use_temp_path=off;

upstream pss_admin {
    server 127.0.0.1:43971;
    keepalive 64;
}

server {
    listen 80;
    # listen 443 ssl http2;  # recommended: SSL termination here
    server_name epg.example.com;

    # gzip helps: typical EPG JSON compresses ~5–8x.
    gzip on;
    gzip_types application/json;
    gzip_min_length 512;
    gzip_proxied any;

    location = /data/epg/channel {
        proxy_pass http://pss_admin;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # PSS returns no-cache; force-cache it at the edge.
        proxy_ignore_headers Cache-Control Expires Set-Cookie;
        proxy_hide_header   Cache-Control;
        proxy_hide_header   Pragma;
        proxy_hide_header   Expires;

        # Cache key = full URL with query string. The src/ch/lang/t
        # parameters already determine response uniqueness.
        proxy_cache_key     "$scheme$host$request_uri";

        proxy_cache         pss_epg;
        proxy_cache_valid   200 60s;          # staleness TTL
        proxy_cache_valid   400 404 10s;
        proxy_cache_lock           on;        # coalesce cache misses
        proxy_cache_lock_timeout   5s;
        proxy_cache_use_stale      error timeout updating
                                   http_500 http_502 http_503;

        # Serve the JSON to the client with its own TTL
        # (the player won't recheck EPG before that).
        add_header Cache-Control "public, max-age=60";
        add_header X-Cache-Status $upstream_cache_status always;
    }
}
```

#### Erläuterungen und Empfehlungen

- **TTL ``proxy_cache_valid 200 60s``** — Kompromiss zwischen EPG-Frische und Last auf dem Upstream. Das Programm ändert sich nicht in Echtzeit, daher sind 30–300 Sekunden angemessen. Nach dem Import neuer Ereignisse leert PSS seinen Cache sofort, der Edge-Cache zieht beim nächsten TTL nach.
- **``proxy_cache_lock on``** ist bei vielen Clients zwingend: Bei einem Cache-Miss bündelt es parallele Anfragen auf denselben Schlüssel zu einer einzigen Upstream-Anfrage und schützt SQLite vor Spitzen-`BUSY` unter Last.
- **``keys_zone`` und ``max_size``** werden anhand der Anzahl `(Kanal × Tag × Sprache)` dimensioniert: 32 MB `keys_zone` reichen für Hunderttausende Schlüssel; 2 GB `max_size` decken einen Monat Verlauf für Hunderte Kanäle mit Reserve ab.
- **gzip** reduziert den Verkehr deutlich: Antworten lassen sich gut komprimieren (sich wiederholende JSON-Schlüssel, Kyrillisch in UTF-8).
- **``X-Cache-Status``** in der Antwort erlaubt der Middleware, `HIT`/`MISS`/`EXPIRED` zu sehen und die Effektivität des Caches zu beurteilen.
- Liegen nginx und PSS auf derselben Maschine, verlangt der Admin-Server für Loopback keine HTTP-Digest-Authentifizierung, daher kann der Upstream-Block ohne `proxy_set_header Authorization …` bleiben. Bei einer Trennung über Netze legen Sie ein dediziertes `viewer`-Konto an und ergänzen Sie die Digest-Authentifizierung in `proxy_pass`.
- **HTTPS** wird sinnvollerweise an nginx terminiert: PSS unterstützt HTTPS direkt, aber ein Edge-Server ist meist effizienter bei der Abwicklung von TLS-Handshakes für Tausende gleichzeitige Clients.

### Verwandte Endpunkte

- `POST /data/epg/sql?s=<src_id>` — beliebige SQL-Abfrage an die EPG-Datenbank (insbesondere, um eine Liste der `channel_id` zu erhalten).
- `POST /data/epg/update?s=<src_id>` — erzwungene Aktualisierung einer externen XMLTV-Quelle.

Die vollständige Liste und ausführliche Beschreibung der HTTP-API finden Sie in `manual/http_data_api.txt`.

## Programmoptimierung

Treten bei vielen **stream**s Probleme mit hoher CPU-Last oder Speichermangel auf, können die Einstellungen optimiert werden.

You can disable the MPEG-TS filtering and processing features if you don’t need to.By default, the **stream** has the **Clean All Unnecessary Data** function enabled, disable it if there is no unwanted data in the stream. Disabling these features completely will remove the **Original Media Information** section of the Report.

Vollständige Deaktivierung oder Änderung der **Mosaic**-Einstellungen. Eine vollständige Deaktivierung erfolgt in den Server-Einstellungen. Sie können sie individuell für jeden **stream** deaktivieren oder das Aktualisierungsintervall mit der Einstellung **Check Interval** ändern.

**Anstieg des Speicherverbrauchs bei einer großen Anzahl von Streams.** PSS gibt nicht genutzten Speicher periodisch an das Betriebssystem zurück, und die mitgelieferte *systemd*-Unit begrenzt standardmäßig die Anzahl paralleler Speicherzuteilungs-Pools (Umgebungsvariable **MALLOC_ARENA_MAX**). Das bremst den allmählichen Anstieg des **RSS** beim Betrieb mit Dutzenden von Streams und ist keine Folge eines Lecks. Den Wert manuell zu ändern ist in der Regel nicht erforderlich.

### Queue-Overload-Fehler für die Datenbanken DBStat und DBEPG

Treten auf, wenn die Datenbankleistung nicht ausreicht — langsame Festplatte oder überlastetes System.

Den Speicherort der Datenbanken legt der Parameter `data-dir` in der Konfigurationsdatei `pss.properties` fest.

Mögliche Lösungen:

1. Verschieben der Datenbankdateien nach /tmp. Es wird der Systemspeicher verwendet — eine Abschätzung des verfügbaren Speichers und Anpassung der Statistik-Aufbewahrungsdauer ist erforderlich (siehe Server-Einstellungen). Bei einem System-Neustart gehen die Daten verloren.
2. Detaillierungsgrad der Statistik reduzieren — siehe Parameter dbstat-detail. Standard 5 s, lässt sich bis 20 erhöhen.
3. Die EPG-Datenbank im Arbeitsspeicher halten — Parameter `dbepg-memory=true` setzen.

## Transkoder

Die Transkoder sind als eigenständige Binärdateien implementiert, die von pstreamer als separate Prozesse gestartet werden.

1-zu-N-Konfigurationen werden unterstützt — von einem Decoder können mehrere Streams mit unterschiedlichen Encoder-Einstellungen erzeugt werden.

Der Quellstream muss Video und Audio enthalten; Varianten ohne Video oder ohne Ton werden nicht unterstützt.

![Transcoder-Pipeline](../_images/pipeline_transcoder.svg)

*Transcoder-Architektur: ein gemeinsamer **decoder** → **N** unabhängige Zweige *VPP + encoder* (1:N); Audio ist mehrspurig mit optionaler Transcodierung pro Output; Untertitel und Teletext werden durchgereicht.*

Implementierte Codecs:

- Video SW decoder: mpeg2, h.264, hevc (h.265)
- Video NW decoder: mpeg2, h.264, hevc (h.265)
- Video SW encoder: mpeg2, h.264, hevc (h.265)
- Video NW encoder: h.264, hevc (h.265)

Interlaced Streams werden am Eingang und Ausgang unterstützt.

Für H.264- und HEVC-Decoder wird Interlace Alternate (zwei getrennte Halbbilder im Stream) unterstützt; es wird in Interlace Interleaved umgewandelt.

Der HEVC-Decoder unterstützt das Profil Main10 mit bt.709 (SDR) und bt.2020 (HDR). Der HEVC-Encoder nutzt stets das Profil Main mit bt.709.

Für die H.264- und HEVC-Decoder wird VBR (Variable Frame Rate) unterstützt; es wird in eine konstante Framerate umgewandelt.

- Audio decoder — mpeg (layer 1,2,3), aac, ac3
- Audio encoder — mpeg (layer 2), aac

Es gibt den Transkodierungsmodus **Video Passthrough** — Video wird nicht transkodiert, nur der Ton; verwendet wird der *SW*-Transkoder.

> **Bemerkung**
>
> Für die Transkodierung müssen zwei oder mehr Streams konfiguriert werden — mit output (Decoder) und input (Encoder).

Zur Konfiguration einer Transkoder-Instanz ist nötig:

- Quelle — im Stream-Output-Transkoder (Decoder) hinzufügen. In den Einstellungen Typ wählen: SW, NV oder Video Passthrough.
- Ausgangsstream — im Stream-Input-Transkoder (Encoder) hinzufügen; in den Einstellungen die Quelle (Decoder) wählen.
- Wiederholen, wenn mehrere Ausgangsstreams für einen Decoder benötigt werden.

### Einstellungen des Output-Transkoders (Decoder)

- **Convert colors to BT.709** — Konvertierung von SD BT.470-2 (PAL) und SMPTE 170M (NTSC) nach BT.709
- **Trace** — für die Diagnose das detaillierte Transkoder-Log aktivieren.

Für den korrekten Betrieb des Transkoders muss der Quellstream bestimmte Anforderungen erfüllen; in einigen Fällen lässt sich das korrigieren. Diese Einstellungen konvertieren den Stream nicht — sie wirken als Hinweise für den korrekten Betrieb des Transkoders.

Für die Korrektur der Eingangsdaten gibt es folgende Einstellungen:

- **Fix PAR** — Pixel Aspect Ratio korrigieren. Wird als Bruchzahl im Format N/D angegeben; für Wide SD z. B. 16/9.
- **Fix Framerate** — Framerate explizit angeben. In manchen Streams kann die Framerate im SPS fehlen, im Transkoder-Log erscheint dann der entsprechende Fehler. In diesen Fällen ist die Framerate manuell anzugeben. Wird als Bruch im Format N/D angegeben.

Beispiele für Framerate-Werte:

- PAL — 25/1
- NTSC — 30/1 oder 30000/1001
- Cinema — 24/1 oder 24000/1001

### Einstellungen des Input-Transkoders (Encoder)

- **Encoder Type** — Video-Codec.
- **Align Total Bitrate** — Stuffing-Bitrate des Streams (Auffüllen mit null-Paketen). Wichtig, wenn der Stream für DVB-Übertragung genutzt wird. Die Bitrate muss garantiert höher sein als die Bitrate von Video und allen Audio-Spuren.
- **Video Profile** — für H.264 lässt sich das Kodierungsprofil wählen.
- **Video Bitrate** — Bitrate des Videostreams in kbps. Die Kodierung läuft stets in CBR; die Gesamtbitrate ist durch die Tonspuren höher.
- **Speed Preset** — Voreinstellungen für die Kodierung, Werte 1–7. Niedriger = höhere Qualität und höherer Ressourcenbedarf. Standard 4.
- **GOP Interval** — Intervall in Frames für die GOP (dieser Parameter entspricht dem Key Frame Interval). Standard 25 (1 Sekunde bei 25p), empfohlener Wert, wenn Player die Wiedergabe an einem beliebigen Punkt des Streams beginnen.
- **BFrame** — für höhere Qualität aktivieren. Empfohlener Wert: 3.
- **Lookahead** — für höhere Qualität aktivieren. Empfohlen: 20–50 Frames.
- **Resize** — Bildgrößenänderung.
- **Deinterlace** — wandelt Interlace in Progressive um.

Einfügen eines *crop* (leere Ränder am Bild) wird nicht unterstützt. Beliebige Bildgrößen werden nicht zugelassen, da Seitenverhältnisse verzerrt würden.

Für **resize** stehen folgende Optionen zur Verfügung:

- Größe proportional um Faktor 2 und 4 verkleinern.
- Format Wide SD 16:9 setzen, der passende Aspect Ratio wird gewählt.
- Upscale SD→HD. Wird auf SD-PAL/NTSC-Quellen angewandt. Interlace wird nicht unterstützt — bei Bedarf vorher deinterlacen.
- Breite festlegen. Die Höhe wird proportional berechnet.
- Höhe festlegen. Die Breite wird proportional berechnet.

Einige Parameter können mit dem gewählten Transkoder unverträglich sein; Fehler erscheinen in dessen Log.

### Audioverarbeitung

Standardmäßig werden alle Tonspuren ohne Verarbeitung von Input zu Output durchgereicht. Unnötige Spuren lassen sich über PID-Filter im Stream entfernen.

Wenn der Ton transkodiert werden soll, lässt sich das über Regeln pro Audio-Codec konfigurieren. Option *skip* — Tonspur mit diesem Codec entfernen.

Fehlen im Ausgangsstream Tonspuren, kommt es zu einem Fehler — siehe Transkoder-Logs.

### PCR-Erzeugung und TR 101 290.

Der MPEG-TS-Multiplexer erzeugt einen neuen PCR. Bei korrekt gesetztem **Align Total Bitrate** (über der Summe aus Video- und Audiobitrate) sollte der PCR die TR-101-290-Prüfung bestehen.

### Status der Transkoder

Bei Problemen im Transkoder-Betrieb (kein Stream vom Encoder) sind die Logs im Bereich **Transcoders** zu prüfen — dort ist die Liste der Instanzen zu sehen (jede Zeile ist eine separate Instanz, decoder + N encoders); beim Klick auf eine Instanz öffnet sich der Log-Status-Dialog. Angezeigt werden das aktuelle Log und das Log des vorherigen Starts. Für ein detailliertes Log aktivieren Sie *trace* in den output (decoder)-Einstellungen.
