shaka-proxy es un proxy para convertir streams MPEG-DASH en una salida MPEG-TS o HLS que se pueda abrir directamente con VLC, ffplay u otros reproductores IPTV.
Está pensado sobre todo para streams DASH en directo. El proxy lee el MPD, selecciona las pistas de vídeo y audio, descarga los fragmentos que van apareciendo y, si el contenido usa CENC/ClearKey, utiliza Shaka Packager para descifrarlo con las claves que se hayan configurado. Después FFmpeg remultiplexa el resultado a MPEG-TS sin volver a codificar el vídeo.
La idea es esta:
MPD + segmentos DASH
│
▼
shaka-proxy
│
├── descarga y sigue el directo
├── Shaka Packager (si hay cifrado)
└── FFmpeg
│
▼
MPEG-TS / HLS
│
▼
VLC
Al final, en vez de tener que abrir el MPD original y gestionar todo lo que hay detrás, el cliente puede usar una URL normal:
http://127.0.0.1:8090/live/3e7c7e64b3c366ad87d1826a/stream.ts
o:
http://127.0.0.1:8090/live/3e7c7e64b3c366ad87d1826a/index.m3u8
shaka-proxyno obtiene claves DRM ni solicita licencias. Las claves ClearKey, tokens, cookies o credenciales que necesite un origen deben ser proporcionadas por el usuario. Úsalo únicamente con contenido para el que tengas autorización.
Actualmente el proxy incluye, entre otras cosas:
- DASH en directo a MPEG-TS.
- Resolución de
BaseURLen los niveles MPD, Period, AdaptationSet y Representation. - Salida HLS.
- Streams CENC/ClearKey mediante Shaka Packager.
- Streams DASH sin cifrar.
- Remux con FFmpeg usando
-c copy, sin transcodificación. - Selección automática de calidad con límite configurable.
- Selección de audio.
- Listas M3U como entrada.
#KODIPROP:inputstream.adaptive.license_key.- ClearKey en formato KID/KEY, JSON y JWK.
- Headers por canal.
- User-Agent y Referer personalizados.
- Tokens que se pueden actualizar desde un archivo sin reiniciar el proxy.
- Reintentos cuando el CDN todavía no tiene disponible un segmento.
- Caché compartida entre clientes.
- Un solo pipeline de procesamiento por canal aunque haya varios espectadores.
- Continuidad MPEG-TS independiente para cada conexión.
- HLS generado a partir de los mismos segmentos ya procesados.
- EPG XMLTV.
- Playlist M3U final generada automáticamente.
- Panel de estado y endpoint JSON.
- Límite de canales activos y cierre automático de sesiones inactivas.
- Proxy de salida, bind a una IP concreta y DNS de egress.
- Soporte para varias instancias usando el mismo código.
- Cómo funciona
- DASH, CENC y ClearKey
- Qué hace Shaka Packager
- Qué hace FFmpeg
- Sincronía de audio y vídeo
- Selección de vídeo
- Selección de audio
- Seguimiento del directo
- Reintentos y segmentos que todavía no existen
- Caché
- Varios clientes y continuidad MPEG-TS
- Salida MPEG-TS
- Salida HLS
- M3U de entrada
- Headers por canal
- Tokens dinámicos
- Acceso al CDN
- Playlist generada
- EPG / XMLTV
- Estado
- Gestión de sesiones
- Instalación
- Configuración
- Arranque
- Reverse proxy / HTTPS
- Logs
- Tests
- Limitaciones
Cuando alguien abre un canal, shaka-proxy crea una sesión para ese canal o reutiliza la que ya exista.
A partir de ahí el proceso es más o menos este:
Cliente pide /live/3e7c7e64b3c366ad87d1826a/stream.ts
│
▼
cargar / actualizar MPD
│
▼
elegir vídeo y audio
│
▼
localizar nuevos segmentos
│
▼
descargar fragmentos
│
▼
Shaka Packager
│
▼
FFmpeg
│
▼
segmento .ts
│
┌─────┴─────┐
▼ ▼
stream.ts HLS
El MPD se sigue actualizando mientras el canal esté activo. Cuando aparecen nuevos fragmentos, se procesan en orden y se añaden a la ventana del canal.
Una cosa importante es que el procesamiento se hace por canal, no por cliente.
Si hay tres VLC viendo el mismo canal:
┌── VLC 1
DASH → Shaka → TS ──┼── VLC 2
└── VLC 3
No se lanzan tres pipelines completos contra el origen. Los clientes comparten los segmentos que ya ha preparado la sesión del canal.
Un stream DASH no suele ser un flujo único. El MPD describe varias pistas y una serie de fragmentos pequeños.
Por ejemplo:
manifest.mpd
video/
init.mp4
100.m4s
101.m4s
102.m4s
audio/
init.mp4
300.m4s
301.m4s
302.m4s
El MPD también puede ofrecer varias calidades de vídeo, diferentes audios, codecs, bitrates, etc.
shaka-proxy analiza esa información y decide qué representación utilizar.
Si el contenido está cifrado con CENC, los fragmentos descargados no se pueden mandar directamente a FFmpeg como si estuvieran en claro.
En el MPD puede aparecer un default_KID. Ese KID identifica la clave que corresponde a una pista.
KID -> KEY
La KEY no se obtiene del MPD. Tiene que estar ya disponible en la configuración del canal.
Una entrada M3U puede incluir una clave de esta forma:
#KODIPROP:inputstream.adaptive.license_type=clearkey
#KODIPROP:inputstream.adaptive.license_key={00112233445566778899aabbccddeeff:ffeeddccbbaa99887766554433221100}El parser también entiende otros formatos habituales, como un objeto JSON KID -> KEY o una estructura JWK ClearKey.
No es necesario que audio y vídeo utilicen la misma clave. Si el MPD usa KID diferentes, el proxy puede pasar varias claves a Shaka.
Shaka Packager se utiliza para procesar cada pista DASH.
Para un segmento concreto, el proxy descarga:
init segment
+
media segment
y prepara un archivo temporal que Shaka pueda procesar.
Si el canal tiene claves configuradas, se construye el argumento de raw keys:
label=k0:key_id=<KID>:key=<KEY>
label=k1:key_id=<KID>:key=<KEY>
...
y Packager se ejecuta con raw-key decryption.
El flujo queda así:
init + fragmento cifrado
│
│ KID / KEY
▼
Shaka Packager
│
▼
pista en claro
Si el stream no está cifrado, el mismo pipeline puede procesarlo sin activar el descifrado.
shaka-proxy no implementa el cifrado/descifrado CENC por su cuenta. Esa parte se deja a Shaka Packager.
Después de Shaka quedan las pistas de vídeo y audio procesadas, pero siguen estando en el formato de entrada usado por DASH/fMP4.
FFmpeg se encarga de remultiplexarlas a MPEG-TS.
No se pretende transcodificar:
H.264 -> H.264
HEVC -> HEVC
audio -> mismo audio
El pipeline usa -c copy, por lo que normalmente el coste de CPU es mucho menor que el de volver a codificar el canal y no se pierde calidad por una nueva compresión.
Para H.264 y HEVC se aplican los bitstream filters necesarios para llevar el vídeo de MP4 al formato esperado dentro de MPEG-TS.
Audio y vídeo no tienen por qué empezar exactamente en el mismo timestamp dentro de cada fragmento.
El proxy intenta conservar el reloj original de DASH para no introducir pequeños saltos o drift al juntar los segmentos.
FFmpeg trabaja con los timestamps originales y aplica un offset común cuando hace falta mantenerlos positivos.
Esto es especialmente importante en un stream que puede quedarse abierto durante horas: un pequeño error repetido en cada fragmento terminaría notándose bastante.
El MPD puede ofrecer varias representaciones:
576p
720p
1080p
2160p
El proxy las ordena principalmente por resolución y bitrate.
La altura máxima se controla con:
"max_height": 1080Con ese valor intentará utilizar la mejor representación disponible hasta 1080p.
Con:
"max_height": 0no se aplica un límite de altura.
También se ignoran representaciones que parecen estar destinadas a I-frames/trick mode en lugar de reproducción normal.
El proxy busca los AdaptationSet de audio del MPD.
Desde la versión 1.0.6 se incluyen todas las pistas de audio procesables, con una
representación de la mejor calidad disponible por pista. El español aparece
primero y se marca como predeterminado al remultiplexar; se reconocen spa,
es y variantes como es-ES. Si no hay español se conserva el orden del MPD.
Cuando el contenido está cifrado también se comprueba que exista una clave
conocida para el KID de cada pista. Los audios se incluyen tanto en stream.ts
como en los segmentos TS de HLS, con su etiqueta de idioma.
El reproductor puede aplicar su propia preferencia de idioma. Procesar más pistas aumenta las descargas y el trabajo por segmento; un fallo de audio mantiene los reintentos existentes para evitar publicar segmentos incompletos.
Una vez iniciada una sesión, el proxy no vuelve a empezar desde cero con cada cliente.
Mantiene una posición dentro del directo y va actualizando el MPD para encontrar los nuevos segmentos.
Al empezar intenta entrar cerca del borde del directo, pero utilizando un segmento que ya debería estar cerrado y disponible en el CDN.
Después continúa en orden:
... 100 101 102 103 104 105 ...
↑
inicio
y pasa a:
103 -> 104 -> 105 -> 106 -> ...
Si el MPD cambia de representación de vídeo o audio, la sesión cambia de generación y evita mezclar segmentos producidos con configuraciones incompatibles.
En streaming en directo es bastante normal que un MPD anuncie un segmento y que algún nodo del CDN tarde un poco más en tenerlo listo.
Por eso una descarga no se da por perdida inmediatamente ante determinados errores.
Se contemplan respuestas temporales como:
404
425
429
500
502
503
y se reintenta durante un pequeño periodo.
El número general de intentos se puede ajustar con:
"cdn_retries": 3Además, si un segmento falla repetidamente, segment_failures_before_skip limita cuánto tiempo puede bloquear el avance del canal.
"segment_failures_before_skip": 2Después de superar ese umbral se marca como fallido y el productor puede continuar con los siguientes segmentos.
Los segmentos MPEG-TS terminados se mantienen en una pequeña ventana local.
"hls_window": 6Con una ventana de 6, el proxy conserva aproximadamente los últimos seis segmentos preparados.
Esa caché la comparten:
- los clientes de
stream.ts; - la salida HLS;
- nuevas conexiones que entren al canal.
Los segmentos antiguos se van eliminando junto con el estado interno que ya no hace falta, de forma que una sesión que permanezca abierta durante mucho tiempo no tenga que conservar todo el historial del canal.
También existe una pequeña caché para los initialization segments, que normalmente no cambian en cada fragmento.
Aunque el contenido procesado se comparta, cada conexión a stream.ts tiene su propio estado MPEG-TS.
Los paquetes TS utilizan continuity counters por PID. Si se concatena contenido sin tener esto en cuenta, algunos reproductores pueden interpretar que existen pérdidas o discontinuidades.
Por eso shaka-proxy ajusta los continuity counters al enviar los paquetes a cada cliente.
segmentos compartidos
│
├── VLC 1 -> continuidad propia
├── VLC 2 -> continuidad propia
└── VLC 3 -> continuidad propia
Los .ts almacenados no se modifican; el ajuste se hace durante la entrega al cliente.
Cada segmento tiene sincronización propia.
Si el productor, HLS o varios clientes necesitan el mismo fragmento a la vez, el primero lo procesa y el resto reutiliza el resultado cuando está listo.
Esto evita lanzar Shaka y FFmpeg dos veces para el mismo timestamp.
La salida principal de un canal es:
/live/<id>/stream.ts
Ejemplo:
vlc http://127.0.0.1:8090/live/3e7c7e64b3c366ad87d1826a/stream.tsstream.ts es una respuesta HTTP continua.
Los segmentos se van escribiendo a medida que están disponibles y la entrega se acompasa para no mandar varios segundos de vídeo de golpe y quedarse esperando después.
La respuesta utiliza video/mp2t y desactiva el buffering de reverse proxies compatibles mediante:
X-Accel-Buffering: no
También se genera:
/live/<id>/index.m3u8
Los segmentos de la playlist se sirven en:
/live/<id>/seg_<timestamp>.ts
El HLS utiliza la misma caché MPEG-TS que stream.ts, por lo que no hace falta volver a procesar el canal solo por utilizar la salida HLS.
La ventana se controla con hls_window.
Ejemplo:
vlc http://127.0.0.1:8090/live/3e7c7e64b3c366ad87d1826a/index.m3u8La lista de canales se indica con:
"source_m3u": "channels.m3u"Una entrada DASH mínima puede ser:
#EXTM3U
#EXTINF:-1 tvg-id="demo" tvg-name="Canal Demo",Canal Demo
https://example.invalid/live/manifest.mpdCon ClearKey:
#EXTM3U
#EXTINF:-1 tvg-id="demo" tvg-name="Canal Demo",Canal Demo
#KODIPROP:inputstream=inputstream.adaptive
#KODIPROP:inputstream.adaptive.manifest_type=mpd
#KODIPROP:inputstream.adaptive.license_type=clearkey
#KODIPROP:inputstream.adaptive.license_key={00112233445566778899aabbccddeeff:ffeeddccbbaa99887766554433221100}
https://example.invalid/live/manifest.mpdLas líneas #KODIPROP pueden quedarse en la lista aunque el cliente final sea VLC. El proxy lee lo que necesita y no depende de que VLC entienda esas propiedades.
Se pueden indicar User-Agent y Referer mediante las opciones habituales de VLC:
#EXTVLCOPT:http-user-agent=Mozilla/5.0
#EXTVLCOPT:http-referrer=https://example.invalid/También se pueden añadir headers detrás de la URL:
https://example.invalid/manifest.mpd|Header=Value|Otro-Header=Value
Estos headers se utilizan al acceder al MPD y a los recursos del origen según corresponda.
Para valores globales existen:
"default_ua": "Mozilla/5.0 Proxy-Shaka",
"referer": "",
"origin": ""No metas tokens, claves o credenciales reales en una playlist que vayas a subir al repositorio.
Hay soporte para cargar x-tcdn-token desde un archivo independiente.
En config.json:
"token_file": "token.json"Ejemplo del archivo:
{
"access_token": "TOKEN",
"access_token_exp": 1770000000
}El proxy comprueba si el archivo cambia y vuelve a leer el token sin necesidad de reiniciar todo el proceso.
Esto viene bien cuando otro script se encarga de renovar la autenticación:
script de login/renovación
│
▼
token.json
│
▼
shaka-proxy
shaka-proxy únicamente consume el valor. No implementa el login ni la renovación específica de ningún proveedor.
Las descargas del origen se hacen actualmente con curl.
Esto permite controlar algunas cosas que son útiles cuando el servidor tiene varias salidas de red:
- IPv4.
- HTTP/1.1.
- redirects.
- timeouts.
- reintentos.
- headers.
- bind a una IP/interfaz.
- proxy.
- resolución DNS específica.
"proxy_url": "socks5://127.0.0.1:1080"Si está configurado, las conexiones de origen pueden salir por ese proxy.
"egress_bind": "172.18.10.2"Permite forzar la conexión hacia el CDN a través de una dirección local concreta.
Puede ser útil con WireGuard, policy routing o servidores con varias interfaces.
"egress_dns": "1.1.1.1"Cuando se utiliza junto con egress_bind, la resolución puede hacerse usando ese camino de salida y la IP obtenida se pasa después a curl.
El proxy genera automáticamente:
/playlist.m3u8
Las entradas DASH se sustituyen por la URL local del proxy.
Por ejemplo:
https://cdn.example/manifest.mpd
pasa a ser algo como:
http://127.0.0.1:8090/live/3e7c7e64b3c366ad87d1826a/stream.ts
Las entradas de la M3U que no sean DASH pueden mantenerse como passthrough con su URL original.
De esta manera la misma lista puede mezclar canales procesados por shaka-proxy y canales normales.
Desde la versión 1.0.7, las URLs publicadas contienen un identificador opaco de
24 caracteres hexadecimales. Por ejemplo, el canal La 1 se sirve en:
/live/83abedb89a2c19d6032309a6/stream.ts
El identificador se deriva del slug interno con SHA-256 y permanece estable al renovar tokens, regenerar listas y reiniciar el servicio. Si cambia el slug del canal en la lista de entrada, también cambia su identificador.
La lista /playlist.m3u8 proporciona las URLs correspondientes a cada canal.
Los nombres y metadatos siguen visibles en la lista y en el panel de estado.
HLS también publica los segmentos con el identificador opaco.
Las URLs antiguas con slug siguen siendo compatibles y comparten la misma sesión y caché que las nuevas. Internamente se conservan los slugs y los sufijos que distinguen canales con nombres duplicados.
La playlist de entrada se vuelve a leer cuando cambia su fecha de modificación.
Eso permite editar channels.m3u y que la lista generada y los endpoints de estado recojan los cambios sin tener que reiniciar necesariamente el servicio completo.
Si la cabecera M3U tiene url-tvg, el proxy puede descargar una guía XMLTV:
#EXTM3U url-tvg="https://example.invalid/epg.xml"También acepta XMLTV comprimido:
#EXTM3U url-tvg="https://example.invalid/epg.xml.gz"El EPG intenta relacionar los canales con la M3U usando tvg-id y nombre.
La comparación de nombres es tolerante con diferencias simples de mayúsculas, acentos, espacios y signos.
La guía se mantiene en memoria, se actualiza periódicamente y se puede consultar en:
/epg
Desde la versión 1.0.8, al pasar el ratón sobre un programa se abre una ficha flotante con el canal, fecha y horario, título, subtítulo, categoría y sinopsis disponibles en XMLTV. También se puede abrir con el teclado o tocando el programa en el móvil. Se cierra con Escape o al pulsar fuera; permite mover el ratón sobre la ficha y desplazarse por descripciones largas.
Desde la versión 1.0.9, la ficha muestra el póster XMLTV a la izquierda, el título y los datos a la derecha, y la sinopsis debajo. La imagen se carga al abrir la ficha y se oculta si no está disponible. El cursor del programa es el normal.
Hay dos endpoints:
/status
/status.json
/status muestra una página sencilla para ver qué está haciendo el proxy.
Entre los datos disponibles están:
- canales cargados;
- sesiones activas;
- programa que se está emitiendo en cada canal y su horario, según la EPG (hora de Madrid);
- espectadores;
- clientes conectados;
- IP y User-Agent;
- tiempo conectado;
- segmentos en caché;
- resolución y framerate seleccionados;
- esquema de cifrado detectado en las pistas seleccionadas y uso de ClearKey;
- estado del productor;
- estado del token;
- tiempo de actividad/inactividad.
/status.json devuelve la misma clase de información en un formato más cómodo para scripts o monitorización.
La programación se actualiza sin recargar la página. La guía se carga en segundo plano y se reutiliza entre consultas; si no hay un programa vigente en la EPG, se muestra solo el nombre del canal. No se analiza el vídeo: la información corresponde a la programación de ese canal en la guía.
Cuando el MPD declara cifrado, el estado muestra DRM · ClearKey si el proxy utiliza claves ClearKey, sin exponer claves ni identificadores. Si las pistas ya se han inspeccionado y no declaran cifrado, muestra CLEAR. La etiqueta aparece centrada a la derecha del nombre y del programa, sin aumentar la altura de la fila; mientras todavía no se conocen las pistas, no aparece ninguna etiqueta.
Los canales se crean bajo demanda.
La primera petición a:
/live/3e7c7e64b3c366ad87d1826a/stream.ts
crea una sesión. Las siguientes reutilizan esa misma sesión mientras siga viva.
El número máximo de canales que pueden estar activos al mismo tiempo se controla con:
"max_channels": 8Si se llega al límite, el proxy intenta cerrar primero una sesión inactiva.
Las sesiones sin clientes se eliminan después de:
"idle_seconds": 45Cuando una sesión se cierra también se detienen su productor y workers y se limpia su caché temporal.
Por defecto las URLs se pueden construir a partir de la propia petición, pero si el proxy está detrás de un dominio o un reverse proxy conviene configurar:
"public_base": "https://tv.example.com"Entonces /playlist.m3u8 utilizará:
https://tv.example.com/live/3e7c7e64b3c366ad87d1826a/stream.ts
También puede incluir un prefijo:
"public_base": "https://example.com/proxy1"Esto permite montar varias instancias detrás del mismo dominio.
PROXY_SHAKA_HOME permite reutilizar el mismo código con configuraciones independientes.
Por ejemplo:
/opt/shaka-proxy/ # código
/etc/shaka-proxy/a/
config.json
channels.m3u
token.json
hls/
logs/
/etc/shaka-proxy/b/
config.json
channels.m3u
token.json
hls/
logs/
Cada instancia puede tener su propio puerto, M3U, token, caché y logs.
- Python 3.11 o superior.
curl.- FFmpeg.
- Shaka Packager.
- Un compilador C y las cabeceras de Python para el build actual.
- Dependencias de
requirements.txt.
Shaka Packager no se incluye en el repositorio.
Puedes dejarlo en:
bin/packager
o configurar otra ruta.
Comprueba primero que todo está disponible:
python3 --version
curl --version
ffmpeg -version
bin/packager --versiongit clone https://github.com/mateodd1/shaka-proxy.git
cd shaka-proxyCrear el entorno virtual:
python3 -m venv .venvEl proyecto incluye build.sh:
./build.shDespués copia los ejemplos:
cp config.example.json config.json
cp channels.example.m3u channels.m3uy edítalos con tu configuración.
Ejemplo:
{
"listen_host": "127.0.0.1",
"listen_port": 8090,
"public_base": "",
"source_m3u": "channels.m3u",
"token_file": "token.json",
"proxy_url": "",
"egress_bind": "",
"egress_dns": "",
"referer": "",
"origin": "",
"default_ua": "Mozilla/5.0 Proxy-Shaka",
"max_height": 1080,
"idle_seconds": 45,
"max_channels": 8,
"origin_concurrency": 4,
"cdn_retries": 3,
"remux_timeout": 20,
"segment_failures_before_skip": 2,
"hls_window": 6,
"ffmpeg": "/usr/bin/ffmpeg",
"packager": "bin/packager",
"hls_dir": "hls",
"log_dir": "logs"
}Las opciones principales:
| Opción | Uso |
|---|---|
listen_host |
IP donde escucha el servidor. |
listen_port |
Puerto HTTP. |
public_base |
URL pública usada al generar enlaces. |
source_m3u |
M3U de entrada. |
token_file |
Archivo del token dinámico. |
proxy_url |
Proxy para el acceso al origen. |
egress_bind |
IP local usada para salir hacia el CDN. |
egress_dns |
DNS utilizado con el egress. |
referer |
Referer global. |
origin |
Origin global. |
default_ua |
User-Agent por defecto. |
max_height |
Resolución vertical máxima preferida. |
idle_seconds |
Tiempo antes de cerrar un canal sin clientes. |
max_channels |
Máximo de sesiones activas. |
origin_concurrency |
Límite de trabajo simultáneo contra el origen. |
cdn_retries |
Reintentos para peticiones al CDN. |
remux_timeout |
Timeout del remux. |
segment_failures_before_skip |
Fallos antes de saltar un segmento. |
hls_window |
Tamaño de la ventana HLS/caché. |
ffmpeg |
Ruta a FFmpeg. |
packager |
Ruta a Shaka Packager. |
hls_dir |
Directorio temporal de segmentos. |
log_dir |
Directorio de logs. |
Desde la raíz del proyecto:
PROXY_SHAKA_HOME="$PWD" .venv/bin/python run.pyCon el puerto del ejemplo:
http://127.0.0.1:8090/
Endpoints útiles:
| URL | Descripción |
|---|---|
/playlist.m3u8 |
Playlist lista para usar. |
/live/<id>/stream.ts |
MPEG-TS continuo. |
/live/<id>/index.m3u8 |
HLS. |
/live/<id>/seg_<t>.ts |
Segmentos HLS. |
/status |
Estado en web. |
/status.json |
Estado en JSON. |
/epg |
Guía XMLTV. |
Por ejemplo:
vlc http://127.0.0.1:8090/playlist.m3u8Una configuración habitual es dejar shaka-proxy escuchando solo en localhost:
"listen_host": "127.0.0.1"y poner Caddy o Nginx delante:
Internet
│
HTTPS
▼
Caddy / Nginx
│
HTTP
▼
shaka-proxy
Si la URL externa no coincide con la interna, configura public_base.
Para stream.ts es importante que el reverse proxy no acumule demasiado contenido antes de enviarlo al cliente.
Los logs se guardan en:
<log_dir>/proxy-shaka.log
Hay rotación automática del fichero para que no crezca indefinidamente.
En ellos se pueden ver cosas como:
- inicio y cierre de sesiones;
- cambios de representación;
- conexiones de clientes;
- reintentos del CDN;
- segmentos fallidos;
- errores de Shaka;
- errores de FFmpeg;
- gaps y stalls.
Hay varias cosas del proyecto que están hechas específicamente pensando en canales en directo.
Cada segmento se procesa primero dentro de un directorio temporal. El .ts final solo aparece cuando Shaka y FFmpeg han terminado correctamente.
Así un cliente no debería poder abrir un fichero que todavía se esté escribiendo.
Si una tarea que estaba ejecutando Shaka o FFmpeg expira o se cancela, el proxy intenta terminar también el subprocess.
La idea es no dejar procesos huérfanos después de cerrar una sesión.
Los workers pueden preparar trabajo en paralelo, pero los segmentos se publican siguiendo el orden del directo.
Que falle un fragmento no debería tirar todo el canal. Se reintenta y, cuando el fallo persiste más de lo razonable, se puede saltar ese timestamp y continuar.
Para ejecutar los tests directamente contra el código fuente:
PYTHONPATH=src .venv/bin/python -m unittest discover -s tests -vO contra el módulo instalado:
.venv/bin/python -m unittest discover -s tests -vAlgunas pruebas multimedia necesitan FFmpeg/Shaka disponibles. Las pruebas que no los necesitan pueden ejecutarse de forma independiente.
El proxy está hecho alrededor de los MPD y casos de uso para los que lo he ido desarrollando. No pretende implementar absolutamente toda la especificación MPEG-DASH.
A día de hoy:
- el parser está orientado principalmente a
SegmentTemplateySegmentTimeline; - el flujo principal trabaja con segmentos identificados por
$Time$; - la salida normal utiliza una pista de audio;
- se prioriza
lang="spa"; - el remux de vídeo está pensado para H.264 y HEVC;
- no hay transcodificación;
- no hay cliente de licencias DRM;
- no se obtienen claves;
- no se implementan logins ni renovación de tokens específicos de proveedores;
- FFmpeg y Shaka Packager siguen siendo dependencias externas.
Si un MPD utiliza una estructura bastante distinta, seguramente haya que ampliar el parser.
Si solo quieres entender qué hace el proyecto, es esto:
DASH
│
├── si está cifrado: Shaka Packager + ClearKey
│
└── si no: procesamiento normal
│
▼
FFmpeg (-c copy)
│
▼
MPEG-TS / HLS
│
▼
VLC
Todo lo demás —MPD, segmentos, caché, reintentos, clientes, EPG, tokens y estado— lo gestiona shaka-proxy por detrás.