2. Section
2.1 Section的概念
一个TS数据包的最大净荷为184个字节,当一个PSI/SI表的字节长度大于184字节时,就要对这个表进行分割,形成段(section)来传送。分段机制主要是将一个数据表分割成多个数据段。在PSI/SI表到TS包的转换过程中,段起到了中介的作用。由于一个数据包只有188字节,而段的长度是可变的,EIT表的段限长4096字节,其余PSI/SI表的段限长为1024字节。因此,一个段要分成几部分插入到TS包的payload中。
从TS码流中可以获取到TS包,TS包要组成Section,才能提取到想要的信息,所以首先要懂得怎么组section。
组Section之前要了解TS包在码流中发送的一些情况:
(1) TS包发送的时候PID是无序的,连续的TS包的PID可能都是不一样的;
(2) TS包发送的时候Section是相对有序的,也就是说,对于同一个PID的TS包,只有发完了一个Section,才会发送下一个Section,不然无法区分该TS包属于哪一个Section,并且对于这个Section,TS包是有序发送的,否则数据会被打乱;
(3) 某个Section的第一个TS包有PSI/SI表的一些表头信息(table_id,section_length等信息),我称之为SectionHeader,后面的TS包就没有,所以接收某个Section必须先拿到首包。
2.2 TS包组Section
TS包组section首先要找到该section的第一个TS包(下面简称为首包),首包含有该section的长度,可以用来判断一个section是不是组完了。通过判断TS包包头中的Payload Unit Start Indicator,该值为1的话,就说明这个TS包是首包,可以开始组一个section,首包含有Section的头部,结构类似下图。
拿到首包之后,要获取section的长度,有效数据的第二个字节的后四位和第三个字节组成一个12bit的字段,该值就是section_length后面数据的长度,如果算上前面三个字节,整个section的长度就是section_length += 3。将section_length和TS包有效长度进行对比,
(1)如果section_length > TS包的有效数据,证明后面还有其他的TS包,将section_length减去TS包有效数据长度,获得剩余长度;
(2)如果是section_length <= TS包的有效数据,证明该section已经结束了。
如果一个section还没组完,那么就要获取后续的TS包,后续的TS包应该是和原来相同PID,并且TS包头中continuity_counter要比原来的大1(31的话要变成0),拿到包后要与剩余长度进行对比,重复上面的步骤。
2.3 组多个Section和判全判重
对于一些PSI/SI表来说,由于数据较多,有时候不止一个section,怎么针对这个表将所有的section组全?
从上图可以看到首包里有个信息是last_section_number,这个字段表明了当前子表最后一个Section_number,也就是说,当前子表最多有last_section_number+1 个section(section_number从0开始),在获取同一个子表的section时,可以使用链表的形式,将多个section链接起来。
判全和判重:每一个section的首包信息中都有一个version_number的字段,表明当前子表的version,这个字段一旦发生变化,就表明子表发生了变化,旧版本的section就要被抛弃,重新获取新版本的section,如果版本没有发生变化,那么每获取一个section,就要判断这个section的section_number是否之前获取过,我们可以建立一个标记数组,每获取一个section,就把以section_number为下标的标记数组的值置1,表明获取过该section,如果这个标记数组下标从0到last_section_number的值都为1,证明所有的section都被收全了,如果获取了一个新的section,而其标记数组值为1,证明这个section是重复的,此时应该将它丢弃。