| 
(二)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) 该节目的名称 
  
 
 |