Introducción
Un archivo MPEG-TS (Transport Stream) es un formato de archivo utilizado para transmitir video, audio y otros tipos de datos en sistemas de radiodifusión digital, como la televisión digital terrestre (TDT). HbbTV (Hybrid Broadcast Broadband TV) es una especificación que combina la transmisión tradicional de televisión (broadcast) con servicios basados en internet. HbbTV utiliza tecnologías web estándar como HTML, JavaScript y CSS para ofrecer esta experiencia interactiva a los usuarios.
La tabla AIT (Application Information Table) es una parte importante del estándar MPEG-TS que se utiliza específicamente en el contexto de HbbTV. La tabla AIT proporciona información sobre las aplicaciones interactivas disponibles para un servicio de televisión digital en particular. Contiene metadatos relacionados con las aplicaciones, como su nombre, descripción, tipo de aplicación, URL de lanzamiento, etc.
Se ha desarrollado un programa en Python que, basándose en la especificación pública TS 102 809 - V1.1.1 - Digital Video Broadcasting (DVB) extrae para cada servicio (canal) contenido en el archivo MPEG-TS la dirección URL a la página web que aloja el servicio para su posterior análisis.
Uso de la aplicación
Para su uso, simplemente es necesario especificar en el parámetro la ruta al archivo deseado. El archivo puede ser una grabación con el programa SmartDVB. Este es un ejemplo de su ejecución.
$ python get_hbbtv_url_from_ts.py record.ts
Canal: http://servicio.es/hbbtv/index.xhtml
Código fuente
import sys
def get_packet(stream, pid):
for i in range(int(len(stream)/188)):
# get packet
start_packet = i * 188
end_packet = (i + 1) * 188
packet = stream[start_packet:end_packet]
# pid parameter
if packet[2] == pid:
return packet
# not found, return 0
return 0
def get_pid_from_packet(pat):
# byte 12 = pid
return pat[11]
def get_programs_pmt(pmt):
# get bytes length header pmt
length_bytes = pmt[6:8] # two last bytes of header
length_bytes = bin(int(length_bytes.hex(), base=16)
).lstrip('0b') # 2 bytes to bits
length = int(length_bytes[6:16], base=2) # 10 bits = length
# get pmt specific data
pmt_specific_data = pmt[(4+1+3) + 5:(4+1+3) + length - 4]
# get program length
length_program_info = pmt_specific_data[2:4]
length_program_info = bin(int(length_program_info.hex(), base=16)).lstrip(
'0b') # 2 bytes to bits
length_program_info = int(
length_program_info[6:16], base=2) # 10 bits = length
# ignore program info
components = pmt_specific_data[4+length_program_info:]
# get length component and add (ignore if type FF)
programs = []
while True:
if len(components) > 0:
program_length = components[3:5]
program_length = bin(int(program_length.hex(), base=16)).lstrip(
'0b') # 2 bytes to bits
# 10 bits = length
program_length = int(program_length[6:16], base=2)
program = components[:5+program_length]
components = components[5+program_length:] # delete program
programs.append(program)
else:
break
return programs
def get_pid_ait_program(programs):
for program in programs:
if program[0] == 5:
return program[2]
def get_ait_packet(stream, pid):
ait_packet = b''
number = 0
for i in range(int(len(stream)/188)):
# get packet
start_packet = i * 188
end_packet = (i + 1) * 188
packet = stream[start_packet:end_packet]
# pid parameter
if packet[2] == pid:
# check payload start
if packet[1] == 128:
if len(ait_packet) > 0:
break
elif packet[1] == 0:
if len(ait_packet) == 0:
continue
# strip packet header
ait_packet += packet[4:]
# return packet
return ait_packet
def get_url_ait(packet):
# ignore header (12 bytes)
packet = packet[12:]
# ignore application header
packet = packet[10:]
domain = ''
path = ''
while domain == '' or path == '':
# check types and delete if not interesed
if packet[0] != 0x15 and packet[0] != 0x2:
# application_descriptor
packet = packet[2 + packet[1]:]
else:
if packet[0] == 0x15:
path = packet[2:packet[1] + 2]
packet = packet[2 + packet[1]:]
elif packet[0] == 0x2:
domain = packet[6:packet[1] + 1]
packet = packet[2 + packet[1]:]
return domain.decode('ascii') + path.decode('ascii')
def get_program_sid(pmt):
# get sid
return pmt[4+1+4]
def get_name_from_sdt(ts, pid):
sdt = get_packet(ts, 0x11)
sdt = sdt[4+9+4:]
name = ''
while True:
# check types and delete if not interesed
if sdt[0] != pid:
# application_descriptor
sdt = sdt[5+sdt[3]:]
else:
a = hex(sdt[8])
name = sdt[8+sdt[7]+1 : 8+sdt[7]+1 + sdt[8+sdt[7]]]
break
return name.decode('ascii')
# get ts file
print("Opening " + sys.argv[1])
ts = open(sys.argv[1], "rb").read()
# search pat packet
# pat pid = 0, strip header packet and pointer (byte 3), 16 byte size of pat
pat = get_packet(ts, 0)[5:21]
# get program map pid
map_pid = get_pid_from_packet(pat)
# search pmt packet
pmt = get_packet(ts, map_pid)
# get pmt programs
programs_pmt = get_programs_pmt(pmt)
# get ait pid program
pid_ait = get_pid_ait_program(programs_pmt)
# get ait packet print(get_url_ait(ait_packet))
ait_packet = get_ait_packet(ts, pid_ait)
# program sid
p_sid = get_program_sid(pmt)
print(get_name_from_sdt(ts, p_sid) + ": " + get_url_ait(ait_packet))