PSI/SI表的构成是:一个表由一个或多个子表构成,表用table_id来标识;不同的子表由table_id和table_id_extension来区分(具有相同的table_id和不同的table_id_extension);一个子表由一个或多个段构成(具有相同的table_id和table_id_extension,不同section_number来区分);每个段由多个TS数据包的数据组成。每个段具有一个完整的数据结构,表的重要参数----描述符在段里传送。
PSI:(ProgramSpecial Information)节目专用信息表。
PSI信息由节目关联表PAT、条件接收表CAT、节目映射表PMT组成。PSI信息用来描述TS的组成结构;PSI提供了在传送流包中传送节目的初始化信息,信息指定了如何从一个携带多个节目的传输流中找到指定的节目。
表名 |
标准 |
PID |
Table_id |
详情 |
PAT(节目关联表) |
MPEG-2 |
0x0000 |
0x00 |
定义了TS中的所有节目,是PSI信息的根节点,含有所有节目对应的PMT的PID |
PMT(节目映射表) |
MPEG-2 |
从PAT获取 |
0x02 |
提供节目号码与组成它们的原始码流之间的映射,完整地描述了一路节目是由哪些PES组成的,以及它们的PID |
CAT(条件接收表) |
MPEG-2 |
0x0001 |
0x01 |
提供了一个或者多个CA系统及其授权信息,如果对清流进行了加扰处理,那么就必须有CAT表,其包含加扰系统的细节;条件访问管理与授权信息的传送包的PID值 |
SI:Service Information.业务信息表
SI信息内容是按照network(网络)→transport strem(传输流)→service(业务)→event(事件)的分层顺序描述。SI数据信息是按照一定的数据结构进行存储的。SI数据主要有:网络信息、传输流信息、业务信息、业务的事件信息等,并且大量的信息都是通过描述符来传输的。
为了能在一个TS流中提取出各种各样的信息,PSI/SI表里面使用了很多的标识。
Network_id、Original_network_id、Transport_stream_id、Service id、event_id、Bouquet_id
Network_id可以定位一个网络
Network_id、Original_network_id、Transport_stream_id可以定位一个TS流,该流来源于哪个原始网络,目前在哪个网络里播放
Network_id、Original_network_id、Transport_stream_id、Service id可以定位一个节目,这个就是SDT表的内容
Network_id、Original_network_id、Transport_stream_id、Service id、event_id可以定位节目里面的某一个Event,这就是EIT表
Network_id、Original_network_id、Transport_stream_id、Bouquet_id可以定位一个BAT分组,分组下面有service_id可以定位分组里所有节目
表名 |
PID |
Table_id |
详情 |
NIT(网络信息表) |
默认0x0010 一般从PAT表获取 |
当前网络:0x40 其他网络:0x41 |
传递了与通过一个给定的网络传输的TS流的物理结构相关的 信息,以及与网络自身特性相关的信息。 |
SDT(业务描述表) |
0x11 |
现行TS:0x42 其他TS:0x46 |
描述包含于一个特定的传输流中的业务。该业务可能是现行传输流中的一部分,也可能是其他传输流中的一部分,可以根据table_id 来确定。 |
BAT(业务分组表) |
0x11 |
0x4A |
提供有关业务群的信息。业务群定义为一组业务的集合,并可能横跨于不同的网络上。 |
EIT(节目段信息表) |
0x12 |
当前TS:PF 0x4E Schedule: 0x50-0x5F 其他TS:PF 0x4F Schedule: 0x60-0x6F |
事件信息表EIT按时间顺序提供每一个业务所包含的事件的信息。按照不同table_id,有四类EIT:当前TS的PF信息和Schedule信息,其他TS的PF信息和Sechedule信息。 |
组完Section之后,应该按照PID和Table_Id将Section过滤到对应的表的解析模块处进行解析,section_length等只需解析时使用的变量可以定义成局部变量不保存,其他的必要信息可以定义在结构体里,解析时进行保存。
解析表的时候,只要根据表的结构将数据一一解析出来就可以了,一般请求的数据会使用char指针或者数组存储,解析的时候可以用char *pdata指针来读取数据,每次读取足够的字节数后,就将pdata+= 上次读取的字节数,这样不用去实时地计算数据读取到了哪个位置,编写代码更加高效。需要注意的是如果pData下传到函数里,函数运行结束后pData的值仍是传下去那一个值,就算在函数里对pdata操作了也不会影响函数外面pdata的值,所以解析描述符的时候可以将pdata下传到子函数里面去解析,但是解析完成的时候记得将pdata往后移动该描述符的长度。
PAT结构体可以如下定义
/*PAT表结构*/
typedef struct CSTSPATSection_S
{
DWORD m_dwTransport_Stream_Id;
int m_nLoopNum; //PAT表ProgramNumber的个数
PATProgram *m_pstPATProgram;
struct CSTSPATSection_S *m_pstNext;//以链表的形式存储PAT表,这个是next指针
}PATSection;
PAT表ProgramNumber为0所指示的PID是NIT表的PID,如果没有ProgramNumber为0的PID,则默认0x10为NIT表的PID;
解析PAT表只需存储TSId、ProgramNumber及其PID;如果PAT表不止一个Section(一般是一个),还需要用链表的方式将信息存储起来
PMT表必须在PAT表解析完成之后进行解析,获得所有当前节目的节目号(ProgramNumber)及其PID之后,就可以请求PMT表了,所以PMT表可能会有很多个,可以使用链表的形式存储,也可以使用结构体数组的形式,为了和SDT等表兼容,最终采用了链表的形式。每个PMT表都可以解析出音视频PID的信息,后续就可以根据这些PID去请求音视频数据了
三个表的结构如上,解析依旧按照上述PAT、PMT表的方式进行解析就可以,如果有多重循环就使用多个while语句进行解析,基本能解出第一重,后续多几重循环也就是多写大同小异的几个子函数。
这几个表是可能有多个子表的,多个子表也需要判全的,因为不像PAT表,一个频点就只会有一个表,只要Section找全了表就收全了,而PMT表每个PID只会有一个Section,只要多个节目对应的PID收全了就OK。NIT表分为Actual和Other,分表代表当前网络和其他网络,一个网络里面会有多个TS_id,每个TS_id代表一个频点,每个频点里面有Service_id、Service_name等信息,所以SDT子表就可以根据NIT来判全。如果是NIT Actual和SDT Actual,只需搜索当前频点就可以了,判全也只需判断Section是否收全了,如果是SDT Other表,就可能会有多个子表了,要判断除去主频点TS_id,其他TS_id的频点是否收全了,才可以完全判断SDT是否收全了。BAT个人理解没有可以提前判断有多少个bouquet_id的方法,但是bouquet_id的个数有上限,所以BAT都是有几个子表就收几个
首先,EIT表对于每一个子表的判全也是不一样的,这部分应该跟其他表区分开来,EIT表每个Section的Section_length上限是4096,并且有segment的概念,每个segment里面最多有8个Section,所以EIT不仅要对单个Section进行判全,还要对Segment里面多个Section进行判全,项目解析里面是利用Segment_last_section_number对8取余再加一,结果就是Segment里面Section的个数了。
对于EIT子表的判全,必须利用SDT表进行,SDT表里面每个ServiceId下都有两个EIT相关的字段,分别是EIT_schedule_flag、EIT_Present_following_flag,如果两个标记为1,就表示这个Service_Id有对应的EIT子表,根据这个就可以判全EIT子表了。
描述符是存在于Section里面的,可以跟表的其他重要字段一起解析出来,解析方法大同小异。
对于PSI/SI表来说,有些描述符是比较重要的,一般都要解析出来,如下面所示:
表名 |
描述符 |
tag |
描述 |
|
network_name_descriptor |
0x40 |
提供网络的名称 |
|
service_list_descriptor |
0x41 |
提供网络下的节目列表 |
NIT |
satellite_delivery_system_descriptor |
0x43 |
提供卫星相关频点等信息 |
|
cable_delivery_system_descriptor |
0x44 |
提供有线相关频点等信息 |
|
terrestrial_delivery_system_descriptor |
0x5A |
提供地面相关频点等信息 |
BAT |
bouquet_name_descriptor |
0x47 |
提供分组名称信息 |
|
service_list_descriptor |
0x41 |
提供分组下的节目列表 |
SDT |
service_descriptor |
0x48 |
提供节目描述信息 |
EIT |
short_event_descriptor |
0x4D |
短事件描述符 |
|
extended_event_descriptor |
0x4E |
拓展信息事件描述符 |