TS流解析
当调谐器选定频点之后,此时本地的接收器能够源源不断地接收到TS码流,在这些码流中,包含了视频信息、音频信息以及各种辅助信息,我们所要做的就是从这些信息中找到所承载的字幕信息,并进行解码。
TS流的复用与解复用:
在数字电视系统的前端,所有的数据(视频,音频以及PSI/SI信息)都会被复用器复用成TS流,从而进行传送,示意图如下:
机顶盒等接收设备接收到的就是这样被复用的一个个包,需要解复用器进行解复用。解复用的意义就在于可以将TS流中类型相同的包存入相同的缓存中,分别处理,这样就可以将视频、音频以及其他业务信息的数据区分隔开来。
TS流拼接成PES包
在接收器接收到TS流之后,首先需要等待数据的同步,即完整的数据包的到来。首先需要查找PID为0x0的包,这是PAT(节目映射表),每个TS流中有一个,每隔0.5s重复。在PAT表中,定义了当下TS流中的所有节目,是PSI的根节点,要查找节目必须从PAT表开始查找,其主要包含频道号码和每一个频道对应的PMT的PID。当选定节目后,按照上述PID找到对应的PMT。
PMT是节目映射表,其中包含了当前频道中所有的video数据的PID,所有的audio数据的PID,以及和当前频道关联在一起的其他数据的PID。从PMT中,我们可以找到字幕数据流所对应的PID,可以根据此PID拼接成一个完整的PES包。
TS包由包头跟有效载荷组成,其中包头4个字节,有效载荷184个字节,一共188个字节,其结构如下所示:
如前文所述,根据PMT中给定的PID找到字幕数据对应的TS包。其中,同步字节是固定的8bit字段,其值为0x47;有效载荷单元起始指示符为1bit标志,当传输流包有效载荷包含PES包数据时,置1指示此传输流包的有效载荷应随着PES包的首字节开始,0指示此传输流包中无任何PES包开始,通过此标志,我们可以判断一个PES包是否已经存储完整;自适应段为2bit字段,指示此包头是否跟随自适应字段或有效载荷,当为01时指示无自适应段,仅有效载荷,为10时仅有自适应段,无有效载荷,11时为自适应段后跟随有效载荷。如果存在自适应段,则连续计数器字段后8个bit指示自适应段长度。如果没有自适应段只有有效载荷,则包头之后的184个字节就是有效载荷,存入缓存中;如果有自适应段,则使用184减去自适应段长度即可得到有效载荷的长度,将其存入缓存中。直到下一个PES包头到达,说明此PES包已完整。
PES包解析
字幕段数据的获取
PES包的完整结构见下图:
包起始码前缀是固定的24比特码,值为0x1;流id指示基本流的类型和编号,对字幕流来说,流id应为0xbd;PES包长度是一个16bit的字段,指示PES包中跟随该字段最后字节的字节数,0值指示PES包长度既未指定,也未限定并且仅在这样的PES包中才被允许;PES头数据长度指示在此PES包头中包含的由任选字段和任意填充字节所占据的字节总数;PTS指示了显示时间与解码时间的关系。在获取PES包长度和PES头数据长度后即可得到PES数据域中的数据,即字幕的相关数据。
字幕段数据的解析
上述得到的字幕数据段结构如下所示:
对字幕数据来说,data_identifier为固定的8bit字段,为0x20;subtitle_stream_id是在PES包中指示字幕流,固定为0x00,之后就是字幕的数据段;最后是8bit的PES数据结束的标志位,固定为0xff。字幕数据段的结构如下:
开始是8bit的同步字节,固定为0x0f,通过此同步字节可用来验证传输信息有没有丢失;之后是8bit的segment_type,代表传输的字幕数据的类型;随后是2个字节的page_id,用来指示字幕服务的类型,通过page_id可用来区分此字幕页是composition page还是ancillary page。Ancillary page代表由多个字幕服务共享的数据,例如logo或者字符符号。segment_length是2个字节的字段,指示该字段后直到字幕段结束的字节的数量,之后就是字幕数据段的数据。
字幕数据段较为常用的是四种类型:页组成段、区域组成段、CLUT定义段以及对象数据段。页组成段携带一个包含0个或者多个region的清单,这个清单定义了页组成段定义的显示中可见的region的集合,这个可见清单在PTS定义的时间变得可用,解码器的显示将立即从之前存在的可见区域的集合切换到最新定义的集合,在页组成段之后可能会跟着0或多个区域组成段,但页组成段的region清单可能跟这些区域组成段的集合有很大的不同。一个完整的区域组成段集合将随着标志有“modechange”或“acquisition point”状态的页组成段出现,这是引入region并为其分配内存的过程,区域组成段携带有关region位置和区域属性的信息,例如水平和垂直分辨率、背景色、区域的像素深度、CLUT的id、包含对象的列表以及对象在区域中的位置。CLUT定义段代表将要有CLUT的更改,也就是颜色的改变。对象数据段包含了一个对象的相关数据,即字幕对应的字符或者像素编码。
页组成段结构如上图所示,部分解释如下:
- page_time_out:以s为单位的时间,在该时间后该页面不再有效,应当从屏幕上擦除此页面,这是为了防止因为错误导致该页面长期停留在屏幕上的情况发生;
- page_state:该字段表示页组合分段中和subtitle页描述相关联的内存规划的状态,相关值的定义如下:
normal case代表页组合分段后跟着不完整的region集合,将要更改其中的字幕元素,acquisition point表示页组合分段后跟着完整的region集合,描述当前的内存规划,即将更新页面, mode change表示页组合分段后跟着的regions,描述一个新的内存规划,需要更新页面。 - region_id:页中唯一标识一个区域的元素,所有的region都应该按照region_vertical_address字段中的顺序排列在页组合分段中。
- region_horizontal_address:指定了region左上角像素的水平地址。
- region_vertical_address:指定了region顶行的垂直地址。
区域组成段结构如上图所示,部分解释如下:
- region_id:唯一标识了一个region。
- region_fill_flag:如果设置为1,代表该区域将使用此段中region_n-bit_pixel_code字段中定义的背景色填充。
- region_width:表示region的宽度。
- region_height:表示region的高度。
- region_level_of_compatibility:表示解码器解码该region所需的CLUT的最小类型。
- region_depth:该region可以使用的最大像素深度。
- CLUT_id:标识CLUT的唯一标识。
- region_8-bit_pixel_code:当region_fill_flag被设置后,该region使用的256色的像素编码。
- region_4-bit_pixel_code:当region_fill_flag被设置后,该region使用的16色的像素编码。
- region_2-bit_pixel_code:当region_fill_flag被设置后,该region使用的4色的像素编码。
- object_id:标识区域中显示的一个对象。
- object_type:标识对象的类型,设置为0x00代表位图,0x01代表字符,0x02代表字符串,0x03保留。
- object_provider_flag:2bit的标识,标识object的来源。
- object_horizontal_position:指定对象的水平位置,单位为像素,相对于区域的左边缘。
- object_vertical_position:指定对象的垂直位置,单位为扫描线,相对于区域的顶部。
- foreground_pixel_code:标识定义字符的前景色的8_bit_pixel_code。
- background_pixel_code:标识定义字符的背景色的8_bit_pixel_code。
CLUT定义段的结构如上所示,部分解释如下:
- CLUT_id:标识页中包含在CLUT定义段域中的CLUT族。
- CLUT_entry_id:指定CLUT的条目号,CLTU的条目号从0开始。
- 2-bit/entry_CLUT_flag:如果设置为1,代表该CLUT值将被用于标识2-bit/entry CLUT的条目。
- 4-bit/entry_CLUT_flag:如果设置为1,代表该CLUT值将被用于标识4-bit/entry CLUT的条目。
- 8-bit/entry_CLUT_flag:如果设置为1,代表该CLUT值将被用于标识8-bit/entry CLUT的条目。
- full_range_flag:如果设置为1,标识Y-value、Cr-value、Cb-value和T-value字段为8bit方案,如果设置为0,这些字段仅包含最重要的比特位。
- Y-value:代表该条目中CLUT的Y输出值。
- Cr-value:代表该条目中CLUT的Cr输出值。
- Cb-value:代表该条目中的CLUT的Cb输出值。
- T-value:代表该条目中的透明度输出值。
对象数据段的结构如上图,部分解释如下:
- object_id:标识object_data_segment中的object数据。
- object_coding_method:标识用于object编码的方法,0x00代表像素编码,0x01代表字符串编码,0x02保留,0x03保留。
- non_modifying_colour_flag:设置为1代表CLUT条目值‘1’是一个非修改的颜色,意味着其不应该覆盖任何底层的object。
- top_field_data_block_length:表示随后顶部区域包含的pixel-data_sub-block的字节数。
- bottom_field_data_block_length:表示随后底部区域包含的pixel-data_sub-block的字节数。
- number_of_codes:表示字符串中编码字符的数量。