OTT-Dienst¶
Liefert Streams über HTTP-basierte Protokolle — HLS, MPEG-DASH (ab Version 1.12) und MPEG-TS over HTTP. HTTPS (SSL) wird unterstützt. Die Ausgabe wird im Tab OTT der Stream-Einstellungen aktiviert.
Die Verbindungs-URLs haben das Format:
http://host:port/http/stream/login/password — Autorisierung per Login und Passwort
http://host:port/http/stream/login — Autorisierung per Login (Token)
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 und DASH:
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 sind in der URL zusätzliche Parameter verfügbar (optional):
[URL]?a=1&s=40&m=40&v=5
a: 1 — absolute Pfade in der Playlist (Standard), 0 — relative Pfade.
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 unterhalb des im Request angeforderten Minimums, wird Fehler 404 zurückgegeben. Damit wird sichergestellt, dass HLS aus einem gefüllten Chunk-Buffer auf dem Server startet.
v: in der Playlist angegebene HLS-Protokollversion. Standard 5; bei einigen HLS-Clients muss die Version geändert werden.
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.
Der HLS-Server hat zwei Modi — Peer mode und OTT mode.
Peer mode — Modus mit einfacher Segment-(Chunk-)Aufteilung. Empfohlen für Peering / Stream-Verteilung.
OTT mode — Modus mit für OTT-Streaming optimierter Segmentierung für schnellen Player-Start. Höhere CPU-Last; empfohlen fürs Broadcasting.
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 mit min interval; werden die Daten aus irgendeinem Grund nicht gefunden, wird der Chunk zwangsweise nach max interval geschnitten.
HLS Adaptive Multistream
Ab Version 1.10 wird HLS Adaptive Multistream unterstützt, ab Version 1.12 zusätzlich DASH Adaptive Multistream
Für adaptive Streams wird eine separate HLS-Playlist konfiguriert. Dafür sind folgende Schritte nötig:
Bei den Streams, die in die adaptive Playlist aufgenommen werden, HLS mit OTT-Modus aktivieren.
Im Hauptmenü erscheint der Bereich für adaptive Streams. Dort einen Stream anlegen und alle in die Playlist einzubindenden Streams eintragen.
Streams kann ein Bitrate-Parameter gesetzt werden. Standard 0 bedeutet, dass die gemessene Bitrate verwendet wird; andernfalls lässt sie sich explizit festlegen.
Für adaptive Playlists gilt eine andere URL:
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.
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 |
|
|
|
DASH MPD |
|
|
|
HLS master |
|
|
|
HLS media |
|
|
|
302 Redirect |
|
— |
|
Raw TS |
|
|
nicht gesetzt; nicht gecacht |
1.2. TS-Segment-Eigenschaften¶
Der Identifier keyID wird als CRC64(startTime || streamID) gebildet und ist global eindeutig. Eine Segment-URL adressiert unveränderliche Inhalte — bei wiederholten Anfragen derselben URL wird identischer Byte-Stream zurückgegeben (solange das Segment innerhalb des gleitenden Fensters liegt).
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.
1.3. Manifest-Eigenschaften¶
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 keine Sitzungs-Identifier im Body. Der Manifest-Inhalt ist zwischen Sitzungen mit derselben (stream, param)-Kombination identisch. So kann der Reverse-Proxy-Cache einen Eintrag bei normalisiertem Cache-Key über Sitzungen hinweg 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 |
|
Eine Sitzung pro Wiedergabe |
VLC 3.x DASH |
|
Wird per Session-Reuse behandelt (siehe 3.3) |
ffmpeg 5.x HLS |
|
Eine Sitzung pro Wiedergabe |
ffmpeg 5.x DASH |
|
Wird per Session-Reuse behandelt (siehe 3.3) |
dash.js, hls.js |
|
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 der Anfrage /dash/.../index.mpd unter login-id L für stream-id S und Flag adaptive=A: existiert in _ottClientList bereits eine DASH-Sitzung mit denselben (L, S, A), wird deren sessID zurückgegeben. Es wird keine neue Sitzung angelegt, kein maxConn-Slot verbraucht.
Gilt nur für DASH. Für HLS ist kein eigener Reuse-Mechanismus nötig: HLS-Clients aktualisieren die Media-Playlist über die Session-URL und lösen bei jedem Refresh kein applyNewOTTSess aus.
3.3. Wiederverwendung von Segmenten zwischen Sitzungen¶
Der Pfad /h<sess>/<keyID>.ts ist beim Auflösen von keyID auf Inhalt sess-unabhängig: keyID identifiziert das Segment innerhalb der registrierten ChunkList eindeutig (siehe _ottStreamList). Nginx mit normalisiertem Cache-Key (Entfernung des Präfixes /h<sess>/) bedient alle Anfragen derselben keyID aus einem einzigen Cache-Eintrag.
4. Anfrageparameter¶
Parameter |
Standardwert |
Auswirkung |
|---|---|---|
|
|
|
|
|
|
|
|
Mindestfensterlänge für die Manifest-Ausgabe |
|
|
|
Das Ändern des Parameters via Query-String aktualisiert die in der Sitzung gespeicherten Werte beim nächsten applyNewOTTSess-Aufruf.
5. Lastcharakteristiken¶
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 |
N Clients auf N verschiedene Streams |
MPD: 0.4·N req/s, segment: 0.2·N req/s |
6. Nginx als cachender Reverse-Proxy¶
6.1. Basiskonfiguration¶
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$" {
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 |
|---|---|
|
Serialisiert Upstream-Anfragen bei parallelen Cache-Misses auf denselben Schlüssel |
|
Liefert die veraltete Kopie an parallele Anfragen während der Cache-Aktualisierung |
|
Nutzt |
|
Verbietet das Caching von Authorization-Fehlern und 404 |
|
Verwaltet einen Pool persistenter Verbindungen zum Origin |
|
Für Segmente; aktiviert die Antwortpufferung in nginx |
|
Für den Bereich |
6.3. Berechnung der max_size des Segmentcaches¶
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.
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;
# + директивы кеширования из 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:
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. Verteilung ü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 |
|---|---|
|
Antwort aus Cache |
|
War nicht im Cache; vom Origin geholt und gespeichert |
|
Abgelaufen, erneuert |
|
Stale-Kopie an parallele Anfrage während der Aktualisierung ausgeliefert |
|
|
|
Origin lieferte 304 Not Modified |
|
|
9.2. Access-Log-Format¶
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; |
Manifest MISS rate |
> 50 % über 1 Minute |
|
Upstream response time p95 |
> 500 ms über 1 Minute |
Origin-Überlastung |
Cache zone fill |
> 90 % über 10 Minuten |
Annäherung an |
10. Diagnose¶
Symptom |
Wahrscheinliche Ursache |
Lösung |
|---|---|---|
Niedrige Segment-HIT-Rate |
|
Header und Regex in der |
404 bei Segmenten nach Verlassen des Fensters |
Gecachter 404 bei Segment, das aus dem Sliding Window gefallen ist |
|
Playback-Start-Verzögerung 2–5 s |
|
Auf 1–2 s senken; |
Manifest aktualisiert nicht |
|
|
Wachsendes TIME_WAIT am Upstream |
|
|
403 auf |
Der Client löst relative URLs gegen die Pre-Redirect-URL auf |
Der Server gibt |
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 ProduktionDie Session-ID im
Location-Header der 302 wird nicht gecacht (no-cache, no-store)
11.2. Rate limiting¶
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;
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;
}
}
Session-URLs (/h<sess>/) benötigen kein Rate Limiting — die Verarbeitung ist günstig, Antworten werden gecacht.
11.3. Caching von Fehlerantworten¶
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:
Perfect Streamer an
127.0.0.1binden (bei lokalem nginx)Firewall-Regel:
iptables -A INPUT -p tcp --dport 41972 ! -s 10.0.0.0/8 -j DROP
12. Middleware-Integration¶
12.1. 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 des Prefix-Logins¶
Gemeinsames Passwort. Alle Subscriber des Prefix-Pools nutzen ein einziges Passwort. Eine Kompromittierung gewährt Zugriff auf jedes
<prefix><string>.ACL-Granularität.
accept-streamgilt für den gesamten Prefix-Pool; eine subscriber-spezifische ACL gibt es ohne externes Billing nicht.Passwort-Rotation. Eine Änderung trennt alle aktiven Subscriber. Für eine schrittweise Ablösung sind vorübergehend zwei Prefix-Logins nötig.
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 Peer mode 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. URLs der VTT-Segmente¶
Schema |
URL |
Inhalt |
|---|---|---|
HLS master |
|
|
HLS subtitle playlist |
|
Liste |
HLS-VTT-Segment |
|
VTT mit HLS-spezifischer X-TIMESTAMP-MAP |
DASH MPD AdaptationSet |
in |
|
DASH-VTT-Segment |
|
VTT mit DASH-spezifischer X-TIMESTAMP-MAP |
<keyHex> ist der 16-stellige Hex-Wert aus CRC64(startTime, streamID, pid). <seq> ist die dezimale Chunk-Nummer im Subtitle-Storage (eigener Zähler getrennt vom Video-Storage).