1、关于 TS 流的解析TS 即是“Transport Stream“的缩写。他是分包发送的,每一个包长为 188 字节。在 TS 流里可以填入很多类型的数据,如视频、音频、自定义信息等。他的包的结构为,包头为 4 个字节,负载为 184 个字节(这 184 个字节不一定都是有效数据,有一些可能为填充数据) 。工作形式: 因为在 TS 流里可以填入很多种东西,所以有必要有一种机制来确定怎么来标识这些数据。制定 TS 流标准的机构就规定了一些数据结构来定义。比如: PSI(Program Specific Information)表,所以解析起来就像这样: 先接收一个负载里为 PAT 的数据包,在整
2、个数据包里找到一个 PMT 包的 ID。然后再接收一个含有 PMT 的数据包,在这个数据包里找到有关填入数据类型的 ID。之后就在接收到的 TS 包里找含有这个 ID 的负载内容,这个内容就是填入的信息。根据填入的数据类型的 ID 的不同,在 TS 流复合多种信息是可行的。关键就是找到标识的 ID 号。现在以一个例子来说明具体的操作:在开始之前先给出一片实际 TS 流例子: 0000f32ch: 47 40 00 17 00 00 B0 0D 00 01 C1 00 00 00 01 E0 ; G.?.?.? 0000f33ch: 20 A2 C3 29 41 FF FF FF FF FF F
3、F FF FF FF FF FF ; )A0000f34ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f35ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f36ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f37ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f38ch: FF FF FF FF FF FF FF FF
4、FF FF FF FF FF FF FF FF ; 0000f39ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f3ach: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f3bch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f3cch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 0000f3dch: FF FF FF FF FF FF F
5、F FF FF FF FF FF 47 40 20 17 ; G .0000f3ech: 00 02 B0 1B 00 01 C1 00 00 E0 21 F0 00 1B E0 21 ; .?.?.?.?0000f3fch: F0 04 2A 02 7E 1F 03 E0 22 F0 00 5D 16 BD 48 ; ?*.?.紿具体的分析就以这个例子来分析。/ Adjust TS packet headervoid adjust_TS_packet_header(TS_packet_header* pheader)unsigned char buf4; memcpy(buf, pheade
6、r, 4);pheader-transport_error_indicator = buf1 7;pheader-payload_unit_start_indicator = buf1 6 pheader-transport_priority = buf1 5 pheader-PID = (buf1 pheader-adaption_field_control = buf3 4 pheader-continuity_counter = buf3 这是一个调整 TS 流数据包头的函数,这里牵扯到位段调整的问题。现在看一下 TS 流数据包头的结构的定义:/ Transport packet hea
7、dertypedef struct TS_packet_headerunsigned sync_byte : 8;unsigned transport_error_indicator : 1;unsigned payload_unit_start_indicator : 1;unsigned transport_priority : 1;unsigned PID : 13;unsigned transport_scrambling_control : 2;unsigned adaption_field_control : 2;unsigned continuity_counter : 4; T
8、S_packet_header; 下面我们来分析,在 ISO/IEC 13818-1 里有说明,PAT(Program Association Table)的 PID 值为0x00,TS 包的标识 (即 sync_byte)为 0x47,并且为了确保这个 TS 包里的数据有效,所以我们一开始查找 47 40 00 这三组 16 进制数,为什么这样?具体的奥秘在 TS 包的结构上,前面已经说了sync_byte 固定为 0x47。现在往下看transport_error_indicator、payload_unit_start_indicator 、 transport_priority 和 P
9、ID 这四个元素,PID 为 0x00,这是 PAT 的标识。 transport_error_indicator 为 0,transport_priority 为 0。把他们看成是两组 8 位 16 进制数就是: 40 00。现在看看我们的 TS 流片断例子,看来正好是 47 40 00开头的,一个 TS 流的头部占据了 4 个字节。剩下的负载部分的内容由 PID 来决定,例子看来就是一个PAT 表。在这里有个地方需要注意一下,payload_unit_start_indicator 为 1 时,在前 4 个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。现在看例子中的数据
10、47 40 00 17 00 第五个字节是 00,说明紧跟着 00 之后就是具体的负载内容。下面给出 PAT 表的结构体:/ PAT table/ Programm Association Tabletypedef struct TS_PATunsigned table_id : 8;unsigned section_syntax_indicator : 1;unsigned zero : 1;unsigned reserved_1 : 2;unsigned section_length : 12;unsigned transport_stream_id : 16;unsigned reser
11、ved_2 : 2;unsigned version_number : 5;unsigned current_next_indicator : 1;unsigned section_number : 8;unsigned last_section_number : 8; unsigned program_number : 16;unsigned reserved_3 : 3;unsigned network_PID : 13;unsigned program_map_PID : 13; unsigned CRC_32 : 32; TS_PAT; 再给出 PAT 表字段调整函数:/ Adjust
12、 PAT tablevoid adjust_PAT_table ( TS_PAT * packet, char * buffer )int n = 0, i = 0;int len = 0;packet-table_id = buffer0;packet-section_syntax_indicator = buffer1 7;packet-zero = buffer1 6 packet-reserved_1 = buffer1 4 packet-section_length = (buffer1 packet-version_number = buffer5 1 packet-current
13、_next_indicator = (buffer5 7;packet-section_number = buffer6;packet-last_section_number = buffer7; / Get CRC_32len = 3 + packet-section_length;packet-CRC_32 = (bufferlen-4 n + )packet-program_number = buffer8 reserved_3 = buffer10 5; if ( packet-program_number = 0x0 )packet-network_PID = (buffer10 p
14、rogram_map_PID = (buffer10 table_id = buffer0;packet-section_syntax_indicator = buffer1 7;packet-zero = buffer1 6; packet-reserved_1 = buffer1 4;packet-section_length = (buffer1 packet-version_number = buffer5 1 packet-current_next_indicator = (buffer5 7;packet-section_number = buffer6;packet-last_s
15、ection_number = buffer7;packet-reserved_3 = buffer8 5;packet-PCR_PID = (buffer8 reserved_4 = buffer10 4;packet-program_info_length = (buffer10 packet-CRC_32 = (bufferlen-4 / Get stream type and PID for ( ; pos section_length + 2 ) - 4; )packet-stream_type = bufferpos;packet-reserved_5 = bufferpos+1
16、5;packet-elementary_PID = (bufferpos+1 reserved_6 = bufferpos+3 4;packet-ES_info_length = (bufferpos+3 esi.pid = packet-elementary_PID;if ( packet-ES_info_length != 0 )pos = pos+5;pos += packet-ES_info_length;elsepos += 5;i+; TS 流可以复合很多的节目的视频和音频,但是解码器是怎么来区分的呢?答案就在 PMT 表里,如其名节目映射表。他就是来解决这个问题的。现在看 PMT
17、 结构体里的 stream_type、elementary_PID 这两个元素,前一个用来确定后一个作为标识 PID 的内容具体是什么,音频或视频等。还有要注意他们不只有一个,所以他们是通过循环读取来确保所有的值都被读取了,当然循环也是有规定的(具体看调整函数上)。从例子上来看,我们在倒数第三行找到了上面分析来的 PMT 表的 PID 为 0x20 的 TS 包。然后就可以把数据是用调整函数填入结构中。然后得到具体节目的 PID 为视频 0x21, 音频 0x22。PS. 文章里的 PID 是用来判断具体 TS 包是什么包的。分析每个包得到的 PID 值,都可以复合在 TS 头部结构体的 PID 里。