Analizando la EPG de DVB-T (la programación de la tele) con Nim
Siempre he sido bastante fan de grabar películas. Me gusta aprovechar lo que ponen gratis. Y como las grabo, me salto la publicidad y las puedo ver «por capítulos». Como tampoco soy fanático de «estar a la última» viendo todos los estrenos cuando salen, y por muchas vidas que viva no voy a ver todas las películas buenas que se han hecho, disfruto de las que van poniendo.
Con la aparición de algunos canales especializados en cine como el gran BeMad, con 24 horas de películas en alta calidad (alta definición y buenas copias) me estaba faltando sistematizar estas grabaciones, ya que muchas veces hay emisiones a horas poco habituales.
Me decidí a retomar un esfuerzo que en su momento abandoné, que es el de analizar el stream de EPG (Electronic Programming Guide) que viene con cualquier conexión DVB-T. Básicamente, es emular lo que hace la TV. Que yo sepa, no existe ninguna API que traiga toda la programación de los canales españoles. Como mucho hay webs, con usabilidad a veces bastante dudosa.
DVB-T
Hay una serie de equivalencias o traducciones que tenemos que tener en cuenta a la hora de pensar en términos de la TDT (DVB-T).
Lo que en la tele analógica conocíamos como «Canal», ahora es un «Servicio» en la tele digital. Antes podíamos establecer la equivalencia entre «Canal» (La Primera, Telecinco, etc) y canal físico de transmisión (frecuencia), porque a cada Emisora o Canal se le asignaba una frecuencia. Pero en el estándar DVB-T tenemos MUXes (multiplexores) que agrupan varios de los antiguos canales. Un MUX tiene asociado una frecuencia, y dentro de este MUX se emite simultáneamente toda la información de los antiguos canales (ahora «Servicios»). Estos MUX tienen tanto ancho de banda, que permiten agrupar los canales SD, los canales HD y las emisiones de radio. Ejemplos:
- PARAMOUNT (562 Mhz):
- Paramount Network
- Dmax
- MEDIASET1 (482 Mhz):
- BeMad
- ATRESMEDIA (758 Mhz):
- Antena 3
- La Sexta
- Neox
Es solo una pequeña selección. Cada MUX tiene asignado una banda de 8Mhz. Dentro van modulados los «Servicios» (antiguos canales) y los antiguos «programas» son ahora «Eventos».
EPG
El programa dvbsnoop es la navaja suiza de la transmisión de televisión digital, como lo pueda ser wireshark o tcpdump para redes. Teniendo un MUX sintonizado, te permite observar todo lo que se está transmitiendo.
En un terminal:
tzap -c channels.conf -rHs "La 1 HD(RTVE)"
En otro terminal:
dvbsnoop -hideproginfo -s sec -ph 3 -n 1000 0x12
A partir de ese momento se empieza a imprimir por pantalla una enorme cantidad de información.
Como experimento curioso, he dejado que dvbsnoop guarde información de La 1 durante 10 segundos. El archivo resultante ocupa 3,8Mb. Si hacemos un cálculo rápido, la capacidad de descarga de datos de este medio es equivalente a 0,38 Mbyte/segundo. Pasando a la magnitud que se suele usar en las conexiones de internet (Mbit/segundo) se trataría de una "conexión" a 1.5Mbps (como si estuviéramos usando uno de los primeros ADSL). Si en lugar de venir información de EPG vinieran páginas HTML, tendríamos un cutre internet parecido al teletexto, pero bastante rápido en general, y gratis.
La guía de programas (EPG) es una transmisión continua y cíclica del calendario de emisión de cada cadena de televisión. Es parecido al teletexto. Si recordamos este sistema, al seleccionar una página transcurría un tiempo indeterminado hasta que podíamos verla. Eso es porque el analizador tiene que esperar a que llegue la página seleccionada.
La EPG transmite una gran cantidad de información y suele abarcar (en función de los servicios que contenga el MUX) unos 3-4 días vista de programación. Una vez presentada por dvbsnoop
en formato texto, este es el aspecto que tiene el comienzo de un programa de televisión («Evento»):
Event_ID: 1299 (0x0513)
Start_time: 0xeb6c190000 [= 2023-11-20 19:00:00 (UTC)]
Duration: 0x0010000 [= 01:00:00 (UTC)]
Running_status: 0 (0x00) [= undefined]
Free_CA_mode: 0 (0x00) [= unscrambled]
Descriptors_loop_length: 233 (0xe9)
DVB-DescriptorTag: 80 (0x50) [= component_descriptor]
Es un fragmento del comienzo. A partir de ahí, presentado con indentación, vienen todas las características de ese evento, las que me interesan son:
Etiqueta de descripción 84 (descriptor de contenido):
DVB-DescriptorTag: 84 (0x54) [= content_descriptor]
descriptor_length: 2 (0x02)
Content_nibble_level_1: 2 (0x02)
Content_nibble_level_2: 0 (0x00)
[= news/current affairs (general)]
User_nibble_1: 0 (0x00)
User_nibble_2: 0 (0x00)
Etiqueta de descripción 77 (descripción corta):
DVB-DescriptorTag: 77 (0x4d) [= short_event_descriptor]
descriptor_length: 23 (0x17)
ISO639_2_language_code: spa
event_name_length: 18 (0x12)
event_name: "LA SEXTA NOTICIAS" -- Charset: Latin alphabet no. 5
text_length: 0 (0x00)
text_char: ""
Etiquetas de descripción 78 (descripción extendida):
DVB-DescriptorTag: 78 (0x4e) [= extended_event_descriptor]
descriptor_length: 175 (0xaf)
descriptor_number: 0 (0x00)
last_descriptor_number: 0 (0x00)
ISO639_2_language_code: spa
length_of_items: 0 (0x00)
text_length: 169 (0xa9)
text: "SUBTITULADO; Los informativos de laSexta 2ª Edición corren a cargo de Cristina Saavedra y Rodrigo Blázquez.; País: ESPAÑA; Reparto: Cristina Saavedra y Rodrigo Blázquez" -- Charset: Latin alphabet no. 5
Este último lo he puesto en plural porque parece haber una limitación a la longitud máxima de un campo, por eso el estándar divide la descripción extendida en sucesivos campos que tendremos que concatenar.
Hay más tags
pero estos son los interesantes para intentar determinar si se trata de una película, saber el título y saber la sinopsis.
Estas etiquetas forman parte del estándar pero no todas las cadenas de televisión los emplean de la misma forma en cuanto al contenido. Algunas ponen más información (lista de actores, por ejemplo) y otras no. Algunas repiten (BeMad) la descripción corta dentro de la descripción larga, pero la mayoría no, etc.
Lo que más varía es la clasificación del contenido de las diferentes emisiones (etiqueta 84). Es por eso que no se puede confiar exclusivamente en esa etiqueta para determinar si se trata de una película. Para determinarlo hace falta un período de observación, viendo como se cataloga cada emisión en cada canal. Ejemplos:
- De BeMad me quedo con todo, porque el 99% son películas (no filtro).
- De Paramount, la información que viene no es relevante, es decir, no emplea esta catalogación.
- De Telecinco solo me interesa «movie/drama».
- De Antena 3 me quedo con «detective/thriller», «adventure/western/war», «romance», «science fiction/fantasy/horror» y «comedy»
- …
Podemos ver como algunos hacen más esfuerzo catalogando la película propiamente dicha en cuanto a su temática, otros no hacen ningún esfuerzo, y otros presentan todas sus películas como «movie/drama». Es completamente libre.
Nim
Todo esto ha sido «una excusa» para aprender un lenguaje nuevo. Hace tiempo que tenía ganas de aprender algún lenguaje compilado y Nim es una pasada. Es un lenguaje compilado, estáticamente tipado, para programación de sistemas (es decir equivalente a C o Rust) pero con una sintaxis sencilla y parecida a Python y Modula-2. Su característica (para mi) más novedosa es que en realidad compila a C, de manera que allá donde tengas un compilador C (básicamente en cualquier sitio) podrás crear el ejecutable final. Pero no solo eso, porque otros objetivos de compilación son C++, Objective-C y ¡¡Javascript!! Sí, con el mismo código Nim (y con muy pocas limitaciones) puedes crear código Javascript que puedes insertar en una página web.
Lo que hace el parser es:
- Sintonizar un MUX. No necesito buscar un canal concreto, teniendo un MUX se reciben todos los eventos -programas- de todos los programas -canales- de ese MUX. Esto explica, por cierto, porque tu TV no recibe la EPG hasta que no sintoniza un canal. En realidad la recibe por grupos. Si recibes la EPG de Cuatro, tendrás también la de Telecinco. Si recibes la de Paramount, tendrás también las de Dmax, etc.
- Capturar unos 10 segundos de EPG. Con esto ya tengo para la programación de 3-4 días. Lo guardar en un fichero y lo convierte a UTF-8.
- Analizar la salida.
- Filtrar. La primera fase es por el tipo de contenido, la segunda fase es una función específica de filtro de cada cadena (por ejemplo buscando CINE en el título).
- Guardar en una base de datos SQLite.
- Enviar un email.
Con las bases de datos SQLite podría montar alguna API o página web. Por ahora va bien con el correo:
El código se encuentra en https://git.sr.ht/~danielside/ParseEPG.
Me gusta implementar los parsers como una máquina de estados (los que se dejan). Este caso es perfecto. Por cada línea que me encuentro en el fichero EPG:
- Si estoy en el servicio que me interesa (la cadena que estoy buscando) activo un flag https://git.sr.ht/~danielside/ParseEPG/tree/master/item/parse.nim#L136. Sin ese flag, todas las demás líneas que vaya encontrando serán ignoradas.
- Si encuentro un
Event_ID
y estoy en el servicio que me interesa (BeMad, Paramount, etc.) entonces activo el flag correspondiente, paso a «modo evento encontrado». Si no, dejo pasar las líneas. - Misma técnica para los descriptores de tipo de contenido, descripción corta, descripción larga. Solo los guardo en variables si estoy en el servicio y en un evento.
- Lo más delicado es saber cuando ha terminado un evento y ha comenzado otro, ya que no hay campos delimitados por tamaño ni etiquetas de fin. En este caso lo que hago es detectar si recibo un
Event_ID
distinto del actual (que tengo en una variable, https://git.sr.ht/~danielside/ParseEPG/tree/master/item/parse.nim#L151). Si ocurre eso, guardo todo lo que llevaba en un nuevo evento, ya con toda la información que más tarde guardaré en la base de datos.
Ha sido un experimento interesante y ya llevo un montón de películas grabadas (con Kaffeine) que antes me perdía.
Perpetrado el 25 de noviembre de 2023 por una IN (Inteligencia Natural), la mia, con cierto esfuerzo.
Archivado en categoría(s) GNU/Linux, Nim, Programación, TDT
Deja una respuesta