ts包、表、子表、section的关系
我们经常接触到创建 DEMUX,注册 Filter 过滤数据, 通过回调过滤出 section 数据,然后我们对 section 数据做具体的解析或者其他操作。 我们这里说的 section 就是段的概念,一个 section 可能包含一个或者多个TS 包,我们可以这样理解,TS 是对数据内容发封装,属于传输层的格式,规定了传输数据的格式,它以 188 字节为单位组成一个 TS 包,在这一层,它不管封装的内容是什么,就是在传输的内容前加 上 4 个字节的头信息组成包。而我们 filter 过滤出来的 section 数据是去掉了 TS 包头的的有 效数据,可能是多个 TS 包组成起来的有效数据,我们解复用 DEMUX 中也包括对接收到的 TS 包数据,根据不同的 PID 来过滤出相应的 TS 包,然后去掉包头,把相关的多个 TS 包有 效数据组织起来形成 section 数据返回给应用开发者调用。 下图是我自己做的关于表,段,TS 包的结构关系:
一个表由一个或多个段构成 (具有相同的 table_id 和 table_id_extension, 不同 section_number 来区分,并且由 last_section_number 确定该表的最大 section 的数目) ;每个段由一个或多个 TS 数 据 包 的 数 据 组 成 , 比 如 一 个 新 的 section 数 据 , 那 么 第 一 个 TS 包 的 payload_unit_start_indicator 一般为 1,后续该 section 的 TS 包的 payload_unit_start_indicator 为 0,直到另外的 section 数据到来时候 TS 包的 payload_unit_start_indicator 才变为 1(同时 也说明该 section 数据结束, section 数据的开始) 而 continuity_counter 随着具有相同 PID 新 , 的 TS 包的增加而增加,这样我们就可以方便我们组织各个 TS 包来获取 section 数据。同时 我们的 section 数据就是去掉各个 TS 包头后组织起来的有效载荷数据, 我们可以对比码流分 析仪来分析。
一般一个 section 的长度是 1024,而 TS 包是 188,所以一般都是一个 section 是由多个 TS 包组成,当然也有可能一个 section 就是由一个 TS 的有效载荷数据组成(向 PAT 表一般都是这样)
表是组成 SI 信息的一种数据结构。在 TS 中有很多不同节目的数据包,解码器如何确定 哪个数据包属于某个节目?其答案就是在 TS 中的 PSI 和 SI 信息里, 这些信息精确地指引出 获得某节目与该节目数据包的 PID 之间的关系。
由 MPEG-2 定义的 TS 里面,数据包携带了两类信息:一是音、视频等素材的数据,二是 PSI 表。具有给定 PID 的数据包的有序排列就形成了 TS 流。PSI 表里的承载的内容主要是 TS(本节目流)的描述参数。由 MPEG-2 定义的 PSI 主要包含有三个表:PAT、PMT、CAT。 每个表都可作为一个或多个 TS 包的净荷插入 TS 中传送。
一个 TS 数据包的净荷为 188 个字节,当一个 PSI/SI 表的字节长度大于 184 字节时,就要 对这个表进行分割,形成段(section)来传送。分段机制主要是将一个数据表分割成多个数 据段。在 PSI/SI 表到 TS 包的转换过程中,段起到了中介的作用。由于一个数据包只有 188 字节,而段的长度是可变的,EIT 表的段限长 4096 字节,其余 PSI/SI 表的段限长为 1024 字 节。因此,一个段要分成几部分插入到 TS 包的净荷中。
PSI/SI 表的构成是:一个表由一个或多个子表构成,表用 table_id 来标识;不同的子表由 table_id 和 table_id_extension 来区分(具有相同的 table_id 和不同的 table_id_extension) ;一 个子表由一个或多个段构成 (具有相同的 table_id 和 table_id_extension, 不同 section_number 来区分) ;每个段由多个 TS数据包的数据组成,是 TS 数据包的数据,去掉了各个 TS 包的 包头后的有效数据组成,然后会形成对应的表格式,然后我们可以通过 filter 过滤出来的 section 数据参考表格式对 section 数据来解析。比如 PAT 的 section 表格式如下:
每个段具有一个完整的数据结构,表的重要参数----描述符在段里传送。图 3 所示是 SDT 表 的结构。
子表大于 1024 时,可把子表分割成两个或更多个段,并通过 section_number 来区分,如图 3-1 所示。
不同的信息表在 TS 中通过 PID 来区分,具有相同 PID 的不同表由 table_id(table_id 是表 标识) 来区分, 属于同一个 table_id 的不同子表由 table_id_extension、 版本号(version_number) 进行区分, 属于同一个子表的不同段由 section_number 区分。 表的扩展标识符有: network_id、 oringinal_network_id、boquet_id、 tansport_stream_id、service_id 等。
对于 NIT 表的子表具有相同的 table_id、network_id 和 version_number。
对于 BAT 表的子表具有相同的 table_id、bouquet_id 和 version_number。
对于 SDT 表的子表具有相同的 table_id、oringinal_network_id、tansport_stream _id 和 version_number。
对于 EIT 表的子表具有相同的 table_id、oringinal_network_id、tansport_stream _id、 service_id 和 version_number。
PAT的定义:
Table_id:为8bit字段,该字段标识节目关联分段,对于PAT,置为0x00。
Section_syntax_indicator:1bit字段,对于PAT,置为0x01。
Reserved:2bit保留字段,用于将来扩展,置为11。
Section_length:12bit字段,指示当前section的长度,计数值从分段长度下一个字节开始,包括CRC校验的4个字节,开头两位置为00,因此其大小不超过1021。
Transport_stream_id:16bit字段,当前TS流的ID,与网络中其他TS流相区别,由运营商指定。
Reserved:2bit保留字段,用于将来扩展,置为11。
Version_number:5bit字段,指出PAT表的版本号,一旦PAT表有变化,其版本号增1,当增至31时,恢复至0。
Current_next_indicator:1bit,置为1时,表示传送的PAT当前有效,置为0表示PAT下一次有效。
Section_number:8bit字段,表示section的数目,从0x00开始。
Last_section_number:8bit字段,指出最后一个section号,即PAT表section的最大数目。
Program_number:16bit字段,指出了节目对于哪一个PMT PID是可用的,当为0x00时,后面的PID对应于NIT。
Reserved:3bit保留字段,用于将来扩展,置为111。
Network_id:13bit字段,NIT PID。
Program_map_PID:13bit字段,对应于program_number所指定的节目的program_map_section的PID,从上面可看出:一个program用4字节来表示(包括16bit的program_number与13bit的PID)。
CRC:用来证实数据正确性的循环冗余校验码。
(section_number和 last_section_number的功能是当PAT内容>184字节时,PAT表会分成多个段(sections),解复用程序必须在全部接收完成后再进行PAT的分析)
PMT定义如下:
各字段含义如下:
table_id:8bits的ID,应该是0x02
section_syntax_indicator:1bit的段语法标志,应该是''1'' ''0'':固定是''0'',如果不是说明数据有错.
reserved:2bits保留位,应该是''00''
section_length:16bits段长度,从program_number开始,到CRC_32(包含)的字节总数.
program_number:16bits的频道号码,表示当前的PMT关联到的频道.换句话就是说,当前描述的是program_number频道的信息.
reserved:2bits保留位,应该是''00''
version_number:版本号码,如果PMT内容有更新,则version_number会递增1通知解复用程序需要重新接收节目信息,否则version_number是固定不变的.
current_next_indicator:当前未来标志符,一般是0
section_number:当前段号码
last_section_number:最后段号码,含义和PAT中的对应字段相同,请参考PAT部分.
reserved:3bits保留位,一般是''000''.
PCR_PID:13bits的PCR PID,具体请参考ISO13818-1,解复用程序不使用该参数.
reserved:4bits保留位,一般是''0000''
program_info_length:节目信息长度(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes) 紧接着就是频道内部包含的节目类型和对应的PID号码了.
stream_type:8bits流类型,标志是Video还是Audio还是其他数据.
reserved:3 bits保留位.
elementary_PID:13bits对应的数据PID号码(如果stream_type是Video,那么这个PID就是Video PID,如果stream_type标志是Audio,那么这个PID就是Audio PID)
reserved:4 bits保留位.
ES_info_length:和program_info_length类似的信息长度(其后是N2个描述符号)
CRC_32:32bits段末尾是本段的CRC校验值
SDT的定义
DVB系统提出了一个SDT表格,该表格标志一个节目的名称,并且能和 PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了. SDT, Service description section,服务描述段 SDT可以提供的信息包括: (1) 该节目是否在播放中 (2) 该节目是否被加密 (3) 该节目的名称
SDT定义如下: 各字段定义如下:
table_id:8bits的ID,可以是0x42,表示描述的是当前流的信息,也可以是0x46,表示是其他流的信息(EPG使用此参数)
section_syntax_indicator:段语法标志,一般是''1''
reserved_future_used:2bits保留未来使用
reserved:1bit保留位,防止控制字冲突,一般是''0'',也有可能是''1''
section_length:12bits的段长度,单位是Bytes,从transport_stream_id开始,到CRC_32结束(包含)
transport_stream_id:16bits当前描述的流ID
reserved:2bits保留位
version_number:5bits的版本号码,如果数据更新则此字段递增1
current_next_indicator:当前未来标志,一般是''0'',表示当前马上使用.
original_netword_id:16bits的原始网络ID号
reserved_future_use:8bits保留未来使用位
接下来是N个节目信息的循环:
service_id:16 bits的服务器ID,实际上就是PMT段中的program_number.
reserved_future_used:6bits保留未来使用位
EIT_schedule_flag:1bit的EIT信息,1表示当前流实现了该节目的EIT传送
EIT_present_following_flag:1bits的EIT信息,1表示当前流实现了该节目的EIT传送
running_status:3bits的运行状态信息:1-还未播放 2-几分钟后马上开始,3-被暂停播出,4-正在播放,其他---保留
free_CA_mode:1bits的加密信息,''1''表示该节目被加密. 紧 接着的是描述符,一般是Service descriptor,分析此描述符可以获取servive_id指定的节目的节目名称.具体格式请参考 EN300468中的Service descriptor部分.