TS流讲解--什么是ts流
一 从TS流开始
数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PMT等信息。因此,我们首先需要了解TS流是什么,以及TS流是怎样形成、有着怎样的结构。
(一) TS流、PS流、PES流和ES流都是什么?
ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。
PES流:把基本流ES分割成段,并加上相应头文件打包成形的打包基本码流。
PS流(Program Stream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。
TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)。
由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格。
(二) TS流是如何产生的?
(三) TS流的格式是怎样的?
TS流是基于Packet的位流格式,每个包是188个字节(或204个字节,在188个字节后加上了16字节的CRC校验数据,其他格式一样)。整个TS流组成形式如下:
Packet Header(包头)信息说明 |
|||
1 |
sync_byte |
8bits |
同步字节 |
2 |
transport_error_indicator |
1bit |
错误指示信息(1:该包至少有1bits传输错误) |
3 |
payload_unit_start_indicator |
1bit |
负载单元开始标志(packet不满188字节时需填充) |
4 |
transport_priority |
1bit |
传输优先级标志(1:优先级高) |
5 |
PID |
13bits |
Packet ID号码,唯一的号码对应不同的包 |
6 |
transport_scrambling_control |
2bits |
加密标志(00:未加密;其他表示已加密) |
7 |
adaptation_field_control |
2bits |
附加区域控制 |
8 |
continuity_counter |
4bits |
包递增计数器 |
表 |
PID 值 |
PAT |
0x0000 |
CAT |
0x0001 |
TSDT |
0x0002 |
EIT,ST |
0x0012 |
RST,ST |
0x0013 |
TDT,TOT,ST |
0x0014 |
下面以一个TS流的其中一个Packet中的Packet Header为例进行说明:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
… |
|
Packet(十六进制) |
4 |
7 |
0 |
7 |
e |
5 |
1 |
2 |
… |
||||||||||||||||||||||||
Packet(二进制) |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
… |
Packet Header 信息 |
1 sync_byte=0x47 |
2 |
3 |
4 |
5 PID=0x07e5 |
6 |
7 |
8 |
… |
sync_byte=01000111, 就是0x47,这是DVB TS规定的同步字节,固定是0x47.
transport_error_indicator=0, 表示当前包没有发生传输错误.
payload_unit_start_indicator=0, 含义参考ISO13818-1标准文档
transport_priority=0, 表示当前包是低优先级.
PID=00111 11100101即0x07e5, Video PID
transport_scrambling_control=00, 表示节目没有加密
adaptation_field_control=01 即0x01,具体含义请参考ISO13818-1
continuity_counte=0010 即0x02,表示当前传送的相同类型的包是第3个
TS流的基本内容就是这些了。
回顾一下,TS流是一种位流(当然就是数字的),它是由ES流分割成PES后复用而成的;它经过网络传输被机顶盒接收到;数字电视机顶盒接收到TS流后将解析TS流。
TS流是由一个个Packet(包)构成的,每个包都是由Packet Header(包头)和Packet Data(包数据)组成的。其中Packet Header指示了该Packet是什么属性的,并给出了该Packet Data的数据的唯一网络标识符PID。
到这里,我们对TS流已经有了一定的了解,下面将从TS流转向PAT表和PMT表的学习。
二 从TS流到PAT、PMT
(一) PAT表(Program Association Table,节目关联表)
5. 通过一段TS流中一个Packet分析PAT表(表格+分析)
1. PAT表的描述(表格+分析)
PAT表定义了当前TS流中所有的节目,其PID为0x0000,它是PSI的根节点,要查寻找节目必须从PAT表开始查找。
TS流ID |
transport_stream_id |
该ID标志唯一的流ID |
节目频道号 |
program_number |
该号码标志TS流中的一个频道,该频道可以包含很多的节目(即可以包含多个Video PID和Audio PID) |
PMT的PID |
program_map_PID |
表示本频道使用哪个PID做为PMT的PID,因为可以有很多的频道,因此DVB规定PMT的PID可以由用户自己定义 |
2. PAT表的定义(代码+分析)
PAT表主要包含频道号码和每一个频道对应的PMT的PID号码,这些信息我们在处理PAT表格的时候会保存起来,以后会使用到这些数据。下面将PAT表的定义给出:
- typedef struct TS_PAT_Program
- {
- unsigned program_number : 16; //节目号
- unsigned program_map_PID : 13; // 节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个
- }TS_PAT_Program
3. PAT表的结构(代码+分析)
- typedef struct TS_PAT
- {
- unsigned table_id : 8; //固定为0x00 ,标志是该表是PAT表
- unsigned section_syntax_indicator : 1; //段语法标志位,固定为1
- unsigned zero : 1; //0
- unsigned reserved_1 : 2; // 保留位
- unsigned section_length : 12; //表示从下一个字段开始到CRC32(含)之间有用的字节数
- unsigned transport_stream_id : 16; //该传输流的ID,区别于一个网络中其它多路复用的流
- unsigned reserved_2 : 2;// 保留位
- unsigned version_number : 5; //范围0-31,表示PAT的版本号
- unsigned current_next_indicator : 1; //发送的PAT是当前有效还是下一个PAT有效
- unsigned section_number : 8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
- unsigned last_section_number : 8; //最后一个分段的号码
- std::vector<TS_PAT_Program> program;
- unsigned reserved_3 : 3; // 保留位
- unsigned network_PID : 13; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID
- unsigned CRC_32 : 32; //CRC32校验码
- } TS_PAT;
4. PAT表的解析(代码+分析)
- HRESULT CTS_Stream_Parse::adjust_PAT_table( TS_PAT * packet, unsigned char * buffer)
- {
- packet->table_id = buffer[0];
- packet->section_syntax_indicator = buffer[1] >> 7;
- packet->zero = buffer[1] >> 6 & 0x1;
- packet->reserved_1 = buffer[1] >> 4 & 0x3;
- packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
- packet->transport_stream_id = buffer[3] << 8 | buffer[4];
- packet->reserved_2 = buffer[5] >> 6;
- packet->version_number = buffer[5] >> 1 & 0x1F;
- packet->current_next_indicator = (buffer[5] << 7) >> 7;
- packet->section_number = buffer[6];
- packet->last_section_number = buffer[7];
- int len = 0;
- len = 3 + packet->section_length;
- packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
- | (buffer[len-3] & 0x000000FF) << 16
- | (buffer[len-2] & 0x000000FF) << 8
- | (buffer[len-1] & 0x000000FF);
- int n = 0;
- for ( n = 0; n < packet->section_length - 12; n += 4 )
- {
- unsigned program_num = buffer[8 + n ] << 8 | buffer[9 + n ];
- packet->reserved_3 = buffer[10 + n ] >> 5;
- packet->network_PID = 0x00;
- if ( program_num == 0x00)
- {
- packet->network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ];
- TS_network_Pid = packet->network_PID; //记录该TS流的网络PID
- TRACE(" packet->network_PID %0x /n/n", packet->network_PID );
- }
- else
- {
- TS_PAT_Program PAT_program;
- PAT_program.program_map_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];
- PAT_program.program_number = program_num;
- packet->program.push_back( PAT_program );
- TS_program.push_back( PAT_program );//向全局PAT节目数组中添加PAT节目信息
- }
- }
- return 0;
- }
从for()开始,就是描述了当前流中的频道数目(N),每一个频道对应的PMT PID是什么。解复用程序需要接收所有的频道号码和对应的PMT 的PID,并把这些信息在缓冲区中保存起来。在后部的处理中需要使用到PMT的 PID。
5. 通过一段TS流中一个Packet分析PAT表(表格+分析)
这里我们分析一段TS流其中一个Packet的Packet Data部分:
首先给出一个数据包,其数据如下:
Packet Header |
Packet Data |
0x47 0x40 0x00 0x10 |
0000 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff…… ff ff |
分析Packet Header如下表所示:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
… |
|
Packet(十六进制) |
4 |
7 |
4 |
0 |
0 |
0 |
1 |
0 |
… |
||||||||||||||||||||||||
Packet(二进制) |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
… |
Packet Header Bits |
1 sync_byte=0x47 |
2 |
3 |
4 |
5 PID=0x0000 |
6 |
7 |
8 |
… |
根据包头数据格式,我们可以知晓整个数据包的属性,列表如下:
sync_byte |
0x47 |
固定同步字节 |
transport_error_indicator |
“0” |
没有传输错误 |
payload_unit_start_indicator |
“1” |
在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。即上面数据中红色部分不属于有效数据包。 |
transport_priority |
“0” |
传输优先级低 |
PID |
0x0000 |
PID=0x0000说明数据包是PAT表信息 |
transport_scrambling_control |
“00” |
未加密 |
adaptation_field_control |
“01” |
附加区域控制 |
continuity_counte |
“0000” |
包递增计数器 |
如上表所示,我们可以知道,首先Packet的Packet Data是PAT信息表,因为其PID为0x0000,并且在包头后需要除去一个字节才是有效数据(payload_unit_start_indicator="1")。这样,Packet Data就应该是“ 00 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff …… ff ff ”。
Packet Data分析 |
|||||||||||||||||||||||||
第n个字节 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
… |
||||
Packet Data(除去开头的0x00) |
00 |
b0 |
11 |
00 |
01 |
c1 |
00 |
00 |
00 |
00 |
e0 |
1f |
00 |
01 |
e1 |
00 |
24 |
ac |
48 |
84 |
… |
||||
字段名 |
位 |
具体值 |
次序 |
说明 |
|||||||||||||||||||||
table_id |
8 |
0000 |
第1个字节 0000 0000B(0x00) |
PAT的table_id只能是0x00 |
|||||||||||||||||||||
section_syntax_indicator |
1 |
1 |
第2、3个字节 1011 0000 0001 0001B(0xb0 11) |
段语法标志位,固定为1 |
|||||||||||||||||||||
zero |
1 |
0 |
|||||||||||||||||||||||
reserved |
2 |
11 |
|||||||||||||||||||||||
section_length |
12 |
0000 0001 0001B=0x011=17 |
段长度为17字节 |
||||||||||||||||||||||
transport_stream_id |
16 |
0x0001 |
第4、5个字节 0x00 0x01 |
||||||||||||||||||||||
reserved |
2 |
11 |
第6个字节 1100 0001B(0xc1) |
||||||||||||||||||||||
version_number |
5 |
00000 |
一旦PAT有变化,版本号加1 |
||||||||||||||||||||||
current_next_indicator |
1 |
1 |
当前传送的PAT表可以使用,若为0则要等待下一个表 |
||||||||||||||||||||||
section_number |
8 |
0x00 |
第7个字节0x00 |
||||||||||||||||||||||
last_section_number |
8 |
0x00 |
第8个字节 0x00 |
||||||||||||||||||||||
开始循环 |
|||||||||||||||||||||||||
program_number |
16 |
0x0000-第一次 |
2个字节(0x00 00) |
节目号 |
|||||||||||||||||||||
reserved |
3 |
111 |
2个字节 1110 0000 0001 1111B(0xe0 1f) |
||||||||||||||||||||||
network_id(节目号为0时) program_map_PID(节目号为其他时) |
13 |
0 0000 0001 1111B=31 -第一次 |
节目号为0x0000时,表示这是NIT,PID=0x001f,即31 节目号为0x0001时,表示这是PMT,PID=0x100,即256 |
||||||||||||||||||||||
结束循环 |
|||||||||||||||||||||||||
CRC_32 |
32 |
-- |
4个字节 |
由以上几个表可以分析出PAT表和PMT表有着内在的联系。也就是之前提到的。PAT表描述了当前流的NIT(Network Information Table,网络信息表)中的PID、当前流中有多少不同类型的PMT表及每个PMT表对应的频道号。而PAT表和PMT表到底有什么深层次的联系呢?在讨论完了PMT表和SDT表后再做讨论吧。
6. 过滤PAT表信息的伪代码(代码)
- int Video_PID=0x07e5,Audio_PID=0x07e6;
- void Process_Packet(unsigned char*buff)
- { int I; int PID=GETPID(buff);
- if(PID==0x0000) { Process_PAT(buff+4); } // 如果PID为0x0000,则该Packet Data为PAT信息,因此调用处理PAT表的函数
- else{ // 这里buff+4 意味着从Packet Header之后进行解析(包头占4个字节)
- ……
- }
- }
(二) PMT表(Program Map Table,节目映射表)(Service Descriptor Table)
1. PMT表的描述
如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表。
(1) 当前频道中包含的所有Video数据的PID
(2) 当前频道中包含的所有Audio数据的PID
(3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)
只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。
2. PMT表的定义(代码)
- <span style="font-size:14px;">//PMT 表定义</span>
- <span style="font-size:14px;">typedef struct TS_PMT_Stream
- {
- unsigned stream_type : 8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定
- unsigned elementary_PID : 13; //该域指示TS包的PID值。这些TS包含有相关的节目元素
- unsigned ES_info_length : 12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数
- unsigned descriptor;
- }TS_PMT_Stream;
- </span>
3. PMT表的结构体定义(代码)
- //PMT 表结构体
- typedef struct TS_PMT
- {
- unsigned table_id : 8; //固定为0x02, 表示PMT表
- unsigned section_syntax_indicator : 1; //固定为0x01
- unsigned zero : 1; //0x01
- unsigned reserved_1 : 2; //0x03
- unsigned section_length : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
- unsigned program_number : 16;// 指出该节目对应于可应用的Program map PID
- unsigned reserved_2 : 2; //0x03
- unsigned version_number : 5; //指出TS流中Program map section的版本号
- unsigned current_next_indicator : 1; //当该位置1时,当前传送的Program map section可用;
- //当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。
- unsigned section_number : 8; //固定为0x00
- unsigned last_section_number : 8; //固定为0x00
- unsigned reserved_3 : 3; //0x07
- unsigned PCR_PID : 13; //指明TS包的PID值,该TS包含有PCR域,
- //该PCR值对应于由节目号指定的对应节目。
- //如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
- unsigned reserved_4 : 4; //预留为0x0F
- unsigned program_info_length : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。
- std::vector<TS_PMT_Stream> PMT_Stream; //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定
- unsigned reserved_5 : 3; //0x07
- unsigned reserved_6 : 4; //0x0F
- unsigned CRC_32 : 32;
- } TS_PMT;
4. PMT表的解析(代码)
- //PMT 表的解析
- HRESULT CTS_Stream_Parse::adjust_PMT_table ( TS_PMT * packet, unsigned char * buffer )
- {
- packet->table_id = buffer[0];
- packet->section_syntax_indicator = buffer[1] >> 7;
- packet->zero = buffer[1] >> 6 & 0x01;
- packet->reserved_1 = buffer[1] >> 4 & 0x03;
- packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
- packet->program_number = buffer[3] << 8 | buffer[4];
- packet->reserved_2 = buffer[5] >> 6;
- packet->version_number = buffer[5] >> 1 & 0x1F;
- packet->current_next_indicator = (buffer[5] << 7) >> 7;
- packet->section_number = buffer[6];
- packet->last_section_number = buffer[7];
- packet->reserved_3 = buffer[8] >> 5;
- packet->PCR_PID = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;
- PCRID = packet->PCR_PID;
- packet->reserved_4 = buffer[10] >> 4;
- packet->program_info_length = (buffer[10] & 0x0F) << 8 | buffer[11];
- // Get CRC_32
- int len = 0;
- len = packet->section_length + 3;
- packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
- | (buffer[len-3] & 0x000000FF) << 16
- | (buffer[len-2] & 0x000000FF) << 8
- | (buffer[len-1] & 0x000000FF);
- int pos = 12;
- // program info descriptor
- if ( packet->program_info_length != 0 )
- pos += packet->program_info_length;
- // Get stream type and PID
- for ( ; pos <= (packet->section_length + 2 ) - 4; )
- {
- TS_PMT_Stream pmt_stream;
- pmt_stream.stream_type = buffer[pos];
- packet->reserved_5 = buffer[pos+1] >> 5;
- pmt_stream.elementary_PID = ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;
- packet->reserved_6 = buffer[pos+3] >> 4;
- pmt_stream.ES_info_length = (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];
- pmt_stream.descriptor = 0x00;
- if (pmt_stream.ES_info_length != 0)
- {
- pmt_stream.descriptor = buffer[pos + 5];
- for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )
- {
- pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len];
- }
- pos += pmt_stream.ES_info_length;
- }
- pos += 5;
- packet->PMT_Stream.push_back( pmt_stream );
- TS_Stream_type.push_back( pmt_stream );
- }
- return 0;
- }
5. 通过一段TS流中一个Packet分析PMT表(表格+分析)
老样子,还是通过分析一段TS流的数据包Packet来学习PMT表。
Packet Header |
Packet Data |
0x47 0x43 0xe8 0x12 |
00 02 b0 12 00 01 c1 00 00 e3 e9 f0 00 1b e3 e9 f0 00 f0 af b4 4f ff ff…… ff ff |
首先解析Packet Header,分析如下:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
… |
|
Packet(十六进制) |
4 |
7 |
4 |
3 |
e |
8 |
1 |
2 |
… |
||||||||||||||||||||||||
Packet(二进制) |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
… |
Packet Header Bits |
1 sync_byte=0x47 |
2 |
3 |
4 |
5 PID=0x03e8 |
6 |
7 |
8 |
… |
PID=0x03e8为其PID
下面是详细的解析表
Packet Header分析 |
|||
Packet Header:0x47 0x40 0x00 0x10 |
|||
1 |
sync_byte |
0x47 |
固定同步字节 |
2 |
transport_error_indicator |
“0” |
没有传输错误 |
3 |
payload_unit_start_indicator |
“1” |
在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。 |
4 |
transport_priority |
“0” |
传输优先级低 |
5 |
PID |
0x03e8 |
PID=0x03e8说明数据包是PMT表信息 |
6 |
transport_scrambling_control |
“00” |
未加密 |
7 |
adaptation_field_control |
“01” |
附加区域控制 |
8 |
continuity_counte |
“0010” |
包递增计数器 |
因为payload_unit_start_indicator=‘1’,在解析数据包的时候需要去除Packet Data的第一个字节。下面是对Packet Data的详细解析:
PMT表的Packet Data分析 |
|||||||||||||||||||||||||
第n个字节 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
… |
||||
Packet Data |
02 |
b0 |
12 |
00 |
01 |
c1 |
00 |
00 |
e3 |
e9 |
f0 |
00 |
1b |
e3 |
e9 |
f0 |
00 |
f0 |
1b |
e3 |
… |
||||
字段名 |
位数 |
具体值 |
次序 |
说明 |
|||||||||||||||||||||
table_id |
8 |
0x02 |
第1个字节 |
||||||||||||||||||||||
section_syntax_indicator |
1 |
1B |
第2、3个字节 1011 0000 0001 0010B=0xb012 |
段语法标志 |
|||||||||||||||||||||
zero |
1 |
0B |
|||||||||||||||||||||||
reserved |
2 |
11B=0x03 |
|||||||||||||||||||||||
section_length |
12 |
0000 0001 0010B=0x12 |
段长度,从program_number开始,到CRC_32(含)的字节总数 |
||||||||||||||||||||||
program_number |
16 |
0x0001 |
第4、5个字节 0x00 01 |
频道号码,表示当前的PMT关联到的频道 |
|||||||||||||||||||||
reserved |
2 |
11B=0x03 |
第6个字节 1100 0001B=0xc1 |
||||||||||||||||||||||
version_number |
5 |
00000B=0x00 |
版本号码,如果PMT内容有更新,则它会递增1通知解复用程序需要重新接收节目信息 |
||||||||||||||||||||||
current_next_indicator |
1 |
1B=0x01 |
当前未来标志符 |
||||||||||||||||||||||
section_number |
8 |
0x00 |
第7个字节0x00 |
当前段号码 |
|||||||||||||||||||||
last_section_number |
8 |
0x00 |
第8个字节 0x00 |
最后段号码,含义和PAT中的对应字段相同 |
|||||||||||||||||||||
reserved |
3 |
111B=0x07 |
第9、10个字节 1110 0011 1110 1001B=0xe3e9 |
||||||||||||||||||||||
PCR_PID |
13 |
000111110B=0x3e9 |
PCR(节目参考时钟)所在TS分组的PID |
||||||||||||||||||||||
reserved |
4 |
1111B=0x0f |
第11、12个字节 1111 0000 0000 0000=0xf000 |
||||||||||||||||||||||
program_info_length |
12 |
000000000000B=0x000 |
节目信息长度(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes)紧接着就是频道内部包含的节目类型和对应的PID号码了 |
||||||||||||||||||||||
stream_type |
8 |
0x1b |
第13个字节 0x1b |
流类型,标志是Video还是Audio还是其他数据 |
|||||||||||||||||||||
reserved |
3 |
111B=0x07 |
第14、15个字节 1110 0011 1110 1001B=0xe3e9 |
||||||||||||||||||||||
elementary_PID |
13 |
000111110 1001=0x3e9 |
该节目中包括的视频流,音频流等对应的TS分组的PID |
||||||||||||||||||||||
reserved |
4 |
1111B=0x0f |
第16、17个字节 1111 0000 0000 0000B=0xf000 |
||||||||||||||||||||||
ES_info_length |
12 |
0000 0000 0000=0x000 |
|||||||||||||||||||||||
CRC |
32 |
—— |
—— |
(三) 解复用模型(代码)
- int Video_PID=0x07e5,Audio_PID=0x07e6;
- void Process_Packet(unsigned char*buff)
- {
- int i; int PID=GETPID(buff);
- if(PID==0x0000) { Process_PAT(buff+4); } //PAT表的PID为0x0000
- else if(PID==Video_PID) { SaveToVideoBuffer(buff+4); } //PID指示该数据包为视频包
- else if(PID==Audio_PID) { SaveToAudioBuffer(buff+4); } //PID指示该数据包为音频包
- else{ // buff+4 意味着要除去buff前4个字节(即包头)
- for( i=0;i<64;i++)
- { if(PID==pmt[i].pmt_pid) { Process_PMT(buff+4); Break; }
- } } }
解复用的意义在于,由于TS流是一种复用的码流,里面混杂了多种类型的包;解复用TS流可以将类型相同的Packet存入相同缓存,分别处理。这样就可以将Video、Audio或者其他业务信息的数据区分开来。
(四) DVB搜台原理以及SDT表(Service Descriptor Table,业务描述表)
(1) 该节目是否在播放中
(2) 该节目是否被加密
(3) 该节目的名称
三、 从PAT开始,走向更远
TS流讲解--什么是ts流相关推荐
- 对TS流的一些理解TS流的结构
TS流的结构 TS流由很多个TS包组成,而每个TS包的结构包括:4B的包头,可变长的调整字段,有效净荷(payload).现在分别说一下这三部分的结构和作用. 包头 包头最小为4字节,这四字节分 ...
- 将RTSP流保存为本地TS文件
1.功能:将RTSP流保存为本地TS文件 2.存在问题: 保存mp4文件播放不了,还未解决-希望路过的大佬帮忙瞅瞅 _ 3.流程: 0)初始化:并注册所有的解封装器.封装器和协议,初始化网络库: 1) ...
- MPEG-2传输流的码流分析(PS与TS码流)
0 引言 目前,我国数字电视的信道传输的三种主要传输方式都有了统一明确的国家标准.其主要差别是信道编码.调制方法,但视频和音频信源处理和系统层复用都是以MPEG-2为基础的编码方案.MPEG-2标准自 ...
- [BZOJ3698]XWW的难题(有源汇上下界最大流+讲解)
题目: 我是超链接 题解: 建图的话和有源汇可行流一样 求解方法: 在新图上跑ss到tt的最大流(附加源汇) 若新图满流,那么一定存在一种可行流 记此时∑f(s,i)=sum1∑f(s,i)=sum1 ...
- java io流操作_十个Demo进行讲解Java中IO流的常用操作~
好久不见的IO流 对IO流的学习,我记得还是初学Java基础的时候,后来找工作过程中经常看到有些招聘信息中写到熟悉IO流,现在想想IO流,真的是一脸懵逼,不说这么多废话了,IO流这次好好整理一下. 说 ...
- ANSYS APDL学习(8):选取全部的封闭曲面生成实体(体积)的命令流讲解
选取全部的封闭曲面生成实体(体积)的命令流讲解 来源 封闭曲面生成体结果: 应用 背景: GUI步骤 FLST命令 FITEM命令 VA 命令 来源 书籍:< Ansys Mechanical ...
- “万字“ Java I/O流讲解
Java I/O流讲解 每博一文案 谁让你读了这么多书,又知道了双水村以外还有一个大世界,如果从小你就在这个天地里,日出而作,日落而息. 那你现在就会和众乡亲抱同一理想:经过几年的辛劳,像大哥一样娶个 ...
- lo流讲解 和使用(大纲)
IO流 IO:input.output 流:指的是程序中数据的流动方向 分类: 按流向分: 输入流:从文件到控制台(读取文件中的数据至控制台) 输出流:从控制台到文件(将控制台中的数据存储至文件) 按 ...
- Java19-day10【标准输入输出流、字节字符打印流、对象序列化-反序列化流、serialVersionUIDtransient、Properties】
视频+资料[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg 提取码:zjxs] Java基础--学习笔记(零起点打开java世界的大门)--博 ...
最新文章
- 什么是分布式系统,如何学习分布式系统(转)
- 【知识小课堂】mongodb 之 查询关键词使用
- java之异常java.net.MalformedURLException解决办法
- 查看oracle 锁定用户名,oracle用户名被锁定
- 有哪些类目适合刚创业的新手淘宝卖家做?
- 提高网站访问速度的方法汇总
- java之Calendar类
- js 正则中冒号代表什么_是否还在疑惑Vue.js中组件的data为什么是函数类型而不是对象类型...
- stata 亚组分析_手把手教你用R的gemtc包对分类变量进行贝叶斯网状Meta分析
- 浅谈分子动力学(MD)模拟及其势文件
- Booth算法笔算快捷方式
- 摄像头网络模组的使用
- 计算机科学家刘欣,科学家都爱啥运动?
- 7-32 哥尼斯堡的“七桥问题”
- 从Trie树(字典树)谈到后缀树(10.28修订)
- 清华大学推荐:这32本书籍你看过几本?
- 华为云容器镜像服务 SWR 加速镜像的拉取和推送
- 基于Aforge的手势识别之一~~~简单的手写识别
- selenium 火狐下载弹框去除
- KU115 FPGA 高性能万兆光纤网络硬件加速卡 / 2 路 10G 光纤数据加速卡
热门文章
- 初级ai思维导图,基础人工智能设计图
- s8 android10,三星S8和Note 8不会获得Android 10升级
- 武忠祥老师每日一题、考研题型总结
- Dialog 使用详解
- otrs软件_在Linux下安装OTRS系统
- 练习赛 | Datawhale和Kesci联合发起的比赛实践
- linux基础(1)-常见指令及权限理解
- 大学英语计算机二级,大学英语四级和计算机二级到底有没有用?看完这篇就懂了!...
- nginx代理二级目录
- 前沿分享|阿里云高级技术专家 王若(百润): 数据库游戏行业最佳实践