H264解码之PS流解析

PS头封装格式

PS流是对PES的进一步封装,是将具有共同时间基准的一个或多个PES包组合而成的单一的数据流;其基本单位是PS包,PS流由很多个PS包组成,PS包主要由固定包头,系统头,和PES包组成,其具体组成如下图所示:
图一
下图更为清晰:
ps
PS流有一个结束码MPEG_program_end_code:占位32bit,其值为0x000001B9,PS流总是以0x000001BA开始,以0x000001B9结束,对于一个PS文件,有且只有一个结束码0x000001B9,不过对于网传的PS流,则应该是没有结束码的;
具体PS包头字段组成顺序如下:
包头

  • pack_start_code字段:起始码,占位32bit,标识PS包的开始,固定为0x000001BA
  • ‘01’字段:占位2bit;
  • SCR字段:占位46bit,其中包含42bit的SCR值和4个bit的marker_bit值;其中SCR值由system_clock_reference_basesystem_clock_reference_extension两部分组成;字节顺序依次是:
  • system_clock_reference_base [32..30]:占位3bit;
  • marker_bit:占位1bit;
  • system_clock_reference_base [29..15]:占位15bit;
  • marker_bit:占位1bit;
  • system_clock_reference_base [14..0]:占位15bit;
  • marker_bit:占位1bit;
  • system_clock_reference_extension:占位9bit;
  • marker_bit:占位1bit;
  • program_mux_rate字段:速率值字段,占位22bit,正整数,表示P-STD接收此字段所在包的PS流的速率;这个值以每秒50字节作为单位;禁止0值;
  • Marker_bit:标记字段,占位1bit,固定为’1’;
  • Marker_bit:标记字段,占位1bit,固定为’1’;
  • Reserved字段:保留字段,占位5bit;
  • pack_stuffing_length字段:长度字段,占位3bit;规定了此字段之后填充字段的长度;
  • stuffing_byte:填充字段,固定为0xFF;长度由前一字段确定;
  • system_header字段:系统头部字段,只有当下一个字段的值为系统头部起始码0x000001BB时才存在,长度不定,其组成字段顺序如下所示:
    header
  • system_header_start_code字段:系统头部起始码,占位32bit,值固定为0x000001BB,标志系统首部的开始;
  • header_length字段:头部长度字段,占位16bit,表示此字段之后的系统首部字节长度;
  • Marker_bit字段:占位1bit,固定值为1;
  • rate_bound字段:整数值,占位22bit,为一个大于或等于PS流所有PS包中的最大program_mux_rate值的整数;可以被解码器用来判断是否可以对整个流进行解码;
  • Marker_bit字段:占位1bit,固定值为1;
  • audio_bound字段:占位6bit;取值范围0到32间整数;大于或等于同时进行解码处理的PS流中的音频流的最大数目;
  • fixed_flag字段:标志位,占位1bit;置位1表示固定比特率操作,置位0则为可变比特率操作;
  • CSPS_flag字段:CSPS标志位,占位1bit;置位1表示此PS流满足标准的限制;
  • system_audio_lock_flag字段:标志位,占位1bit,表示音频采样率和STD的system_clock_frequency之间有一特定常数比例关系;
  • system_video_lock_flag字段:标志位,占位1bit,表示在系统目标解码器system_clock_frequency和视频帧速率之间存在一特定常数比例关系;
  • Marker_bit字段:占位1bit,固定值为1;
  • video_bound字段:整数,占位5bit,取值范围0到16;大于或等于同时进行解码处理的PS流中的视频流的最大数目;
  • packet_rate_restriction_flag字段:分组速率限制标志字段,占位1bit,若CSPS_flag == 1,则此字段表示哪种限制适用于分组速率;若CSPS_flag == 0,则此字段无意义;
  • reserved_bits字段:保留字段,占位7bit,固定为’1111111’;
  • LOOP:当下一个bit为1时进入
    • stream_id字段:占位8bit,表示其后的P-STD_buffer_bound_scaleP-STD_buffer_size_bound字段所涉及的流的编码和基本流的号码;若stream_id ==’1011 1000’,则其后的P-STD_buffer_bound_scaleP-STD_buffer_size_bound字段对应PS流中的所有音频流;若stream_id ==’1011 1001’,则其后的P-STD_buffer_bound_scaleP-STD_buffer_size_bound字段对应PS流中的所有视频流;若取其他值,则应大于’1011 1100’,且按照标准对应Stream id(详见附录1);PS流中的每个原始流都应在每个系统首部中通过这种机制精确地规定一次它的P-STD_buffer_bound_scaleP-STD_buffer_size_bound
    • ‘11’字段:占位2bit;
    • P-STD_buffer_bound_scale字段:占位1bit,表示用来解释后面P-STD_buffer_size_bound字段的比例因子;如果之前的stream_id表示音频流,则此值应为0,若之前的stream_id表示视频流,则此值应为1,对于其他stream类型,此值可以0或1;
      + P-STD_buffer_size_bound字段:占位13bit,无符号整数;大于或等于所有PS流分组的P-STD输入缓冲区大小BSn的最大值;若P-STD_buffer_bound_scale == 0,则P-STD_buffer_size_bound以128字节为单位;若P-STD_buffer_bound_scale == 1,则P-STD_buffer_size_bound以1024字节为单位;
  • LOOP End
    目前的系统头部好像是没有用到的,所以对于系统头部的解析,我们一般只要先首先判断是否存在系统头(根据系统头的起始码0x000001BB),然后我们读取系统头的头部长度,即header_length部分,然后根据系统头部的长度,跳过PS系统头,进入下一个部分,即PS的payload,PES包;在固定包头和系统头之后,就是PS包的payload,即PES包;若PSM存在,则第一个PES包即为PSM。

PSM格式介绍

PSM提供了对PS流中的原始流和他们之间的相互关系的描述信息;PSM是作为一个PES分组出现,当stream_id == 0xBC时,说明此PES包是一个PSM;PSM是紧跟在系统头部后面的;PSM是作为PS包的payload存在的;
PSM由很多字段组成,其字节顺序如下所示:
psm

  • Packet start code prefix字段:包头起始码,固定为0x000001,占位24bit;与后面的字段map_stream_id一起组成分组开始码,标志着分组的开始;
  • map_stream_id字段:类型字段,标志此分组是什么类型,占位8bit;如果此值为0xBC,则说明此PES包为PSM;
  • program_stream_map_length字段:长度字段,占位16bit;表示此字段之后PSM的总长度,最大值为1018(0x3FA);
  • current_next_indicator字段:标识符,占位1bit;置位1表示当前PSM是可用的,置位0则表示当前PSM不可以,下一个可用;
  • Reserved:保留字段,占位2bit;
  • program_stream_map_version字段:版本字段,占位5bit;表示PSM的版本号,取值范围1到32,随着PSM定义的改变循环累加;若current_next_indicator == 1,表示当前PSM的版本号,若current_next_indicator == 0,表示下一个PSM的版本号;
  • Reserved:保留字段,占位7bit;
  • marker_bit:标记字段,占位1bit,固定为1;
  • program_stream_info_length字段:长度字段,占位16bit;表示此字段后面的descriptor字段的长度;
  • Descriptor字段:program Stream信息描述字段,长度由前个字段确定;
  • elementary_stream_map_length字段:长度字段,占位16bit;表示在这个PSM中所有ES流信息的总长度;包括stream_type, elementary_stream_id, elementary_stream_info_length的长度,即N*32bit;是不包括具体ES流描述信息descriptor的长度的;
  • LOOP
    • stream_type字段:类型字段,占位8bit;表示原始流ES的类型;这个类型只能标志包含在PES包中的ES流类型;值0x05是被禁止的;常见取值类型有MPEG-4 视频流:0x10;H.264 视频流:0x1B;G.711 音频流:0x90;因为PSM只有在关键帧打包的时候,才会存在,所以如果要判断PS打包的流编码类型,就根据这个字段来判断;
    • elementary_stream_id字段:流ID字段,占位8bit;表示此ES流所在PES分组包头中的stream_id字段的值;其中0x(C0DF)指音频,0x(E0EF)为视频;
    • elementary_stream_info_length字段:长度字段,占位16bit;表示此字段之后的,ES流描述信息的长度;
    • Descriptor:描述信息,长度由前个字段确定;表示此类型的ES流的描述信息,这个描述信息的长度是不包含在elementary_stream_map_length字段里面的;
  • LOOP End
  • CRC_32:CRC字段,占位32bit,CRC校验值;
    如果要解析原始流编码类型,则需要解析PSM,我们需要找到0x000001BC的位串,然后根据PSM的结构进行解析;如果不需要解析,则在找到PSM的开始码之后,找到此字段的长度字段,跳过此长度即可,直接解析后面的PES包;
    PS总结:
    解析PS包,要先找到PS包的的起始码0x000001BA位串,然后解析出系统头部字段,之后进入PS包的负载,判断是否有PSM,根据PSM确定payload的PES包中所负载的ES流类型;然后再根据ES流类型和ID从PES包中解析出具体的ES流;解包过程则相反;若要从PS流中找出来帧类型,必须将PS包解析成ES并组成完整的帧,然后在帧数据开始根据NAL头来进行帧的类型的判断;

PSM只有在关键帧打包的时候,才会存在;IDR包含了SPS,PPS和I帧;每个IDR NALU前一般都会包含SPS、PPS等NALU,因此将SPS、PPS、IDR的NALU 封装为一个PS 包,包括PS头,PS system header,PSM,PES;所以一个IDR NALU PS 包由外到内顺序是:PS header| PS system header | PSM| PES。对于其它非关键帧的PS包,就简单多了,直接加上PS头和PES 头就可以了。顺序为:PS header | PES header | h264raw data。以上是对只有视频video 的情况,如果要把音频Audio也打包进PS 封装,只需将数据加上PES header 放到视频PES 后就可以了。顺序如下:PS 包=PS头|PES(video)|PES(audio);
附录1:stream_id类型定义

stream_id Note stream coding
1011 1100 1 program_stream_map
1011 1101 2 private_stream_1
1011 1110 padding_stream
1011 1111 3 private_stream_2
110x xxxx ISO/IEC 13818-3 or ISO/IEC 11172-3 audio
1110 xxxx ITU-T Rec. H.262†
1111 0000 3 ECM_stream
1111 0001 3 EMM_stream
1111 0010 5 ITU-T Rec. H.222.0†
1111 0011 2 ISO/IEC_13522_stream
1111 0100 6 ITU-T Rec. H.222.1 type A
1111 0101 6 ITU-T Rec. H.222.1 type B
1111 0110 6 ITU-T Rec. H.222.1 type C
1111 0111 6 ITU-T Rec. H.222.1 type D
1111 1000 6 ITU-T Rec. H.222.1 type E
1111 1001 7 ancillary_stream
1111 1010 Ö 1111 1110 reserved data stream
1111 1111 4 program_stream_directory
发布了135 篇原创文章 · 获赞 67 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/y601500359/article/details/97649112