搜索
 找回密码
 立即注册

简单一步 , 微信登陆

TS传输流的定义(二)

作者:halleyhuang | 时间:2016-10-11 15:58:05 | 阅读:4145| 显示全部楼层
(二)PMT表(Program Map Table,节目映射表)1、 PMT表的描述  
如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表。
        PMT表中包含的数据如下:
(1) 当前频道中包含的所有Video数据的PID
(2) 当前频道中包含的所有Audio数据的PID
(3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)
        只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。
[cpp] view plaincopy


  • <span style="font-weight: normal;">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 style="color:#006600;"> </span></span>


3、 PMT表的结构体定义[cpp] view plaincopy


  • <span style="font-weight: normal;">//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;</span>

4、 PMT表的解析
[cpp] view plaincopy


  • <span style="font-weight: normal;">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;
  • }</span><span style="color:#ff6600;">
  • </span>


5、 通过一段TS流中一个Packet分析PMT表(表格+分析)
老样子,还是通过分析一段TS流的数据包Packet来学习PMT表。
        下面给出了一段TS流数据中的一个Packet(十六进制数)
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
——
——



(三)解复用模型
[cpp] view plaincopy


  • <span style="font-weight: normal;">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.pmt_pid) { Process_PMT(buff+4); Break; }  
  • } } }</span>  


  解复用的意义在于,由于TS流是一种复用的码流,里面混杂了多种类型的包;解复用TS流可以将类型相同的Packet存入相同缓存,分别处理。这样就可以将Video、Audio或者其他业务信息的数据区分开来。
(四)DVB搜台原理以及SDT表(Service Descriptor Table,业务描述表)
机顶盒先调整高频头到一个固定的频率(如498MHZ),如果此频率有数字信号,则COFDM芯片(如MT352)会自动把TS流数据传送给MPEG- 2 decoder。 MPEG-2 decoder先进行数据的同步,也就是等待完整的Packet的到来.然后循环查找是否出现PID== 0x0000的Packet,如果出现了,则马上进入分析PAT的处理,获取了所有的PMT的PID。接着循环查找是否出现PMT,如果发现了,则自动进入PMT分析,获取该频段所有的频道数据并保存。如果没有发现PAT或者没有发现PMT,说明该频段没有信号,进入下一个频率扫描。

  在解析TS流的时候,首先寻找PAT表,根据PAT获取所有PMT表的PID;再寻找PMT表,获取该频段所有节目数据并保存。这样,只需要知道节目的PID就可以根据PacketHeade给出的PID过滤出不同的Packet,从而观看不同的节目。这些就是PAT表和PMT表之间的关系。而由于PID是一串枯燥的数字,用户不方便记忆、且容易输错,所以需要有一张表将节目名称和该节目的PID对应起来,DVB设计了SDT表来解决这个问题。 该表格标志一个节目的名称,并且能和PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了。
         SDT可以提供的信息包括:
(1) 该节目是否在播放中
(2) 该节目是否被加密
(3) 该节目的名称


该会员没有填写今日想说内容.
回复

使用道具 举报

大神点评1

bean.yang 发表于:2016-10-18 11:27:38
科普
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册
手机版