MPEG2 PS和TS流格式--非常重要

 

l    PSI的PID是特定的,含PSI的数据包必须周期性的出现在传输流中。


PMT (Program Map Table )节目映射表


PMT所在分组的PID由PAT指定,所以要先解出PAT,再解PMT


PMT中包含了属于同一节目的视频、音频和数据原始流的PID。


找到了PMT,解多路复用器就可找到一道节目对应的每个原始流的PID,再根据原始流


PID,去获取原始流。如下图:PID1和PID2分别对应某道节目的视频原始流和音频原始流


的PID。





l      PAT (Program Association Table )节目关联表

l      PAT所在分组的PID=0


      PAT中列出了传输流中存在的节目流

l

l      PAT指定了传输流中每个节目对应PMT所在分组的PID

l      PAT的第一条数据指定了NIT所在分组的PID ,其他数据指定了PMT所在分组的PID,如下图所示:








lCAT (Conditional Access Table )条件访问表

lCAT所在分组的PID=1

lCAT中列出了条件控制信息(ECM)和条件管理信息(EMM)所在分组的PID。

lCAT用于节目的加密和解密

 

lNIT( Network Information Table)网络信息表

lNIT所在分组的PID由PAT指定

lNIT提供一组传输流的相关信息,以及于网络自身特性相关的信息,比如网络名称,传输参数(如频率,调制方式等)。

lNIT一般是解码器内部使用的数据,当然也可以做为EPG的一个显示数据提供给用户做为参考。



几种PSI之间的关系,如下图所示:首先PAT中指定了传输流中所存在的节目,及每个节目对应的PMT的PID号。 比如Program 1对应的PMT 的PID=22,然后找到PID=22的TS分组,解出PMT,得到这个节目中包含的原始流的PID,再根据原始流的PID去找相应的TS分组,获取原始流的数据,然后就可以送入解码器解码了。




二、数据结构


(1)TS分组


前面提到,TS分组由188个字节构成,其结构如下:







transport_packet()


sync_byte                                                                    // 8
transport_error_indicator                                          //1
payload_unit_start_indicator                                    //1
transport_priority                                                       // 1 

PID                                                                             //13
transport_scrambling_control                                  // 2
adaptation_field_control                                            //2
continuity_counter                                                      //4
if(adaptation_field_control=='10'  || adaptation_field_control=='11'){ 
  adaptation_field() 

if(adaptation_field_control=='01' || adaptation_field_control=='11') { 
  for (i=0;i   data_byte                                                                   //8
  } 

}


前面32bit的数据即TS分组首部,它指出了这个分组的属性。

sync_byte  同步字节,固定为0x47 ,表示后面的是一个TS分组,当然,后面包中的数据是不会出现0x47的


transport_error_indicator 传输错误标志位,一般传输错误的话就不会处理这个包了


payload_unit_start_indicator 这个位功能有点复杂,字面意思是有效负载的开始标志,根据后面有效负载的内容不同功能也不同,后面用到的时候再说。


transport_priority  传输优先级位,1表示高优先级,传输机制可能用到,解码好像用不着。


PID  这个比较重要,指出了这个包的有效负载数据的类型,告诉我们这个包传输的是什么内容。前面已经叙述过。


       transport_scrambling_control加密标志位,表示TS分组有效负载的加密模式。TS分组首部(也就是前面这32bit)是不应被加密的,00表示未加密。


        adaption_field_control  翻译为“调整字段控制”,表示TS分组首部后面是否跟随有调整字段和有效负载。01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00的话解码器不进行处理。空分组没有调整字段。


       continuity_counter   一个4bit的计数器,范围0-15,具有相同的PID的TS分组传输时每次加1,到15后清0。不过,有些情况下是不计数的。如下:(1)TS分组无有效负载(2)复制的TS分组和原分组这个值一样(3)后面讲到的一个标志discontinuity_indicator为1时

      adaptation_field()        调整字段的处理

      data_byte                      有效负载的剩余部分,可能为PES分组,PSI,或一些自定义的数

                                            据。

(2)PAT


  PAT数据结构如下






program_association_section() {
table_id                                                                    // 8
section_syntax_indicator                                      //1
'0'                                                                           //1
reserved                                                              // 2
section_length                                                    //12
transport_stream_id                                         // 16
reserved                                                           // 2
version_number                                                 // 5
current_next_indicator                                     //1
section_number                                                //8
last_section_number                                         // 8
for (i=0; i  program_number                                              // 16
  reserved                                                          // 3
  if(program_number == '0') {
   network_PID                                                  //  13
  }
  else {
   program_map_PID                                           // 13
  }
}
CRC_32                                                            //  32
}

table_id  固定为0x00 ,标志是该表是PAT


section_syntax_indicator 段语法标志位,固定为1


section_length         表示这个字节后面有用的字节数,包括CRC32。假如后面的字节加上前面的字节数少于188,后面会用0XFF填充。假如这个数值比较大,则PAT会分成几部分来传输。


transport_stream_id    该传输流的ID,区别于一个网络中其它多路复用的流。



version_number       范围0-31,表示PAT的版本号,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经变化了,程序必须重新搜索节目.


current_next_indicator 表示发送的PAT是当前有效还是下一个PAT有效。


       section_number       分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段


加1,最多可能有256个分段


       last_section_number   最后一个分段的号码


       program_number  节目号



       network_PID   网络信息表(NIT)的PID,网络信息表提供了该物理网络的一些信息,和电视台相关的。节目号为0时对应的PID为network_PID


program_map_PID  节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个


CRC_32   CRC32校验码


上面program_number,network_PID,program_map_PID 是循环出现的。program_number等于0时对应network_PID,program_number等于其它值时对应program_map_PID。


举个例子,下述流为带PAT的TS分组:


47 40 00 1c 00 00 b0 15 13 f6 e7 00 00 00 00 e0 10 00 01 e0 20 00 02 e0 21 1a 34 b4 77 ff…………..ff


其中红色的四个字节是TS分组头部,用数据结构解出首部,得到PID=0x00,表示为该分组的有效负载是PAT。蓝色的00称为“指针域”----Pointer field,表示了一个偏移量,即从后面第几个字节开始是PAT部分。为00表示后面紧接着的就是PAT:00 b0 15 13 f6 e7 00 00 00 00 e0 10 00 01 e0 20 00 02 e0 21 1a 34 b4 77


再利用PAT的数据结构解出PAT,得到如下信息:


---------------PAT Information-------------


table_id: 00


section_syntax_indicator: 01


section_length: 0015


transport_stream_id: 13f6


version_number: 13


current_next_indicator: 01


section_number: 00


last_section_number: 00


program_number: 0000


network_PID: 0010


program_number: 0001


program_map_PID: 0020


program_number: 0002


program_map_PID: 0021


CRC_32: 1a34b477




可以看出,此PAT只有一段,包含了三个节目,节目号0000对应于network_PID=0010 ,节目号0001对应于program_map_PID =0020,节目号0002对应于program_map_PID =0021,从实际的角度,我们应该把这三个节目号理解为三个频道,第一个频道中的内容是网络信息,第二、三个频道包含了节目信息。在数字电视中,一个频道即对应于一个频点,如498MHZ,一个频道上可以有多个节目,后面的PMT即是告诉了我们某个频道中所有节目对应的PID。


于是现在就搜寻PID=0x0020的TS分组,即是频道2对应的PMT信息。




( 其实楼主的理解不是完全正确,transport_stream_id标识了一个唯一的传输流(每一个传输流对应一个频点,如498MHz),一个PAT表表示一个流里面的信息。
上面的三个节目号理解为三个频道是正确的,但在数字电视中,一个频道对应的也是一个节目,而不是一个频点(当然节目号为0时对应的是NIT的PID)。
)



(3)PMT


  PMT数据结构如下:





TS_program_map_section() {
table_id                                                                              // 8
section_syntax_indicator                                                  //1
'0'                                                                                      //  1
reserved                                                                           //    2
section_length                                                                   //  12
program_number                                                               //16
reserved                                                                              // 2
version_number                                                                  //5
current_next_indicator                                                       //1
section_number                                                                  // 8
last_section_number                                                          //8
reserved                                                                               //3
PCR_PID                                                                             //13
reserved 4
program_info_length                                                          //12
for (i=0; i  descriptor()
}
for (i=0;i  stream_type                                                                        //8
  reserved                                                                               //3
  elementary_PID                                                                  //13
  reserved                                                                               //4
  ES_info_length                                                                   //12
  for (i=0; i   descriptor()
  }
}
CRC_32                                                                               //32
}

table_id  固定为0x02 ,标志是该表是PMT


section_syntax_indicator


section_length    


version_number     


current_next_indicator 以上四个字段意思和PAT相同,可参考上面解释


       section_number     


       last_section_number   以上两个字段意思和PAT相同,不过值都固定为0x00,我觉得这样的原因可能是因为PMT不需要有先后顺序,因为先定义哪个节目都是无所谓。


       program_number  节目号,表示该PMT对应的节目


       PCR_PID            PCR(节目时钟参考)所在TS分组的PID,根据PID可以去搜索相应的TS分组,解出PCR信息。


program_info_length  该节目的信息长度,在此字段之后可能会有一些字节描述该节目的信息


stream_type          指示了PID为elementary_PID的PES分组中原始流的类型,比如视频流,音频流等,见后面的表


elementary_PID      该节目中包括的视频流,音频流等对应的TS分组的PID


ES_info_length      该节目相关原始流的描述符的信息长度。



stream_type对应的类型:



还是举个例子,下述是一个包含PMT的TS分组,


  47 40 20 1c 00 02 b0 1f 00 01 e7 00 00 e1 00 f0 00 02 e1 00 f0 05 02 03 b2 44 5f 04 e1 10 f0 03 03 01 67 c9 ab c8 d2


红色的四个字节是TS分组头部,蓝色的00是“指针域”,意义同PAT中的指针域。所以下面的数据就是PMT的内容:02 b0 1f 00 01 e7 00 00 e1 00 f0 00 02 e1 00 f0 05 02 03 b2 44 5f 04 e1 10 f0 03 03 01 67 c9 ab c8 d2


再解出PMT,得到下列信息:


table_id: 02


section_syntax_indicator: 01


section_length: 01f


program_number: 0001


version_number: 13


current_next_indicator: 01


section_number: 00


last_section_number: 00


PCR_PID: 0100


program_info_length: 000


descriptor:


steam_type: 00


elementary_PID: 0001


ES_info_length: 000


descriptor:


steam_type: 02


elementary_PID: 0001


ES_info_length: 005


descriptor: 02 03 b2 44 5f


steam_type: 04


elementary_PID: 0011


ES_info_length: 003


descriptor: 03 01 67


CRC_32: c9abc8d2



可以看出,该节目号0001包含了三个流的信息,流类型分别为00,02,04,00的流为保留值,可以不考虑,02表示原始流为视频流,其elementary_PID为0001,04表示原始流为音频流,其elementary_PID为0011,两个流分别还带有descriptor(描述符),说明了该原始流的一些信息。


得到了这个elementary_PID,再从后面的传输流中找到PID为这个值的TS分组,其有效负载即为这个原始流的数据,获取数据送到解码器,即可还原这个视频或音频了。




三、总结


上面的都是一些零散的知识,跟我们实际应用有什么关系呢?下面就是一个简易的应用过程---搜台。搜台过程大致如下:


先调整高频头到一个固定的频率(498MHZ),如果此频率有数字信号,相关芯片会自动把TS流数据传送给MPEG- 2 decoder. MPEG-2 decoder先进行数据的同步,也就是等待完整的Packet的到来.然后循环查找是否出现PID== 0x0000Packet,如果出现了,则马上进入分析PAT的处理,获取了所有的PMTPID.接着循环查找是否出现PMT,如果发现了,则自动进PMT分析,获取该频段所有的频道数据并保存.如果没有发现PAT或者没有发现PMT,说明该频段没有信号,进入下一个频率扫描


上述过程主要涉及到PATPMT的一些解码和解复用知识,这也是目前我学习到的,当然,数字电视涉及到的知识远远不止这些,解码方面就还包括调整字段的处理,SI(业务信息)应用,时钟的处理,CA加密解MI系统等,还需要继续的学习和实践。

你可能感兴趣的:(h264/h265,音视频综合,Mpeg2,TS,PS)