Servicio OTT¶
Emite flujos por protocolos basados en HTTP — HLS, MPEG-DASH (desde la versión 1.12) y MPEG-TS over HTTP. Soporta HTTPS (SSL). La emisión se activa en la pestaña OTT de los ajustes 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 similar para HLS y DASH:
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 la URL admite parámetros adicionales (opcionales):
[URL]?a=1&s=40&m=40&v=5
a: 1 — rutas absolutas en la playlist (por defecto), 0 — rutas relativas.
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 mínimo solicitado, se devolverá el error 404. Esto se hace para que HLS arranque desde un búfer de chunks ya lleno en el servidor.
v: versión del protocolo HLS indicada en la playlist. Por defecto 5; algunos clientes HLS pueden requerir cambiarla.
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.
Hay 2 modos de funcionamiento del servidor HLS — Peer mode y OTT mode.
Peer mode — modo con segmentación (chunks) simple. Recomendado para peering / distribución de flujos.
OTT mode — modo con una segmentación optimizada para emisión OTT y arranque rápido de los reproductores. La carga de CPU es mayor; recomendado para emisión.
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 PAT/PMT/SPS/PPS/IFrame y los chunks se cortan según el criterio de inicio rápido de los reproductores. El análisis comienza en min interval; si por alguna razón los datos no se encuentran, el chunk se corta forzosamente en max interval.
HLS Adaptive Multistream
Desde la versión 1.10 se soporta HLS Adaptive Multistream y desde la 1.12 DASH Adaptive Multistream
Para los flujos adaptativos se configura una playlist HLS dedicada. Para ello:
En los flujos que se incluirán en la playlist adaptativa, activar HLS con OTT Mode.
En el menú principal aparecerá la sección de flujos adaptativos. Allí hay que añadir un flujo e indicar todos los flujos que deben incluirse en esa playlist.
A los flujos se les puede asignar un parámetro de bitrate. Por defecto 0 significa que se usa el valor medido; en caso contrario se puede fijar explícitamente.
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.
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 |
|
|
|
DASH MPD |
|
|
|
HLS master |
|
|
|
HLS media |
|
|
|
302 Redirect |
|
— |
|
Raw TS |
|
|
no establecido; no se cachea |
1.2. Características de los segmentos TS¶
El identificador keyID se forma como CRC64(startTime || streamID) y es globalmente único. La URL de un segmento direcciona contenido inmutable — ante solicitudes repetidas de 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.
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 (por defecto, sin el parámetro de URL «a»), los manifiestos HLS media y DASH MPD no contienen el identificador de sesión en el cuerpo. El contenido del manifiesto es idéntico entre sesiones pertenecientes a una misma combinación (stream, param). Esto permite que el caché del reverse-proxy reutilice una entrada entre sesiones con normalización de la clave de caché.
Con absPath=1 (parámetro de URL «a=1»), el cuerpo del manifiesto contiene URL absolutas que incluyen esquema, host e identificador de sesión. El contenido se vuelve específico de la sesión — la reutilización del caché entre sesiones deja de ser posible.
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¶
En el procesamiento de la solicitud /dash/.../index.mpd ejecutada bajo login-id L para stream-id S y la flag adaptive=A, si en _ottClientList ya existe una sesión DASH con los mismos (L, S, A), se devuelve su sessID. No se crea una nueva sesión y no se consume ningún slot maxConn.
Solo se aplica a DASH. HLS no necesita un mecanismo de reuse separado: los clientes HLS actualizan la media playlist mediante la URL de sesión y no disparan applyNewOTTSess en cada refresh.
3.3. Reutilización de segmentos entre sesiones¶
La ruta /h<sess>/<keyID>.ts es independiente de sess al resolver keyID en contenido: keyID identifica de forma unívoca el segmento dentro de las ChunkList registradas (ver _ottStreamList). Nginx con clave de caché normalizada (eliminando el prefijo /h<sess>/) sirve todas las solicitudes del mismo keyID desde una única entrada de caché.
4. Parámetros de la petición¶
Parámetro |
Valor por defecto |
Impacto |
|---|---|---|
|
|
|
|
|
|
|
|
Longitud mínima de ventana para emitir el manifest |
|
|
|
Cambiar el parámetro mediante query string actualiza los valores guardados en la sesión al próximo applyNewOTTSess.
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$" {
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;
# + директивы кеширования из 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 |
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;
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;
}
}
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 subscribers del pool prefix usan una sola contraseña. Su compromiso da 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 desconecta a todos los subscribers activos. Para una sustitución progresiva se requieren dos prefix-logins temporalmente.
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 Peer mode 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 el hex de 16 caracteres de CRC64(startTime, streamID, pid). <seq> es el número decimal del chunk en el subtitle storage (contador independiente del video storage).