"""Classes to parse MPEG TS and DVB SI tables """ from dvb import bs, si def known_parsers(): rv = {} rv[0x00] = PAT_Parser() # rv[0x01] = CAT_Parser() rv[0x02] = PMT() rv[0x40] = rv[0x41] = NIT() rv[0x42] = rv[0x46] = SDT() rv[0x4a] = BAT() eit = EIT() for t in range(0x4e, 0x70): rv[t] = eit return rv # Parsers class PacketStuffing(bs.Parser): """Read and discard a string of 0xff octets until we find the pointer field following, which is less than 0xff. The MPEG2 TS spec says that the value is always zero, but I'm playing it safe here and using non-0xff as the trigger. """ def __init__(self): bs.Parser.__init__(self, None, 0) def parse(self, stream, state={}): count = 1 data = stream.read_string(1) while data == "\xff": data = stream.read_string(1) count += 1 return (ord(data), count*8) class PAT_Parser(bs.StructuredParser): """Parse the Programme Allocation Table (PAT) """ _syntax = ( bs.Parser("section_syntax_indicator", 1), bs.Reserved(3), bs.Parser("section_length", 12), bs.Parser("transport_stream_id", 16), bs.Reserved(2), bs.Parser("version_number", 5), bs.Typed("current_next_indicator", 1, bool), bs.Parser("section_number", 8), bs.Parser("last_section_number", 8), bs.LengthArray_Deferred( "programs", "section_length", -9, (bs.Parser("program_number", 16), bs.Reserved(3), si.PAT_ProgramPID(13) )), bs.Parser("CRC_32", 32) ) def __init__(self): bs.StructuredParser.__init__(self, None, self._syntax, SI_Table) class PMT(bs.StructuredParser): """Parse the Programme Map Table (PMT) """ _syntax = ( bs.Parser("section_syntax_indicator", 1), bs.Reserved(3), bs.Parser("section_length", 12), bs.Parser("program_number", 16), bs.Reserved(2), bs.Parser("version_number", 5), bs.Typed("current_next_indicator", 1, bool), bs.Parser("section_number", 8), bs.Parser("last_section_number", 8), bs.Reserved(3), bs.Parser("PCR_PID", 13), bs.Reserved(4), bs.Parser("program_info_length", 12), bs.LengthArray_Deferred( "program_info", "program_info_length", 0, si.Descriptor()), si.PMT_StreamInfoArray("stream_info"), bs.Parser("CRC_32", 32) ) def __init__(self): bs.StructuredParser.__init__(self, None, self._syntax, SI_Table) class NIT(bs.StructuredParser): """Parse the Network Information Table (NIT) """ _syntax = ( bs.Parser("section_syntax_indicator", 1), bs.Reserved(3), bs.Parser("section_length", 12), bs.Parser("network_id", 16), bs.Reserved(2), bs.Parser("version_number", 5), bs.Typed("current_next_indicator", 1, bool), bs.Parser("section_number", 8), bs.Parser("last_section_number", 8), bs.Reserved(4), bs.Parser("network_descriptors_length", 12), bs.LengthArray_Deferred( "network_descriptors", "network_descriptors_length", 0, si.Descriptor()), bs.Reserved(4), bs.Parser("transport_stream_loop_length", 12), bs.LengthArray_Deferred( "transport_stream_loop", "transport_stream_loop_length", 0, (bs.Parser("transport_stream_id", 16), bs.Parser("original_network_id", 16), bs.Reserved(4), bs.Parser("transport_descriptors_length", 12), bs.LengthArray_Deferred( "transport_descriptors", "transport_descriptors_length", 0, si.Descriptor()) )), bs.Parser("CRC_32", 32) ) def __init__(self): bs.StructuredParser.__init__(self, None, self._syntax, SI_Table) class BAT(bs.StructuredParser): """Parse the Bouquet Association Table (BAT) """ _syntax = ( bs.Parser("section_syntax_indicator", 1), bs.Reserved(3), bs.Parser("section_length", 12), bs.Parser("bouquet_id", 16), bs.Reserved(2), bs.Parser("version_number", 5), bs.Typed("current_next_indicator", 1, bool), bs.Parser("section_number", 8), bs.Parser("last_section_number", 8), bs.Reserved(4), bs.Parser("bouquet_descriptors_length", 12), bs.LengthArray_Deferred( "bouquet_descriptors", "bouquet_descriptors_length", 0, si.Descriptor()), bs.Reserved(4), bs.Parser("transport_stream_loop_length", 12), bs.LengthArray_Deferred( "transport_stream_loop", "transport_stream_loop_length", 0, (bs.Parser("transport_stream_id", 16), bs.Parser("original_network_id", 16), bs.Reserved(4), bs.Parser("transport_descriptors_length", 12), bs.LengthArray_Deferred( "transport_descriptors", "transport_descriptors_length", 0, si.Descriptor()) )), bs.Parser("CRC_32", 32) ) def __init__(self): bs.StructuredParser.__init__(self, None, self._syntax, SI_Table) class SDT(bs.StructuredParser): """Parse the Service Description Table (SDT) """ _syntax = ( bs.Parser("section_syntax_indicator", 1), bs.Reserved(3), bs.Parser("section_length", 12), bs.Parser("transport_stream_id", 16), bs.Reserved(2), bs.Parser("version_number", 5), bs.Typed("current_next_indicator", 1, bool), bs.Parser("section_number", 8), bs.Parser("last_section_number", 8), bs.Parser("original_network_id", 16), bs.Reserved(8), bs.LengthArray_Deferred( "service_info", "section_length", -12, (bs.Parser("service_id", 16), bs.Reserved(6), bs.Typed("EIT_schedule_flag", 1, bool), bs.Typed("EIT_present_following_flag", 1, bool), bs.Parser("running_status", 3), bs.Typed("free_CA_mode", 1, bool), bs.Parser("descriptors_loop_length", 12), bs.LengthArray_Deferred( "descriptors_loop", "descriptors_loop_length", 0, si.Descriptor()) )), bs.Parser("CRC_32", 32) ) def __init__(self): bs.StructuredParser.__init__(self, None, self._syntax, SDT_Table) class EIT(bs.StructuredParser): """Parse the Event Information Table (EIT) """ _syntax = ( bs.Parser("section_syntax_indicator", 1), bs.Reserved(3), bs.Parser("section_length", 12), bs.Parser("service_id", 16), bs.Reserved(2), bs.Parser("version_number", 5), bs.Typed("current_next_indicator", 1, bool), bs.Parser("section_number", 8), bs.Parser("last_section_number", 8), bs.Parser("transport_stream_id", 16), bs.Parser("original_network_id", 16), bs.Parser("segment_last_section_number", 8), bs.Parser("last_table_id", 8), bs.LengthArray_Deferred( "event_info", "section_length", -15, (bs.Parser("event_id", 16), si.Time("start_time"), si.Duration("duration"), bs.Parser("running_status", 3), bs.Typed("free_CA_mode", 1, bool), bs.Parser("descriptors_loop_length", 12), bs.LengthArray_Deferred( "descriptors_loop", "descriptors_loop_length", 0, si.Descriptor()) )), bs.Parser("CRC_32", 32) ) def __init__(self): bs.StructuredParser.__init__(self, None, self._syntax, SI_Table) # Support structures (e.g. table classes) class SI_Table(object): """A generic SI table.""" def series(self): return self.table_id def __repr__(self): return repr(self.__dict__) class SDT_Table(SI_Table): """The SDT table.""" def series(self): return "%d:%d:%d" % (self.table_id, self.original_network_id, self.transport_stream_id)