DVR / Archivo

Desde la versión 1.13, Perfect Streamer incluye un DVR integrado — un archivo de flujo persistente en disco, paralelo a la salida OTT normal (HLS / DASH). El archivo se escribe automáticamente, sin proceso aparte, y se reproduce por las mismas URLs OTT que el live — única diferencia: el parámetro query.

Funcionalidades:

  • Grabación de cada flujo OTT al archivo en el almacenamiento elegido.

  • Reproducción HLS, DASH y Low-Latency HLS del archivo (VOD) en las mismas URL que el live (DASH y LL-HLS — sobre CMAF, en OTT/HLS/LL-HLS/LL-Dash).

  • Subtítulos (WebVTT) — escritos junto con chunks TS.

  • Varios almacenamientos — un flujo se vincula a uno; flujos distintos pueden escribir a discos distintos.

  • Limpieza automática por tiempo de retención y uso del disco.

  • EPG-aligned VOD — archivo según evento EPG referenciado.

  • VOD adaptativo — soportado para grupos adaptativos.

El DVR no requiere licencia aparte. Se activa por flujo añadiendo una vinculación al almacenamiento.

El DVR no reemplaza la emisión en vivo. Si un flujo tiene archivo, el cliente recibe una playlist en vivo con el mismo comportamiento que sin DVR. El archivo solo comienza a reproducirse cuando el cliente solicita explícitamente el modo VOD mediante un parámetro de consulta de la URL (véase más abajo).

Configuración del almacenamiento

Un almacenamiento es un registro en la sección Configuration / DVR Storage. Cada registro describe un directorio en disco donde PSS escribe ficheros del archivo. Un flujo usa un almacenamiento.

Al añadir un almacenamiento se configuran:

Name — nombre mostrado.

Dir Path — ruta al directorio en disco. Tras crear el registro, la ruta no se puede cambiar — para mover el archivo, eliminar el registro y añadir uno nuevo. Los ficheros existentes no se tocan en disco al eliminar el registro.

Max Usage, % — umbral de uso del disco (por defecto 90 %). Al superarlo, arranca la limpieza size-based (ver abajo). Mín 1 %, máx 100 %.

Cleanup Interval, seg — periodo de la tarea de limpieza (por defecto 10 seg). En cada tick se corta primero lo más antiguo que la profundidad de retención; luego, si supera Max Usage, chunks viejos.

Disk Pressure Grace, seg — segundos que Used % debe superar Max Usage continuamente antes de Size-based cleanup (por defecto 60 seg). Filtra picos cortos.

Disk Pressure Cut, seg — límite superior por tick de limpieza: segundos de vídeo por flujo borrables de una vez (por defecto 300 seg). El resto pasa al siguiente tick.

Disk Emergency Bytes — umbral de espacio libre bajo el cual el almacenamiento pasa a Error y la grabación para (por defecto 2 GiB). Recuperación auto si espacio libre ≥ 2 × este valor.

Alarm Disk-Full Hysteresis, % — el margen por debajo de Max Usage hasta el que la limpieza basada en tamaño reduce el llenado, para que Used % no fluctúe justo en el umbral (2 % por defecto).

La mayoría de valores por defecto sirven para instalaciones típicas; suele bastar ajustar Max Usage y Dir Path.

Tiene sentido crear un almacenamiento por disco. Si en el mismo disco se indican varios registros con subdirectorios distintos, competirán por el espacio libre — el disco es compartido, pero la limpieza es propia de cada almacenamiento.

Vinculación de un flujo al almacenamiento

En los ajustes Stream / OTT aparece una sección DVR:

Storage — lista desplegable de almacenamientos; 0 significa «archivo desactivado para este flujo».

Storage Hours — profundidad del archivo para este flujo, en horas, de 1 a 2160 (hasta 90 días). Los chunks más antiguos que este valor se eliminan en cada tick de la tarea de limpieza (Rolling cleanup).

Storage Min Hour — umbral de protección inferior (horas). La limpieza nunca borra chunks más jóvenes, ni bajo presión Max Usage. Útil si la lógica de negocio exige una grabación reciente garantizada, p. ej. «las últimas 2 horas siempre presentes».

Cambio de Storage en caliente:

  • poner 0 — desactiva el archivo; los chunks en disco se conservan, no se escriben nuevos. Las sesiones VOD empiezan a devolver 404;

  • elegir otro almacenamiento — el flujo se desvincula del antiguo y empieza a escribir en el nuevo. Los ficheros del antiguo no se migran.

Los cambios en Storage Hours y Storage Min Hour se aplican al instante — la limpieza usa el nuevo valor en el siguiente tick.

Tras la configuración, el flujo escribe el archivo automáticamente al entrar en Running.

VOD: reproducción del archivo

El archivo se reproduce por las mismas URLs que el live HLS / DASH (ver sección OTT service) — sólo cambia el parámetro query.

URLs y parámetros

URL para HLS, DASH y Low-Latency HLS (DASH y LL-HLS — sobre CMAF, requieren el modo OTT/HLS/LL-HLS/LL-Dash):

Sin parámetros query — live normal con ventana deslizante. Con el parámetro t — modo VOD.

Parámetro

Función

t=<epoch>

Hora de inicio VOD (Unix epoch, seg). t=0 — desde el inicio del archivo. La presencia de t (incluso t=0) activa el modo VOD.

d=<sec>

Duración de la ventana VOD, seg. d=0 o ausente — «hasta el momento actual». Sólo tiene sentido con t.

epg=<epoch>

EPG-aligned VOD: el servidor localiza el evento EPG activo en el momento dado y toma sus start y duration como bordes de la ventana. Incompatible con t y d (sustitución server-side). Ver abajo.

a, s, m, v

Parámetros live estándar (ver OTT service); s y m se ignoran en modo VOD.

Comportamiento según t y d:

t

d

Ventana

Condición 404

ninguno

—

live (ventana deslizante)

—

0

ninguno / 0

[inicio archivo, ahora]

DVR no vinculado

0

> 0

[inicio archivo, +d]

DVR no vinculado

> 0

ninguno / 0

[t, ahora]

DVR no vinculado

> 0

> 0

[t, t+d]

DVR no vinculado

Normalización de bordes:

  • t anterior al inicio del archivo — start se alinea automáticamente al primer chunk disponible (la limpieza pudo recortarlo). No es 404 — se sirve lo que queda.

  • t en el futuro o ventana totalmente anterior al archivo — playlist vacía pero válida (HLS: sólo header + EXT-X-ENDLIST; DASH: @type="static", mediaPresentationDuration="PT0S").

  • Los chunks se seleccionan estrictamente por el instante de inicio del chunk que cae en el intervalo semiabierto [t, t+d) — no hay segmentos parciales.

VOD en un flujo sin archivo (o cuyo almacenamiento está en Error) — 404 inmediato sobre master playlist / MPD. No se crea sesión.

Texto de error en el cuerpo 404 (visible en logs del servidor y HTTP body):

  • VOD: stream not running — el flujo está en la config pero no en Running.

  • VOD: no DVR archive — el flujo no tiene almacenamiento o está en Error.

  • VOD: DVR detached — el flujo se desvinculó del almacenamiento entre peticiones.

  • VOD: EPG event not found — sin evento para ?epg=.

Playlist HLS VOD — cerrada (el reproductor ve la duración y puede buscar):

#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

Estos marcadores no están en la playlist live — esa es la única diferencia.

MPD DASH VOD — estático: @type="static", mediaPresentationDuration fijo, <SegmentURL> explícitos. El DASH live sigue @type="dynamic".

Si el intervalo elegido tiene huecos en el archivo (p. ej. reinicio de grabación o limpieza en medio), el MPD DASH se divide automáticamente en varios <Period> — uno por pista contigua. Los reproductores (VLC, dashjs, Shaka) cruzan los bordes de periodo sin configuración especial.

EPG-aligned VOD

Si el flujo está vinculado a una fuente EPG (campos EPG Source y EPG Channel en los ajustes), el cliente puede solicitar el archivo por un momento contenido en un evento EPG:

El servidor localiza el evento EPG activo en el epoch dado y usa sus start y duration como bordes de la ventana VOD. Útil para catálogos: la UI conoce la hora del evento pero no debe calcular bordes exactos.

Si el flujo no está vinculado a EPG o no hay evento activo — 404 ``VOD: EPG event not found``. t y d se ignoran si está epg.

Flujos adaptativos

Los grupos adaptativos (ver HLS Adaptive Multistream) admiten los mismos parámetros VOD:

En la master playlist (HLS) sólo entran las variantes con almacenamiento DVR configurado. Las variantes sin DVR se omiten (en el log aparece VOD: variant N has no DVR); el ensamblado sigue con las demás.

En la variante DASH, cada calidad se vuelve un <Representation> distinto en <Period> comunes; el reproductor cambia de calidad sin reabrir el manifiesto.

Comportamiento del reproductor

El VOD HLS / DASH del archivo se reproduce en reproductores estándar: VLC, hls.js, dashjs, Shaka, ffmpeg. La búsqueda en timeline funciona.

Si el reproductor pide un segmento ya borrado por la limpieza, el servidor devuelve 404 para ese segmento (no 500). VLC, hls.js, dashjs lo saltan y siguen con el siguiente.

Capacidades adicionales:

  • Protección de sesiones activas: con sesión VOD abierta, la limpieza no borra chunks dentro de su ventana (ver abajo).

  • Live-edge bridge: si un cliente en sesión VOD pide un segmento más allá del límite derecho de la ventana VOD — por ejemplo, avanza en la línea de tiempo al llegar al final del archivo — el servidor entrega automáticamente el segmento desde la memoria live. Sin redirecciones ni reautenticación.

  • Caché de playlist: ante peticiones repetidas del mismo VOD index.m3u8 / index.mpd, el servidor devuelve respuesta idéntica byte a byte — sin reconstruir. Apto para CDN delante de PSS.

Subtítulos en el archivo

Si el flujo lleva subtítulos (DVB Subtitling, Teletext o WebVTT) y la opción OTT WebVTT está activa, los subtítulos se archivan en paralelo con los chunks TS — en ficheros .vtt junto a .ts.

En modo live, la master playlist contiene EXT-X-MEDIA:TYPE=SUBTITLES; en modo VOD, el servidor devuelve una playlist VOD de subtítulos (con ENDLIST) y segmentos .vtt en las mismas URLs.

Particularidades:

  • HLS: la cabecera X-TIMESTAMP-MAP se conserva al inicio de cada .vtt (requerida por la spec HLS).

  • DASH: la cabecera X-TIMESTAMP-MAP se elimina al vuelo (ligada al PCR absoluto, choca con el anclaje DASH; si no, VLC muestra subtítulos vacíos).

  • En grupo adaptativo: si el sub-stream activo no escribe subtítulos, los segmentos VTT devuelven 404. En la siguiente variante, el reproductor podrá recibirlos otra vez.

Los subtítulos se desactivan vía OTT WebVTT en el flujo, o no activando HLS en modo OTT/HLS.

Limpieza y retención

PSS usa cuatro estrategias de limpieza que corren en cada tick de la tarea de limpieza (por defecto cada 10 seg):

  1. Rolling cleanup (por flujo): por cada flujo se borran chunks más viejos que Storage Hours. Corre siempre, aun con disco a medias.

  2. Size-based cleanup (por almacenamiento): cuando Used % supera Max Usage continuamente durante Disk Pressure Grace seg, los chunks más viejos se cortan proporcionalmente entre los flujos vinculados. Por tick: Disk Pressure Cut seg de vídeo por flujo. Nunca chunks más jóvenes que Storage Min Hour.

  3. Emergency disk-full cut: si el espacio libre cae bajo Disk Emergency Bytes, la limpieza es agresiva y puede borrar incluso chunks protegidos por sesión. La grabación para hasta que el espacio libre se recupere con histéresis × 2.

  4. Orphan scan: en el disco pueden quedar archivos no contabilizados en el índice (tras una detención abrupta de PSS). El recolector recorre los subdirectorios de los flujos y elimina esos archivos «olvidados» — la primera pasada poco después del arranque del servicio, luego una vez por hora y cada vez que se dispara la presión de disco. Como protección frente a una carrera con la escritura, se omiten los archivos de menos de 60 s.

Nota. Las tareas de limpieza corren en segundo plano; el usuario normalmente no actúa. Si el archivo crece más rápido de lo que se corta, bajar Storage Hours en los flujos o subir Disk Pressure Cut.

Protección de sesiones VOD activas

Al abrir una sesión VOD, en cada almacenamiento implicado se registra un «slot de protección» con la hora de inicio de la ventana. Las limpiezas Rolling y size-based no tocan chunks dentro de la ventana de la sesión abierta. El slot se libera automáticamente al cerrar (FIN, timeout).

Esto significa:

  • Si un cliente mantiene una sesión VOD largo tiempo, puede buscar a cualquier instante de su ventana — los chunks no «desaparecen bajo él».

  • La limpieza por Max Usage puede no llevar Used % al umbral mientras la sesión está activa; al irse el cliente, la limpieza alcanza.

  • Emergency disk-full cut y Storage Min Hour saltan la protección: si el disco está casi sin libre, los chunks se borran y el cliente recibe 404 en los segmentos afectados (el reproductor los salta).

  • Tras reiniciar PSS, los slots de protección desaparecen — la limpieza reanuda al instante.

Varios almacenamientos

Se puede crear cualquier número de registros DVR Storage — uno por directorio / disco. Los flujos se vinculan a distintos almacenamientos de forma independiente. La limpieza y umbrales (Max Usage, Disk Pressure) operan por almacenamiento.

Casos de uso:

  • Tiering por valor: almacenamiento SSD rápido para canales premium con gran profundidad, HDD capacitivo para el resto.

  • Disco dedicado al archivo: para que la grabación DVR no compita con el disco del sistema o ficheros temporales.

  • Separación por proyecto: un disco para el lote nº 1, otro para el nº 2 — simplifica migración y auditoría.

Cambiar la vinculación del flujo (campo Storage en Stream / OTT) conmuta la grabación al vuelo. Los ficheros antiguos quedan intactos en el disco previo — pueden borrarse a mano o reanudar la grabación devolviendo el flujo.

Estado del almacenamiento y monitorización

La sección Data / DVR Storage List (y la API GET /data/dvr-storage-list) muestra por almacenamiento:

  • State — Ready / Error (más el estado intermedio Not Ready en un almacenamiento recién añadido).

  • Total / Free / Used Bytes — espacio libre y ocupado en el disco.

  • Used % — porcentaje actual de uso.

  • Archived Bytes — tamaño total de chunks indexados de todos los flujos vinculados (sin orphans).

  • Pressure Since Sec — momento (epoch) en que Used % superó por primera vez Max Usage en este episodio; 0 significa «sin presión».

  • Active Task — la operación de mantenimiento en segundo plano que se ejecuta en este momento: gc-orphans (eliminación de archivos huérfanos), disk-pressure-trim (recorte bajo presión de disco) o none, y cuántos segundos lleva ya ejecutándose. Las operaciones de corta duración (< 2 s) no parpadean en la interfaz.

  • Last cleanup — un resumen de las últimas ejecuciones de limpieza: hace cuánto tiempo se realizó la anterior recogida de archivos huérfanos (y cuántos se eliminaron) y el último recorte por presión de disco (cuánto espacio se liberó).

  • Attached streams — la lista de los flujos asociados; para cada uno se muestran el nombre, el indicador de grabación en curso (active), la profundidad de archivo configurada (retention-hours) y el llenado real (archived-sec y archived-bytes). La suma de archived-bytes de todos los flujos es igual a Archived Bytes de todo el almacenamiento.

Estados:

  • Ready — el directorio está disponible, la grabación y la limpieza proceden con normalidad.

  • Error — el directorio de almacenamiento no está disponible (desmontado, sin permisos, error del sistema de archivos) o hay un espacio libre críticamente bajo en el disco; la grabación se detiene. La recuperación es automática — en cuanto el directorio vuelve a estar disponible y aparece suficiente espacio libre.

Si el almacenamiento ha pasado a Error, compruebe si el directorio del archivo está montado y si hay espacio libre en el disco — PSS no sale solo de este estado hasta que el problema se resuelva físicamente.

Llenado del archivo a lo largo del tiempo:

  • A nivel de stream (Data / Stream) está disponible la métrica storage-gap-percent — la proporción de huecos de tiempo en el archivo acumulado: 0 % significa grabación continua, valores más altos significan que hay discontinuidades en el archivo (reinicios de la fuente, tramos recortados por la limpieza).

  • El endpoint GET /data/dvrstat devuelve un histograma del llenado del archivo por intervalos de tiempo, con el marcado de eventos de grabación (inicio/parada de la fuente, cambio de PMT, conmutaciones del cifrado, reconstrucción del índice, pasada de limpieza) y de actividad de subtítulos — para dibujar la línea de tiempo del archivo DVR en la interfaz de administración.

Protección contra pérdida accidental

Varias operaciones del admin causan pérdida del archivo o cese de la entrega VOD. Antes de ejecutarlas, el UI muestra confirmación modal:

  • Eliminar DVR Storage — todos los flujos vinculados pierden acceso VOD; los ficheros quedan en disco pero son inalcanzables vía PSS sin el registro.

  • Cambiar el flujo a otro almacenamiento — el VOD sobre el archivo antiguo deja de funcionar.

  • Desvincular el flujo del almacenamiento (Storage = 0) — el mismo efecto.

  • Bajar Max Usage — puede disparar la limpieza size-based y borrar chunks viejos.

  • Bajar Storage Hours / Storage Min Hour — puede sacar parte del archivo del rolling-window.

Decisión deliberada del producto: la operación es posible pero con confirmación, y los ficheros borrados quedan en disco (recuperables de backup). Ubicar el archivo en un servidor de ficheros / RAID aparte reduce mucho el riesgo de pérdida irreversible.

Limitaciones de la versión actual

  • Una sesión VOD abierta por el cliente no sigue el live edge — si se añadieron chunks durante la sesión, el cliente debe volver a pedir la playlist (comportamiento estándar HLS / DASH).

  • Asignar un almacenamiento por disco es lo recomendable — varios registros con subdirectorios distintos compiten por el espacio.

  • Los segmentos se direccionan por hash (HLS sobre MPEG-TS) o mediante la plantilla $Number$.m4s con un init.mp4 común (live DASH sobre CMAF) / mediante <SegmentURL> explícitas hacia .m4s (VOD DASH). Cambiar el tamaño de chunk entre live y VOD no requiere reabrir la URL.

  • El orphan scanner corre cada hora; para acelerar, reiniciar PSS.