Servicio OTT¶
Entrega flujos mediante protocolos basados en HTTP — HLS (sobre MPEG-TS), MPEG-DASH y Low-Latency HLS (sobre CMAF — MP4 fragmentado, desde la versión 1.13), así como MPEG-TS over HTTP. Se admiten HTTPS (SSL) y HTTP/3 (QUIC). La entrega se activa en la pestaña OTT de los ajustes de Stream.
Las URL de conexión tienen el formato:
http://host:port/http/stream/login/password — autorización por login y contraseña
http://host:port/http/stream/login — autorización por login (token)
http://host:port/http/stream/ — autorización por IP
host y port se configuran en los ajustes de http server.
stream — ID del stream. No confundir con el número de orden en la lista de streams. El ID se muestra en la parte superior de la página de estadísticas del stream y en la columna ID de la lista de streams; se establece al crear el stream y nunca cambia.
De forma análoga para HLS, DASH y Low-Latency HLS (los dos últimos — solo en OTT/HLS/LL-HLS/LL-Dash, véase más abajo):
En la página de estadísticas del stream se muestran las URLs de los protocolos conectados (como plantilla) y su estado actual. El acceso no autorizado está prohibido; los clientes deben estar declarados en Peers.
Para HLS y DASH la URL admite parámetros adicionales (opcionales):
[URL]?a=1&s=40&m=40&v=5&h3=1
a: 1 — ruta absoluta en la playlist, 0 — ruta relativa (por defecto).
s: duración de la playlist dinámica (s), 40 s por defecto.
m: duración mínima de la playlist dinámica (s), 40 s por defecto. Tamaño máximo de la playlist dinámica 60 s. Si el tamaño actual del búfer de chunks es menor que el tamaño mínimo solicitado en la petición, se devuelve un error 404. Así HLS arranca con un búfer de chunks lleno en el servidor.
v: la versión del protocolo HLS emitida en la lista de reproducción. Por defecto, el valor depende del modo HLS (véase más abajo): OTT/HLS y OTT/HLS/LL-HLS/LL-Dash — 6, Peering/HLS — 3. Un valor explícito en la URL anula el valor por defecto. Cambiar la versión puede ser necesario para la compatibilidad con un cliente HLS concreto.
h3: opt-in a HTTP/3 (QUIC) para esta sesión OTT. Hace que el servidor emita una cabecera
Alt-Svcen la respuesta, tras lo cual un reproductor / navegador compatible cambia a QUIC. Véase la sección HTTP/3 (QUIC) más abajo.
Para compatibilidad con algunos clientes HLS se puede añadir el nombre de archivo index.m3u8 a la URL, p. ej. http://host:port/hls/stream/login/password/index.m3u8.
El modo de entrega se establece con el ajuste de stream OTT HLS (pestaña OTT): Peering/HLS, OTT/HLS o OTT/HLS/LL-HLS/LL-Dash.
Peering/HLS — un modo con división simple en segmentos (chunks). Recomendado para el peering (distribución) de flujos. Se entrega solo HLS sobre MPEG-TS (/hls). Por defecto, la lista de reproducción se sirve como EXT-X-VERSION:3 para compatibilidad con los clientes peer.
OTT/HLS — un modo con una división de segmentos optimizada para un inicio rápido de los reproductores en la difusión OTT. En este modo la carga de CPU es mayor; se recomienda para la difusión. Se entrega HLS sobre MPEG-TS (/hls). Por defecto, la lista de reproducción se sirve como EXT-X-VERSION:6 con EXT-X-INDEPENDENT-SEGMENTS y el atributo CHARACTERISTICS en EXT-X-MEDIA TYPE=SUBTITLES (Apple HLS, hls.js, Safari, dash.js/Shaka). Si un cliente concreto necesita un valor anterior, establézcalo mediante el parámetro de consulta ?v= (véase la lista de parámetros más arriba).
OTT/HLS/LL-HLS/LL-Dash — un modo de entrega sobre CMAF (MP4 fragmentado, fMP4; desde la versión 1.13). El stream genera segmentos fMP4 (.m4s + un init.mp4 común, mimeType="video/mp4"), sobre los que se entregan:
MPEG-DASH en
/dash— ahora sobre CMAF, y no sobre MPEG-TS;Low-Latency HLS en
/llhls(véase Low-Latency HLS más abajo);con el ajuste de stream Enable TS Chunk activado (por defecto true) — adicionalmente HLS legacy sobre MPEG-TS (
/hls), como en OTT/HLS; con false se entregan solo segmentos fMP4, lo que ahorra disco y CPU.
La lista de reproducción HLS en OTT/HLS/LL-HLS/LL-Dash también es EXT-X-VERSION:6 por defecto.
Nota
MPEG-DASH y Low-Latency HLS solo están disponibles en OTT/HLS/LL-HLS/LL-Dash. En OTT/HLS y Peering/HLS se entrega exclusivamente HLS sobre MPEG-TS.
Se puede habilitar SSL (HTTPS) en el servidor HTTP desde sus ajustes.
Chunk Min Interval y Chunk Max Interval
En modo OTT se analiza el flujo en busca de PAT/PMT/SPS/PPS/IFrame y los chunks se recortan según el criterio de arranque rápido de los reproductores. El análisis comienza en min interval y, si por alguna razón los datos no se encuentran, el chunk se recorta forzosamente en max interval.
GOP-aligned segments
En OTT/HLS, el segmentador alinea los límites de los chunks con los puntos de acceso aleatorio y distingue entre IDR y un I-frame ordinario. Si la fuente emite con closed-GOP (con IDR), cada segmento TS .ts comienza garantizadamente con SPS / PPS / IDR (para HEVC — también VPS) — un verdadero punto de entrada desde el cual el reproductor abre el flujo. Si la fuente es open-GOP / sin IDR, el límite es el I-frame más cercano (el comportamiento anterior). El segmentador recorta la «cola» del GOP anterior — los slices P / B — de la ventana entre el PAT inicial y el primer SPS del chunk. Esto:
elimina el cuadro negro inicial al comienzo de la sesión VOD (
?t=/?epg=) en hls.js, Safari y VLC: el decodificador MSE recibe el conjunto correcto de unidades NAL de cabecera ya en el primer segmento y arranca de inmediato;alinea la salida con HLS RFC 8216 §3 («Each Media Segment MUST contain a SPS and a PPS that decode its first Access Unit»);
hace correcta la declaración EXT-X-INDEPENDENT-SEGMENTS en una playlist EXT-X-VERSION:6.
Se controla con el ajuste por flujo gop-aligned-segment (por defecto true). Se aplica solo cuando HLS = OTT/HLS: en Peering/HLS y para los flujos MPTS el comportamiento no cambia. El PSI (PAT / PMT) y el audio se conservan íntegramente; el único efecto secundario es un salto de un cuadro del continuity_counter en el PID de vídeo en la frontera entre dos chunks contiguos, lo que constituye un decode boundary legítimo para HLS / DASH MSE.
Al alinear con los puntos de entrada, la duración real del chunk puede redondearse hacia arriba más allá de Chunk Min Interval. Por ello, en la lista de reproducción live el valor EXT-X-TARGETDURATION refleja la duración de segmento real máxima (sección 4.3.3.1 de RFC 8216) en lugar del mínimo configurado. Esto mantiene el manifiesto dentro del estándar: hls.js y los reproductores compatibles no acortan el intervalo de actualización de la lista de reproducción ni emiten bufferStalledError falsos.
Low-Latency HLS (/llhls)
En OTT/HLS/LL-HLS/LL-Dash, además de /dash está disponible un endpoint Low-Latency HLS aparte — entrega de baja latencia sobre los mismos segmentos fMP4 de CMAF:
La lista de reproducción de medios se divide en segmentos parciales (parts, directivas #EXT-X-PART): el reproductor inicia la reproducción sin esperar a que el segmento completo esté listo. Se aplican la recarga bloqueante de la lista (el servidor retiene la solicitud hasta que el siguiente part esté listo) y la sugerencia de precarga #EXT-X-PRELOAD-HINT.
La duración objetivo de un part se establece con el ajuste de stream Part Target Duration (ms; por defecto 500, rango 100–5000). El valor se aplica al vuelo, sin reiniciar el flujo, y debe ser menor que Chunk Min Interval.
HLS / DASH / LL-HLS Adaptive Multistream
HLS Adaptive Multistream se admite desde la versión 1.10, DASH Adaptive Multistream desde la versión 1.12 (desde la versión 1.13 — sobre CMAF), y Low-Latency HLS Adaptive desde la versión 1.13.
Para los flujos adaptativos se configura una lista de reproducción aparte. Para ello:
Activar la entrega OTT en los flujos que formarán parte de la lista adaptativa: OTT/HLS — para HLS adaptativo sobre MPEG-TS; OTT/HLS/LL-HLS/LL-Dash — para DASH / Low-Latency HLS adaptativos sobre CMAF.
En el menú principal aparecerá una sección de flujos adaptativos. Allí hay que añadir un flujo e indicar todos los flujos que deben incluirse en esta lista de reproducción.
Los flujos pueden tener establecido un parámetro de bitrate. Por defecto es 0 — el bitrate se toma del valor medido; de lo contrario, puede establecerse de forma explícita.
Para playlists adaptativas la URL es distinta:
A los peers (clientes) se les pueden imponer restricciones de acceso a los flujos adaptativos, igual que a los normales. El permiso para un flujo adaptativo incluye el permiso para todos los flujos que lo componen.
HTTP/3 (QUIC)¶
Desde la versión 1.13.1.438, Perfect Streamer incorpora un servidor HTTP/3 integrado para la entrega OTT de HLS, MPEG-DASH y Low-Latency HLS sobre QUIC (RFC 9000 + RFC 9114). La pila es ngtcp2 (QUIC v1) + nghttp3 (HTTP/3 frame layer); la infraestructura TLS reutiliza el mismo certificado que el listener HTTPS.
QUIC sirve únicamente rutas OTT:
/— redirección raíz,/hls/...,/dash/...y/llhls/...— master playlists / MPD,/h<sessID>/...— URL por sesión (media playlists, segmentos, VTT),/http/<stream>/...— MPEG-TS bruto sobre HTTP.
Low-Latency HLS y DASH se entregan sobre QUIC de forma incremental (chunked): las parts se envían al cliente a medida que están listas, sin esperar al segmento completo.
Las rutas administrativas (/data, /config, /xmltv, /db/, /login, /logout, /restart) devuelven 404 sobre QUIC — la API de administración permanece en HTTPS / HTTP-TCP. Se trata de una restricción deliberada para reducir la superficie de ataque del listener QUIC.
Activación¶
Los ajustes de QUIC se encuentran en la sección Configuration / HTTP server (nodo /config/http-server):
HTTP/3 Enable (
http3-enable) — indicador global que activa el listener QUIC. Valor por defecto: off. Al activarlo se abre un socket UDP enhttp3-port; al desactivarlo se cierra.HTTP/3 Port (
http3-port) — puerto UDP del listener QUIC. Valor por defecto: 43984. QUIC opera sobre UDP y HTTPS sobre TCP; los puertos pueden coincidir o diferir, sin conflicto entre ellos.HTTP/3 0-RTT Enable (
http3-zero-rtt-enable) — permite el handshake 0-RTT (RFC 9001 §4.6.1) en conexiones reanudadas del mismo cliente. Reduce la latencia de inicio; debe considerarse el riesgo de reproducción para solicitudes no idempotentes (es seguro para cargas OTT de sólo lectura). Valor por defecto: on.
Los cambios de configuración se aplican en caliente, sin reiniciar el servicio.
El certificado y la clave se toman del listener HTTPS — no existe una configuración de certificado independiente para HTTP/3. Si HTTPS no está configurado (ssl-enable=false), activar HTTP/3 no surte efecto — el handshake no se completará.
El parámetro ?h3 — opt-in por sesión¶
Un navegador no se conecta directamente a un endpoint HTTP/3 — primero accede por HTTPS/TCP, recibe en la respuesta la cabecera Alt-Svc: h3=":<port>"; ma=86400 (RFC 7838), y sólo las solicitudes posteriores a ese origin se conmutan a QUIC.
En Perfect Streamer, la emisión de Alt-Svc es opt-in por sesión OTT. El servidor no anuncia QUIC a todos los clientes indistintamente: el cliente debe solicitar HTTP/3 de forma explícita mediante el parámetro de consulta ?h3 en la URL master HLS / DASH.
Valor en la URL |
Valor opt-in |
|
|---|---|---|
parámetro ausente |
off (default) |
no |
|
on |
sí |
|
on |
sí |
|
explicit off |
no (como si estuviera ausente) |
Una vez que el cliente ha obtenido la URL de sesión /h<sess>/..., el indicador wantH3 queda almacenado en la sesión — todas las actualizaciones posteriores de media playlist, los GET de segmentos y los GET de fragmentos VTT bajo esa URL de sesión reciben también Alt-Svc, aunque ?h3 no figure en la propia solicitud. Sin opt-in en el master no se instaura comportamiento sticky.
Filtrado de Alt-Svc:
El listener QUIC está activado globalmente (
http3-enable=true); de lo contrario el anuncio se suprime aunque se especifique?h3.La solicitud no llegó por QUIC — en solicitudes que ya se sirven sobre H3,
Alt-Svccarece de sentido y no se emite.Por sesión o por URL
wantH3=true(véase la tabla anterior).Los servidores de administración y EPG nunca emiten
Alt-Svc— no disponen de listener QUIC.
Este comportamiento es conforme con la RFC 7838 §3 — el propio servidor decide en qué respuestas emite Alt-Svc.
Escenario de conmutación a QUIC en el navegador¶
Flujo típico (Chrome / Firefox / Safari):
El reproductor abre la URL master con opt-in explícito:
https://stream.example.com:41982/hls/test1/login/password/index.m3u8?h3=1
La primera solicitud va por HTTPS/TCP. En la respuesta el servidor emite
Alt-Svc: h3=":43984"; ma=86400.El navegador almacena la asociación
stream.example.com:41982 → h3=":43984". En Chrome puede verse en la páginachrome://net-internals/#alt-svc; en Firefox —about:networking#http3.En las solicitudes posteriores al mismo origin (actualización de playlist, GET de segmentos, VTT) el navegador abre una conexión QUIC en
udp/43984y continúa sobre HTTP/3. En DevTools → Network la columna Protocol de esas solicitudes pasa ah3.
Si la conexión QUIC no puede establecerse (puerto UDP bloqueado por el cortafuegos, certificado no confiable en el sistema), el navegador permanece de forma transparente en HTTPS/TCP — funcionalmente el flujo continúa reproduciéndose sin interrupción.
Contabilización de clientes y supervisión¶
La dirección IP real de un cliente QUIC en la contabilización de pares activos (/data/http-clients, límites de conexiones concurrentes, ACL por IP) es la dirección de pair real de la conexión QUIC, no la de loopback del puente backend interno.
El atributo ott-type en /data/http-clients es compuesto, con el formato <PROTO>/<scheme>:
PROTO— protocolo OTT:HLS,DASHoHTTP.scheme— el transporte de red real:http,httpsoquic.
Ejemplos: HLS/quic, DASH/https, HTTP/http. La interfaz de administración muestra tanto el protocolo OTT como el transporte de red de cada cliente en una sola columna.
El tiempo de espera de una sesión OTT es de 60 segundos, independientemente del transporte. Cuando el cliente cambia entre transportes, el esquema solo se actualiza hacia arriba según la cadena de prioridad http → https → quic. Un retroceso paralelo a una conexión menos segura no sobrescribe el tipo actual — en la contabilización permanece el transporte más seguro observado.
Compatibilidad de reproductores¶
HTTP/3 para OTT está soportado por:
iOS AVPlayer / Safari — de forma nativa, mediante
Alt-Svc; con 0-RTT.Chrome, Firefox, Edge, Brave — mediante
Alt-Svc; HLS se reproduce con hls.js y DASH con dash.js / Shaka; el tráfico del reproductor hereda HTTP/3 de la fetch API del navegador.Android ExoPlayer — mediante el motor QUIC Cronet (no es el transporte predeterminado; requiere configuración en el cliente).
VLC (libVLC) no soporta HTTP/3 — en estos clientes el flujo sigue funcionando sobre HTTPS/TCP, sin el beneficio de QUIC.
La ventaja real de QUIC frente a HTTPS/TCP se aprecia sobre todo en redes móviles / Wi-Fi / redes con pérdidas:
ausencia de head-of-line blocking en la capa TCP — una pérdida en un flujo HTTP no retrasa a los demás;
handshake 0-RTT en conexiones reanudadas del mismo cliente;
tasa de bits ABR más estable con pérdidas de paquetes del 1 al 5 %.
En redes gestionadas / Wi-Fi corporativo, la ventaja suele ser modesta — del 5 al 10 % en la latencia de inicio y prácticamente nula en régimen estacionario. La carga de CPU del servidor es mayor con QUIC que con el TCP del kernel — es el coste de una pila TLS + transporte en espacio de usuario.
Modelo de caché para OTT HLS y DASH¶
El servidor genera respuestas en tres categorías, que difieren en la vida útil del contenido y en su idoneidad para cacheo en nodos intermedios (reverse proxy, CDN, caché del cliente).
1. Modelo de caché¶
1.1. Recursos y cabeceras HTTP¶
Recurso |
URL |
Content-Type |
Cache-Control |
|---|---|---|---|
Segmento TS (HLS) |
|
|
|
Segmento fMP4 (DASH / LL-HLS) |
|
|
|
DASH MPD |
|
|
|
HLS master |
|
|
|
HLS media |
|
|
|
302 Redirect |
|
— |
|
Raw TS |
|
|
no establecido; no se cachea |
1.2. Características de los segmentos¶
El identificador hexadecimal del segmento en la URL (<keyHex> en las rutas /h<sess>/<keyHex>.ts) se calcula como un CRC64 sobre el tiempo de inicio del segmento y el ID del flujo, y es globalmente único. La URL del segmento direcciona contenido inmutable — ante solicitudes repetidas a la misma URL se devuelve un flujo de bytes idéntico (mientras el segmento permanezca dentro de la ventana deslizante).
La directiva immutable suprime la revalidación condicional del cliente (If-None-Match, If-Modified-Since). El valor max-age=60 es compatible con un timeShiftBufferDepth=40s típico.
Los segmentos fMP4 de CMAF (.m4s) y el init.mp4 común para DASH / Low-Latency HLS se direccionan de forma análoga y se almacenan en caché según el mismo modelo (immutable, max-age=60). Los segmentos parciales (parts) de LL-HLS son solicitudes byte-range dentro del mismo .m4s, por lo que no forman una entrada de caché aparte.
1.3. Características de los manifest¶
max-age=1 limita el límite superior de obsolescencia del contenido en caché a un segundo. Junto con proxy_cache_lock on (nginx), los picos de solicitudes al manifiesto se coalescen en una única solicitud al origen por segundo.
1.4. Variabilidad del contenido¶
Con absPath=0 (valor por defecto, sin el parámetro de URL a) los manifiestos HLS media y DASH MPD no incluyen el identificador de sesión en el cuerpo. El contenido del manifiesto es idéntico entre sesiones que pertenecen a la misma combinación (stream, param). Esto permite que la caché del reverse-proxy reutilice una única entrada entre sesiones cuando la clave de caché está normalizada.
Con absPath=1 (parámetro de URL a=1) el cuerpo del manifiesto contiene URL absolutas que incluyen el esquema, el host y el identificador de sesión. El contenido pasa a ser específico de la sesión; la reutilización de caché entre sesiones no está disponible.
2. Comportamiento de clientes¶
Cliente |
URL de actualización del manifest |
Impacto en el número de sesiones |
|---|---|---|
VLC 3.x HLS |
|
Una sesión por reproducción |
VLC 3.x DASH |
|
Se gestiona mediante session reuse (ver 3.3) |
ffmpeg 5.x HLS |
|
Una sesión por reproducción |
ffmpeg 5.x DASH |
|
Se gestiona mediante session reuse (ver 3.3) |
dash.js, hls.js |
|
Una sesión por reproducción |
3. Mecanismos especializados¶
3.1. Redirect HTTP 302 para DASH¶
Una solicitud de la forma /dash/<stream>/<login>/<pass>/index.mpd devuelve la respuesta 302 Found con la cabecera Location: /h<sess>/index.mpd. El cuerpo de la respuesta es vacío. La autenticación y la asignación de la sesión ocurren durante el procesamiento de la redirección.
Los clientes que admiten caché de redirecciones acceden directamente a la URL de la sesión en las solicitudes posteriores. Los clientes que no la admiten repiten la solicitud de redirección. El coste del reprocesamiento de la redirección se limita a la verificación de autenticación y a las operaciones de session reuse.
3.2. Session reuse para DASH¶
Al procesar una solicitud /dash/.../index.mpd del mismo login al mismo flujo (con el mismo indicador adaptive), el servidor encuentra una sesión DASH ya existente y devuelve su identificador de nuevo. No se crea una nueva sesión; no se consume un slot del límite de conexiones simultáneas.
Solo se aplica a DASH. Para HLS no se necesita un mecanismo de reuse separado: los clientes HLS actualizan la media playlist mediante la URL de sesión y no crean una nueva sesión en cada actualización.
3.3. Reutilización de segmentos entre sesiones¶
La ruta /h<sess>/<keyHex>.ts no depende de <sess> al resolver <keyHex> en contenido: <keyHex> identifica de forma globalmente única un segmento TS dentro de un flujo. Nginx con una clave de caché normalizada (que recorta el prefijo /h<sess>/) sirve cada solicitud del mismo <keyHex> desde una única entrada de caché, sin importar qué clientes la hayan emitido.
Lo mismo vale para los segmentos fMP4 de CMAF (.m4s) e init.mp4: su contenido es inmutable y se direcciona dentro del stream, por lo que la normalización de la cache key produce la misma deduplicación entre sesiones en la caché.
4. Parámetros de la petición¶
Parámetro |
Valor por defecto |
Impacto |
|---|---|---|
|
|
|
|
|
|
|
|
Longitud mínima de ventana para emitir el manifest |
|
|
|
|
ausente (off) |
opt-in a HTTP/3 (QUIC) — hace que el servidor emita |
Cambiar un parámetro mediante query string actualiza los valores guardados en la sesión en su próxima reapertura.
5. Características de carga¶
La carga sobre el origen escala con el número de streams distintos vistos simultáneamente. El aumento del número de clientes que ven el mismo stream no incrementa el número de solicitudes al origen cuando hay un caché reverse-proxy con clave de caché normalizada.
Escenario |
Frecuencia de peticiones origin (ref.) |
|---|---|
1 cliente por flujo X |
MPD: 0.4 req/s, segment: 0.2 req/s |
N clientes en un mismo flujo X (caché activada) |
MPD: 1 req/s, segment: 0.2 req/s |
N clientes ffmpeg en modo de reproducción sobre un mismo flujo |
MPD: 1 req/s (con |
N clientes en N flujos distintos |
MPD: 0.4·N req/s, segment: 0.2·N req/s |
6. Nginx como reverse proxy con caché¶
6.1. Configuración base¶
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. Función de las directivas¶
Directiva |
Propósito |
|---|---|
|
Serializa las peticiones upstream cuando hay cache miss simultáneos para la misma clave |
|
Devuelve la copia obsoleta a peticiones paralelas durante la actualización de la caché |
|
Usa |
|
Prohíbe la caché de errores de autorización y 404 |
|
Mantiene un pool de conexiones persistentes al origin |
|
Para los segmentos; activa el bufferizado de la respuesta en nginx |
|
Para la sección |
6.3. Cálculo de max_size del caché de segmentos¶
Valor orientativo: bitrate × timeShiftBufferDepth × distinct_streams × 2
Ejemplo: 10 flujos × 8 Mbps × 40 s × 2 ≈ 800 MB. Se recomienda un margen 10× para absorber la variabilidad del bitrate.
6.4. Terminación TLS¶
El servidor Perfect Streamer acepta conexiones en los puertos HTTP y HTTPS. Con terminación TLS en nginx, el upstream usa el puerto HTTP. El reenvío de las cabeceras X-Forwarded-Proto y X-Forwarded-Host es obligatorio para la formación correcta de URL absolutas cuando 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;
# + caching directives from 6.1
}
}
server {
listen 80;
server_name stream.example.com;
return 301 https://$host$request_uri;
}
Para HTTPS entre nginx y origin se usan proxy_ssl_verify y proxy_ssl_trusted_certificate. Para conexiones loopback el cifrado es redundante.
6.5. Multi-host¶
Si un mismo proceso nginx atiende varios server_name, se añade $host a la cache key para aislar el contenido:
map $uri $pss_cache_key {
~^/h[0-9a-f]{16}(?<tail>/.+\.(ts|m3u8))$ "$host:stream:$tail";
default "$host:$uri";
}
El tamaño de keys_zone se calcula como 8000 claves/MB. Para instalaciones multi-host con miles de flujos se recomienda keys_zone=...:300m o superior.
7. Caché del cliente¶
Cache-Control: immutable lo respetan los navegadores Chrome/Firefox/Safari. El caché del cliente devuelve el segmento sin solicitud condicional al volver a acceder (incluido seek hacia atrás dentro del búfer del reproductor).
Los Service Workers pueden aplicar una estrategia cache-first basada en el contenido de Cache-Control. Los reproductores DASH (dash.js, Shaka) usan MSE a través de SourceBuffer; un segmento colocado en el búfer permanece disponible sin nueva solicitud HTTP hasta que sale de la ventana.
Para solicitudes entre dominios, la cabecera Access-Control-Allow-Origin: * permite el caché en shared caches sin Vary: Origin. Cambiar el valor de ACAO a un Origin específico requiere Vary: Origin, lo que reduce la eficiencia del shared cache.
8. Distribución mediante CDN¶
Perfect Streamer es compatible con CDNs en modo pull-from-origin (Cloudflare, Akamai, Fastly, BunnyCDN, Amazon CloudFront).
Origin shield. Se recomienda colocar uno o varios nodos shield entre el edge del CDN y el origin para reducir la frecuencia de peticiones al origin cuando los clientes están distribuidos globalmente.
Purge. Los segmentos content-addressed no necesitan purge. Si cambian los metadatos del stream (códec, resolución), los manifiestos se actualizan dentro de max-age=1 sin purge explícito.
Cache warming. Ante un aumento previsto de carga en un stream, se puede precalentar la CDN desde varios puntos geográficos antes del inicio de la emisión.
Distribución geográfica. Los segmentos (max-age=60) son adecuados para caché distribuido geográficamente. Los manifiestos (max-age=1) toleran hasta un segundo de retardo de entrega — aceptable para live no low-latency.
9. Monitorización¶
9.1. X-Cache-Status¶
Añadir add_header X-Cache-Status $upstream_cache_status; en cada location con caché. Valores:
Valor |
Descripción |
|---|---|
|
Respuesta desde caché |
|
No estaba en caché; obtenido del origin y almacenado |
|
Caducado, actualizado |
|
Copia stale devuelta a una solicitud paralela durante la actualización |
|
|
|
Origin devolvió 304 Not Modified |
|
Se activó |
9.2. Formato de access-log¶
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. Métricas¶
El módulo nginx-vts exporta métricas por zona en formato Prometheus:
GET /status/format/prometheus
Umbrales recomendados para alertas:
Métrica |
Umbral |
Causa posible |
|---|---|---|
Segment HIT rate |
< 90 % en 5 minutos |
Normalización de cache key rota; |
Manifest MISS rate |
> 50 % en 1 minuto |
|
Upstream response time p95 |
> 500 ms en 1 minuto |
Sobrecarga del origin |
Cache zone fill |
> 90 % en 10 minutos |
Aproximándose a |
10. Diagnóstico¶
Síntoma |
Causa probable |
Solución |
|---|---|---|
Tasa HIT de segmento baja |
|
Comprobar las cabeceras y la regex en la directiva |
404 en segmentos tras salir de la ventana |
404 cacheado para un segmento que salió de la ventana deslizante |
Añadir |
Retraso del inicio de reproducción 2–5 s |
|
Reducir a 1–2 s; activar |
El manifest no se actualiza |
|
Establecer explícitamente |
Aumento de TIME_WAIT en upstream |
Falta |
Añadir |
403 en |
El cliente resuelve URLs relativas frente a la URL previa al redirect |
El servidor emite |
Lags, rebuffering frecuente en clientes remotos |
Throughput TCP efectivo bajo debido a slow start e idle restart con RTT grandes (300 ms y superiores) |
Ajuste de la pila de red de Linux en el origin: véase 10.1 |
10.1. Ajuste TCP del origin para clientes high-RTT¶
El problema se manifiesta en clientes con un RTT grande hasta el origin (por ejemplo 300 ms o superior) cuando el bitrate del flujo se acerca a la capacidad del canal. Síntomas en el reproductor (VLC, ffmpeg, dash.js) — rebuffering frecuente, warnings del tipo ES_OUT_SET_PCR called too late (aumento de pts_delay), buffer deadlock prevented, interrupciones del flujo. En el servidor, mientras tanto, el cliente parece normal, no hay errores, y el throughput en /data/stream/... se corresponde con el flujo de entrada.
Causa:
TCP slow start. Cada nueva conexión TCP comienza con un congestion window de aproximadamente 14 KB y lo aumenta a lo largo de varios RTT. Con un RTT de 300 ms, alcanzar la ventana completa lleva 2-3 segundos. Durante ese tiempo, un segmento HLS/DASH de 5 s de duración (4-6 MB) se descarga apreciablemente más lento que en tiempo real.
TCP idle restart. Entre solicitudes de segmentos, un cliente HLS en pull-model hace una pausa de 4-5 s. Por defecto, tras tal pausa el kernel de Linux restablece el congestion window de la conexión al initial cwnd (comportamiento
net.ipv4.tcp_slow_start_after_idle=1). Como resultado, en el siguiente GET la conexión keep-alive reanuda la transmisión desde slow start — incluso en una sesión ya calentada.
Una agravante adicional — el congestion control CUBIC por defecto se comporta mal con RTT largos y pérdidas de paquetes en tramos intermedios de la red.
Solución — dos parámetros sysctl en el origin:
# 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
Para una aplicación persistente:
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
El efecto principal lo proporciona el primer parámetro (tcp_slow_start_after_idle=0). Elimina directamente el slow start repetido entre solicitudes de segmentos dentro de una misma conexión keep-alive. El segundo (BBR) aporta robustez adicional y se aplica a todas las nuevas conexiones.
El ajuste no requiere reiniciar Perfect Streamer y se aplica a todas las nuevas conexiones TCP inmediatamente después de sysctl -w. Las conexiones existentes conservan el congestion control con el que fueron establecidas.
11. Seguridad¶
11.1. Session URL¶
Una URL en el formato /h<sess>/... cumple la función de token de sesión — no requiere reautenticación. La duración está acotada por el idle timeout (valor 30 s). En caso de inactividad, la sesión es eliminada por la tarea cleaner.
Requisitos:
HTTPS en todas las rutas OTT (
/hls/,/dash/,/h<sess>/) en producciónEl Session ID en la cabecera
Locationdel 302 no se cachea (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;
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;
}
}
Las URLs de sesión (/h<sess>/) no requieren rate limiting — su procesamiento es barato y las respuestas se cachean.
11.3. Caché de respuestas de error¶
proxy_cache_valid 200 60s;
proxy_cache_valid 301 302 0s;
proxy_cache_valid 404 403 0s;
proxy_cache_valid any 1s;
Prohíbe la caché de redirecciones (sess único en Location) y de respuestas con errores de autorización o recurso inexistente.
11.4. Restricción del acceso de red al origin¶
El puerto 41972 (41982 para HTTPS) debe estar cerrado al tráfico externo. Configuraciones admitidas:
Bindear Perfect Streamer a
127.0.0.1(con nginx local)Regla de firewall:
iptables -A INPUT -p tcp --dport 41972 ! -s 10.0.0.0/8 -j DROP
12. Integración con middleware¶
12.1. Modelo prefix-login¶
Perfect Streamer permite delegar la identificación de usuarios a middleware/sistema de facturación mediante el mecanismo prefix-login. El conector externo al sistema de facturación no se incluye en la versión actual.
Configuración del usuario embedded:
{
"id": 9,
"login": "sub",
"password": "xxx",
"is-prefix": true,
"max-conn-http-hls": 1,
"accept-stream": [ ... ]
}
Con is-prefix: true el servidor acepta URLs cuyo login tiene la forma <prefix><billing_user_id>:
/dash/test1/sub42/xxx/index.mpd
/hls/test1/sub43/xxx/index.m3u8
12.2. Formato de estadísticas¶
<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>
El campo login-id contiene el hash del login URL. login es el valor configurado. match-login es el login URL usado por el cliente.
12.3. Limitaciones del prefix-login¶
Contraseña compartida. Todos los suscriptores del pool prefix usan un único valor de contraseña. Su compromiso otorga acceso a cualquier
<prefix><string>.Granularidad de ACL.
accept-streamse aplica a todo el pool prefix; no hay ACL por suscriptor sin facturación externa.Rotación de contraseña. El cambio de contraseña desconecta a todos los suscriptores activos. Una sustitución progresiva requiere el uso temporal de dos prefix-logins.
13. Subtítulos WebVTT¶
La fuente de subtítulos es DVB Teletext / DVB Subtitling del MPEG-TS de entrada. Las pistas de subtítulos Teletext deben estar presentes en las secciones Media Information o Original Media Information. La sección Analyzer también permite verificar que los paquetes de los PID correspondientes están activos.
Para OTT HLS/DASH debe activarse el modo OTT (en Peering/HLS los subtítulos WebVTT no están disponibles). En la sección Output # OTT el contador de chunks OTT WebVTT buffer chunk count debe ser distinto de cero.
Para diagnosticar los subtítulos, activar Analyze y Trace en el stream. Al iniciar el flujo, el log del stream debe mostrar:
Start Teletext subtitle decoder
[ttxsubdec] ttx: pid=331 magazine=8 page=0x88 lang=***
A continuación, el log registra el texto decodificado de los subtítulos.
13.1. URL de los segmentos VTT¶
Esquema |
URL |
Contenido |
|---|---|---|
HLS master |
|
|
HLS subtitle playlist |
|
lista |
Segmento VTT HLS |
|
VTT con X-TIMESTAMP-MAP estilo HLS |
DASH MPD AdaptationSet |
en |
|
Segmento VTT DASH |
|
VTT con X-TIMESTAMP-MAP estilo DASH |
<keyHex> es un CRC64 hex de 16 caracteres a partir del tiempo de inicio del segmento, el ID del flujo y el PID de la pista de subtítulos. <seq> es el número decimal secuencial de un chunk del flujo de subtítulos (la numeración de subtítulos no está relacionada con la de los chunks TS).