PS和TS封装码流数据格式分析-PS部分
问题背景:
前面分析了TS封装格式的码流,从实际应用上讲,TS这种封装格式文件应用的场合比较多,机顶盒,苹果家族产品,游戏直播等领域现在都用。最新的HLS低延迟规范也进行优化了协议,降低了HLS延时,所以还有比较好的前景和生命周期。
PS文件在当时的DVD时代应用比较多,后面又应用在安防上,所以和自己工作比较相关也需要研究和分析下。
前面研究TS文件封装格式时,基本已经研究了这部分内容。因为PS和TS除了描述的其它数据承载内容不一样,PS主要是PS头,系统头,映射节目头等。而TS主要是PAT、PMT表。这些具有明显差异,一旦到承载的载荷数据是音视频数据时,两者都是PES数据位对象的
其中PES头里面可以定义数据的长度,这样为一个PS包分片成RTP数据也打下基础,这也是TS数据和PS数据能够相互转换比较容易的原因,二者在PES层数据是统一的。
具体分析:
PS数据封装:
1.IDR关键帧封装:
每个IDR NALU 前一般都会包含SPS、PPS等NALU,因此将SPS、PPS、IDR 的NALU 封装为一个PS 包,包括ps 头,然后加上PSsystem header,PS system map,PESheader+h264 raw data。
所以一个IDR NALU PS 包由外到内顺序是:
PSheader|PS system header | PS system Map | PES header | h264 raw data。
2.非关键帧封装:
PS包从外带内的顺序是:
PSheader|PES header | h264 raw data。
3.含有音频的PS封装:
PS头|PES(video)|PES(audio)
PSheader|PES header | h264 raw data|PES header|AAC raw data
下面用ElecardStream Analyzer工具分析结果如下:
如果你想直接看PS解析和封装的结论,直接看文末总结部分即可,中间主要是给了一个实例,一个个bit位和对应字段分析的过程,可以不关注。
各个头字段分析:
1.PS头数据分析 PS header:
PS的头数据字段含义:
实例分析:
原始待分析数据:00 00 01 BA 44 00 04 00 0401 00 03 FF F8
1. Pack_start_code ps包开始标志位:
十六进制:00 00 01 BA
标识一个PS包的开始;
2. System_clock_reference系统时钟参考字段和扩展字段(六字节):系统时钟参考(SCR)分两部分编码的42位字段。第一部分system_clock_reference_base是一个长度为33位的字段;第二部分system_clock_reference_extenstion是一个长度为9位的字段。SCR字段指出了基本流中包含ESCR_base最后一位的字节到达节目目标解码器输入端的期望时间。
十六进制:44 00 04 00 04 01
二进制:01-00 0-1-00 0000 00000000 0-1-00 0000 0000 0000 0-1-00 0000 000-1
即可以看到参考时钟还是参考时钟的扩展都是0;
3. program_mux_rate字段节目符合速率字段(三字节):一个22位整数,规定P-STD在包含该字段的包期间接收节目流的速率。其值以50字节/秒为单位。不允许取0值。该字段值在本标准中的节目多路复合流的不同包中取值可能不同。
十六机制:00 03 FF
二进制:0000 00000000 0011 1111 1111
十进制:255 那么说明速率大小为255*50=12750Byte/s
4. pack_stuffing_length填充字段长度:
十六机制:F8
二进制:1111 1000
说明是0则表示无填充字节。
总结:
1. ps头无论是什么样的包都是要有的,00 00 01 BA是关键,其它字段基本可以忽略不计;
2. 无扩展字段是直接读十四个字节跳过解析即可,但是当pack_stuffing_length不为0时,需要解析出来,加上该长度跳过。该字段位于第十四字节的后面三bit位;
3. 也可以根据该值进行打包和封装,基本对应bit位都是用默认值;
2.PS系统头即节目流系统标题 PS System header:
PS系统头各个字段含义:
语 法 | 位数 | 助记符 |
|---|---|---|
system_header() { | ||
system_header_start_code | 32 | bslbf |
header_length | 16 | uimsbf |
marker_bit | 1 | bslbf |
rate_bound | 22 | uimsbf |
marker_bit | 1 | bslbf |
audio_bound | 6 | uimsbf |
fixed_flag | 1 | bslbf |
CSPS_flag | 1 | bslbf |
system_audio_lock_flag | 1 | bslbf |
system_video_lock_flag | 1 | bslbf |
marker_bit | 1 | bslbf |
vedio_bound | 5 | uimsbf |
packet_rate_restriction_flag | 1 | bslbf |
reserved_bits | 7 | bslbf |
while (nextbits()=='1') { | ||
stream_id | 8 | uimsbf |
'11' | 2 | bslbf |
P-STD_buffer_bound_scale | 1 | bslbf |
P-STD_buffer_size_bound | 13 | uimsbf |
} | ||
} |
实例分析:
原始待分析数据:00 00 01 BB 00 09 81 86 A105 E1 7F E0 E8 00
1. System_header_start_code系统头(四字节):指出系统标题的开始
十六机制:00 00 01 BB
2. header_length标题长度字段(二字节) :16位字段。指出该字段后的系统标题的字节长度。在本规范将来的扩充中可能扩展该字段。
十六进制:00 09 说明后面还有9字节,大部分系统头则表示有十五字节,其实解析到这里我们就可以直接跳过4+2+9字节继续解析后面的包,因为系统头里面的所有字节含义我们都不关注;
3. Rate_bound 速率界限字段(三字节):22位字段,取值不小于编码在节目流的任何包中的program_mux_rate字段的最大值。该字段可被解码器用于估计是否有能力对整个流解码。
十六机制:81 86 A1
二进制:1000 0001 1000 0110 1010 0001
4. Audio_bound 音频界限字段(1字节前6bit位):6位字段,取值是在从0到32的闭区间中的整数;
十六机制:05
二进制:0000 0101
十进制:1
5. Fixed_flag固定标记字段:1位标志位。置'1'时表示比特率恒定的操作;置'0'时,表示操作的比特率可变。
十六机制:05
二进制:000 0101
6. CSPS_flag CSPS标志字段:置'1'时,节目流符合2.7.9中定义的限制。一般就是默认为1;
十六机制:05
二进制:000 0101
7. System_aduio_lock_flag系统音频锁定标记字段:1位字段。表示在系统目标解码器的音频采样率和system_clock_frequency之间存在规定的比率。一般默认1;
十六机制:E1
二进制:1110 0001
十进制:1
8. System_video_lock_flag系统视频锁定标志字段:1位字段。表示在系统目标解码器的视频帧速率和system_clock_frequency之间存在规定的比率。一般默认1;
十六机制:E1
二进制:1110 0001
十进制:1
9. Video_bound视频界限字段:5位字段,取值是在从0到16的闭区间中的整数且不小于节目流中解码过程同时活动的GB/T XXXX.2和GB/T AAAA.2流的最大数目。一般也是默认为1;
十六机制:E1
二进制:1110 0001
十进制:1
10. Packet_rate_restriction_flag:分组速率限制标志字段:1位标志位。若CSPS标识为'1',则该字段表示2.7.9中规定的哪个限制适用于分组速率。若CSPS标识为'0',则该字段的含义未定义。
十六机制:7F
二进制:0111 1111
十进制:0
11. Reserced_bit保留位字段:7位字段。被保留供ISO/IEC将来使用。它的值应为'111 1111',除非ISO/IEC对它作出其它规定。
十六机制:7F
二进制:0111 1111
十进制:0
12. Stream_id流标识字段:8位字段。指示其后的P-STD_buffer_bound_scale和P-STD_buffer_size_bound字段所涉及的流的编码和基本流号码。节目流中的每个基本流应在每个系统标题中通过这种机制精确地规定一次它的P-STD_buffer_bound_scale和P-STD_buffer_size_bound。其实就是看到E0就是视频流;
十六机制:E0
二进制:1110 1111
十进制:224
13. P-STD_buffer_bound_scale P-STD缓存区界限比例字段:1位字段。表示用于解释后续P-STD_buffer_size_bound字段的比例系数。若前面的stream_id表示一个音频流,则该字段值为'0'。若表示一个视频流,则该字段值为'1'。对于所有其它的流类型,该字段值可以为'0'也可以为'1'。
十六机制:E8
二进制:1110 1000
十进制:1
14. P-STD_buffer_size_bound P-STD缓冲区大小界限字段:
十六机制:E8 00
二进制:1110 1000 0000 0000
十进制:17592186044416
我们也用专门的分析工具进行了分析,得到的结论和二进制对起来的各个字段取值是一致的。
总结:
所以对于系统头部的解析,我们一般只要先首先判断是否存在系统头(根据系统头的起始码0x000001BB),然后我们读取系统头的头部长度,即PS SYSTEM HEADER LENGTH部分,然后根据头部的长度,跳过PS系统头。进入下一个部分,即PS 节目流映射头。
3.PS流节目映射流部分即节目流映射 PS MAP Header:
PS节目映射头各个字段含义:
语 法 | 位数 | 助记符 |
|---|---|---|
program_stream_map() { | ||
packet_start_code_prefix | 24 | bslbf |
map_stream_id | 8 | uimsbf |
program_stream_map_length | 16 | uimsbf |
current_next_indicator | 1 | bslbf |
reserved | 2 | bslbf |
program_stream_map_version | 5 | uimsbf |
reserved | 7 | bslbf |
marker_bit | 1 | bslbf |
program_stream_info_length | 16 | uimsbf |
for (i=0;i<N;i++){ | ||
descriptor() | ||
} | ||
elementary_stream_map_length | 16 | uimsbf |
for (i=0;i<N1;i++){ | ||
stream_type | 8 | uimsbf |
elementary_stream_id | 8 | uimsbf |
elementary_stream_info_length | 16 | uimsbf |
for (i=0;i<N2;i++) { | ||
descriptor() | ||
} | ||
} | ||
CRC_32 | 32 | rpchof |
} |
实例分析:
待分析数据:00 00 01 BC 00 0E E0 FF 0000 00 04 1B E0 00 00 43 80 9B 58
1. Packet_start_code_prefix 分组起始前缀字段:24位码。它和跟随其后的map_stream_id共同组成一个分组起始码以标志分组的开始。该字段是值为'00000000 0000 0000 0000 0001' (0x000001)的位串。
十六机制:00 00 01
2. Map_stream_id 映射流标识字段:8位字段,值为0xBC
十六机制:BC
3. Program_stream_map_length 节目流映射长度字段:16位字段。指示紧跟在该字段后的program_stream_map中的字节数。该字段的最大值为1018(0x3FA)
十六机制:00 0E
十进制:15 表示后面还有14字节,除了自己。
4. Current_next_indicator 当前下一个指示符字段:1位字段。置'1'时表示传送的节目流映射当前是可用的。置'0'时表示传送的节目流映射还不可用,但它将是下一个生效的表。一般默认值为1;
十六机制:E0
二进制:1110 1111
十进制:1
5. Program_stream_map_version 节目流映射版本字段:5位字段,表示整个节目流映射的版本号。一旦节目流映射的定义发生变化,该字段将递增1,并对32取模。在current_next_indicator为'1'时,该字段应该是当前适用的节目流映射的版本号;在current_next_indicator为'0'时,该字段应该是下一个适用的节目流映射的版本号。
十六机制:E0
二进制:1110 1111
十进制:16
6. Marker_bit标记位字段:1位字段,取值为'1'。
十六机制:FF
二进制:1111 1111
十进制:1
7. Program_stream_info_length节目流信息长度字段:16位字段,指出紧跟在该字段后的描述符的总长度。
十六机制:00 00
二进制:0000 0000 0000 0000
十进制:0
8. Elementary_stream_map_length基本映射流长度字段:16位字段,指出在该节目流映射中的所有基本流信息的字节长度。它只包括stream_type、elementary_stream_id和elementary_stream_info_length字段。(这里注意一下,这里的基本流映射长度,他只包括他后面的指定的那几个定义字段的总和,即从从这个长度,我们可以知道后面他根了几种类型的流定义,因为一种流的这个定义字段:stream_type(1BYTE)、elementary_stream_id(1byte)和elementary_stream_info_length(2byte)字段总和为4个字节,所以用elementary_stream_map_length/4可以得到后面定义了几个流类型信息。)
十六机制:00 04
二进制:0000 0000 0000 0100
十进制:4
这个值除以4则表示9、10、11这三个字段的循环次数。这里为4,说明后面只有一组,只需要循环一次。
9. Stream_type流类型字段:8位字段,根据表2-29规定了流的类型。该字段只能标志包含在PES分组中的基本流且取值不能为0x05。这里我们看到的是H264编码的视频和0x90的G711的音频。
(这里我们暂时根据国标GB28181中的定义可以知道
1、MPEG-4 视频流: 0x10;
2、H.264 视频流: 0x1B;
3、SVAC 视频流: 0x80;
4、G.711 音频流: 0x90;
5、G.722.1 音频流: 0x92;
6、G.723.1 音频流: 0x93;
7、G.729 音频流: 0x99;
8、SVAC音频流: 0x9B
十六机制:1B
二进制:0001 1011
10. 基本流标识字段 elementary_stream_id:8位字段,指出该基本流所在PES分组的PES分组标题中stream_id字段的值。(这个字段的定义,其中0x(C0~DF)指音频,0x(E0~EF)为视频)
十六机制:E0
二进制:1110 1111
十进制:224
11. Elementary_stream_info_length基本流信息长度字段:16位字段,指出紧跟在该字段后的描述符的字节长度。(即这个类型的流描述长度。这个后面的字段后面的指定长度不在elementary_stream_map_length指定的范围类。)
十六机制:00 00
二进制:0000 0000 0000 0000
12. CRC32校验字段:
32位字段,它包含CRC值以在处理完整个节目流映射后在附录A中定义的解码器寄存器产生0输出值。
十六机制:43 80 9B 58
总结
对于这个字段的解析,我们需要取值0x000001BC的位串,指出节目流映射的开始,暂时不需要处理,读取HeaderLength直接跳过即可,如果需要解析流编码类型,必须详细解析这个字段。
实际上我们需要在解析节目映射流头字段时,需要解析基本映射流长度字段,这里面包含这是否含有音视频以及音视频对应的编码格式。
============================================================================
4.PES包相关内容 PES Header
第一个PES+H264视频帧
第二个PES头+AAC音频帧
PES头各个字段含义:
语 法 | 位数 | 助记符 |
PES_packet(){ | ||
packet_start_code_prefix | 24 | bslbf |
stream_id | 8 | uimsbf |
PES_packet_length | 16 | uimsbf |
if(stream_id != program_stream_map | ||
&& stream_id !=padding_stream | ||
&& stream_id !=private_stream_2 | ||
&& stream_id !=ECM | ||
&& stream_id !=EMM | ||
&& stream_id !=program_stream_directory | ||
&& stream_id !=DSMCC_stream | ||
&& stream_id !=ITU-T Rec.H.222.1 type E stream){ | ||
'10' | 2 | bslbf |
PES_scrambling_control | 2 | bslbf |
PES_priority | 1 | bslbf |
data_alignment_indicator | 1 | bslbf |
copyright | 1 | bslbf |
original_or_copy | 1 | bslbf |
PTS_DTS_flags | 2 | bslbf |
ESCR_flag | 1 | bslbf |
ES_rate_flag | 1 | bslbf |
DSM_trick_mode_flag | 1 | bslbf |
additional_copy_info_flag | 1 | bslbf |
PES_CRC_flag | 1 | bslbf |
PES_extension_flag | 1 | bslbf |
PES_header_data_length | 8 | uimsbf |
if(PTS_DTS_flags =='10'){ | ||
'0010' | 4 | bslbf |
PTS[32..30] | 3 | bslbf |
marker_bit | 1 | bslbf |
PTS[29..15] | 15 | bslbf |
marker_bit | 1 | bslbf |
PTS[14..0] | 15 | bslbf |
marker_bit | 1 | bslbf |
} | ||
if(PTS_DTS_flags =='11'){ | ||
'0011' | 4 | bslbf |
PTS[32..30] | 3 | bslbf |
marker_bit | 1 | bslbf |
PTS[29..15] | 15 | bslbf |
marker_bit | 1 | bslbf |
PTS[14..0] | 15 | bslbf |
marker_bit | 1 | bslbf |
'0001' | 4 | bslbf |
DTS[32..30] | 3 | bslbf |
marker_bit | 1 | bslbf |
DTS[29..15] | 15 | bslbf |
marker_bit | 1 | bslbf |
DTS[14..0] | 15 | bslbf |
marker_bit | 1 | bslbf |
} | ||
if(ESCR_flag =='1'){ | ||
reserved | 2 | bslbf |
ESCR_base[32..30] | 3 | bslbf |
marker_bit | 1 | bslbf |
ESCR_base[29..15] | 15 | bslbf |
marker_bit | 1 | bslbf |
ESCR_base[14..0] | 15 | bslbf |
marker_bit | 1 | bslbf |
ESCR_extension | 9 | uimsbf |
marker_bit | 1 | bslbf |
} | ||
if(ES_rate_flag =='1'){ | ||
marker_bit | 1 | bslbf |
ES_rate | 22 | uimsbf |
marker_bit | 1 | bslbf |
} | ||
if (DSM_trick_mode_flag =='1'){ | ||
trick_mode_control | 3 | uimsbf |
if ( trick_mode_control = =fast_forward ) { | ||
field_id | 2 | bslbf |
intra_slice_refresh | 1 | bslbf |
frequency_truncation | 2 | bslbf |
} | ||
else if ( trick_mode_control = = slow_motion ) { | ||
rep_cntrl | 5 | uimsbf |
} | ||
else if ( trick_mode _control = = freeze_frame ) { | ||
field_id | 2 | uimsbf |
reserved | 3 | bslbf |
} | ||
else if ( trick_mode _control = = fast_reverse ) { | ||
field_id | 2 | bslbf |
intra_slice_refresh | 1 | bslbf |
frequency_truncation | 2 | bslbf |
else if ( trick_mode_control = = slow_reverse ) { | ||
rep_cntrl | 5 | uimsbf |
} | ||
else | ||
reserved | 5 | bslbf |
} | ||
if ( additional_copy_info_flag = ='1'){ | ||
marker_bit | 1 | bslbf |
additional_copy_info | 7 | bslbf |
} | ||
if (PES_CRC_flag==‘1’){ | ||
previous_PES_packet_CRC | 16 | bslbf |
} | ||
if ( PES_extension_flag =='1') { | ||
PES_private_data_flag | 1 | bslbf |
pack_header_field_flag | 1 | bslbf |
program_packet_sequence_counter_flag | 1 | bslbf |
P-STD_buffer_flag | 1 | bslbf |
reserved | 3 | bslbf |
PES_extension_flag_2 | 1 | bslbf |
if(PES_private_data_flag =='1'){ | ||
PES_private_data | 128 | bslbf |
} | ||
if (pack_header_field_flag == '1'){ | ||
pack_field_length | 8 | uimsbf |
pack_header() | ||
} | ||
if (program_packer_sequence_counter_flag == '1'){ | ||
marker_bit | 1 | bslbf |
program_packet_sequence_counter | 7 | uimsbf |
marker-bit | 1 | bslbf |
MPEG1_MPEG2_indentifier | 1 | bslbf |
original_stuff_length | 6 | uimsbf |
} | ||
if (P-STD_buffer_flag = = '1'({ | ||
'01' | 2 | bslbf |
P-STD_buffer_scale | 1 | bslbf |
P-STD_buffer_size | 13 | uimsbf |
} | ||
if (PES_extension_flag_2 == '1'){ | ||
marker_bit | 1 | bslbf |
PES_extension_field_length | 7 | uimsbf |
for(i=0;i<PES_extension_field_length;i++){ | ||
reserved | 8 | bslbf |
} | ||
} | ||
} | ||
for (i=0;i<N1;i++)} | ||
stuffing_byte | 8 | bslbf |
} | ||
for (i=0;i<N2;i++){ | ||
PES_packet_data_byte | 8 | bslbf |
} | ||
} | ||
else if (stream_id = = program_stream_map | ||
|| stream_id = = private_stream_2 | ||
|| stream_id = = ECM | ||
|| stream_id = = EMM | ||
|| stream_id = = program_stream_directory | ||
|| stream_id = = DSMCC_stream | ||
|| stream_id = = ITU-T Rec. H.222.1 type E stream ){ | ||
for (i=0;i<PES_packet_length;i++){ | ||
PES_packet_data_byte | 8 | bslbf |
} | ||
} | ||
else if (steam_id = = padding_stream){ | ||
for (i=0;i<PES_packet_length;i++){ | ||
padding_byte | 8 | bslbf |
} | ||
} | ||
} | ||
实例分析:
待分析数据:00 00 01 E0B8 D7 80 C0 0A 31 00 01 00 01 11 00 01 00 01
1. Packet_start_code_prefix 分组起始前缀字段:24位代码,它和后面的stream_id构成了标识分组开始的分组起始码。它是一个值为'0000 0000 0000 0000 0000 0001'(0x000001)的位串。
十六机制:00 00 01
2. Stream_id流标识字段:在节目流中,它规定了基本流的号码和类型。
stream_id | 注 | 流编码 |
|---|---|---|
1011 1100 | 1 | program_stream_map(0xBC) |
1011 1101 | 2 | private_stream_1(0xBD) |
1011 1110 | padding_stream(0xBE) | |
1011 1111 | 3 | private_stream-2(0xBF) |
110x xxxx | GB/T XXXX.3或GB/T AAAA.3音频流编号xxxx(0xC0~0xDF) | |
1110 xxxx | GB/T XXXX.2或GB/T AAAA.2视频流编号xxxx(0xE0~0xEF) | |
1111 0000 | 3 | ECM_stream(0xF0) |
1111 0001 | 3 | EMM_stream(0xF1) |
1111 0010 | 5 | GB/T XXXX.1附录B或GB/T XXXX.6_DSMCC_stream(0xF2) |
1111 0011 | 2 | ISO/IEC_13522_stream(0xF3) |
1111 0100 | 6 | ITU-T Rec. H.222.1类型A |
1111 0101 | 6 | ITU-T Rec. H.222.1类型B |
1111 0110 | 6 | ITU-T Rec. H.222.1类型C |
1111 0111 | 6 | ITU-T Rec. H.222.1类型D |
1111 1000 | 6 | ITU-T Rec. H.222.1类型E |
1111 1001 | 7 | ancillary_stream(0xF9) |
1111 1010…1111 1110 | 保留数据流 | |
1111 1111 | 4 | program_stream_directory(0xFF) |
符号x表示值'0'或'1'均被允许且可产生相同的流类型。流号码由x的取值决定。注1 类型为program_stream_map的PES分组有唯一的语法,在2.5.4.1中作了规定。2 类型为private_stream_1和ISO/IEC_13352_stream的PES分组与GB/T XXXX.2及GB/T XXXX.3音频流服从相同的PES分组语法。3 类型为private_stream_2,ECM_stream和EMM_stream的PES分组与private_stream_1相似,除了在PES_packet_length字段后未规定语法。4 类型为program_stream_directory的PES分组有唯一的语法,在2.5.5中作了规定。5 类型为DSM_CC_stream的PES分组有唯一的语法,在GB/T XXXX.6中作了规定。6 stream_id与表2-29中的stream_type 0x09相关联。7 stream_id仅用于PES分组。PES分组在传输流中携带了来源于节目流或GB/T AAAA.1系统流的数据(参见2.4.3.7)。 | ||
能看到的就是BC,节目映射流标记。BD一些国标厂家会填充一些私有流。E0代表视频流,C0代表音频流,其它的基本不需要关注。
十六机制:E0
二进制:1110 0000
3.PES_packet_lengthPES分组数据长度字段:16位字段,指出了PES分组中跟在该字段后的字节数目。值为0表示PES分组长度要么没有规定要么没有限制。这种情况只允许出现在有效负载包含来源于传输流分组中某个视频基本流的字节的PES分组中。
十六机制:B8 D7
二进制:1110 1111
十进制:47319
说明该PES的长度是47319,意思从这个字段之后再数这么多字节将是下一个PS包。
4.PES_scrambling_controlPES加扰控制字段:2位字段,表示PES分组有效负载的加扰方式。当加扰发生在PES层,PES分组标题,如果有可选字段的话也包括在内,不应被加扰(参见2-19)。
十六机制:80
二进制:1000 0000
这两个bit位一般是默认为10的,如果是其它stream_id则无所谓填什么。
5.PES_priority优先级字段:1位字段,指示PES分组中有效负载的优先级。'1'表示PES分组中有效负载的优先级高于该字段为'0'的PES分组有效负载。多路复合器能使用该字段来区分安排基本流中数据的优先级。传输机制不应改动该字段。
十六机制:80
二进制:1000 0000
十进制:0
6.Data_alignment_indicator数据对齐指示符字段:1位标志。置'1'时表示PES分组标题后紧跟着在2.6.10中的data_alignment_indicator所指出的视频起始码或音频同步字,如果有data_alignment_indicator描述符的话。若其值为'1'且无该描述符,则需要在表2-47和2-48中alignment_type '01'所表示的对齐。当值为'0'时,没有定义是否有任何此种的对齐。
十六机制:80
二进制:1000 0000
十进制:0
7.Copyright版权字段:1位字段。置'1'时表示相关PES分组有效负载的材料受到版权保护。当值为'0'时,没有定义该材料是否受到版权保护。2.6.24中描述的版权描述符与包含PES分组的基本流相关。若描述符作用于包含PES分组的材料,则版权标志被置为'1'。
十六机制:80
二进制:1000 0000
十进制:0
8.Original_or_copy原始或者拷贝字段:1位字段。置'1'时表示相关PES分组有效负载的内容是原始的;值为'0'表示相关PES分组有效负载的内容是一份拷贝。
十六机制:80
二进制:1000 0000
十进制:0
9. PTS_DTS_flags PTS DTS标志字段:2位字段。当值为'10'时,PTS字段应出现在PES分组标题中;当值为'11'时,PTS字段和DTS字段都应出现在PES分组标题中;当值为'00'时,PTS字段和DTS字段都不出现在PES分组标题中。值'01'是不允许的。
十六机制:C0
二进制:1100 0000
10. ESCR_flag ESCR标志字段:1位标志。置'1'时表示ESCR基础和扩展字段出现在PES分组标题中;值为'0'表示没有ESCR字段。
十六机制:C0
二进制:1100 0000
接下来这6个bit位都是0,说明接下来的标记都是0说明标记的字段在PES头里面都不存在。也是就11-15对应字段不用分析了。
11. ES_rate_flag ES速率标志字段:1位标志。置'1'时表示ES_rate字段出现在PES分组标题中;值为'0'表示没有ES_rate字段。
十六机制:C0
二进制:1100 0000
12.DSM_trick_mode_flagDSM特技方式标志字段:1位标志。置'1'时表示有8位特技方式字段;值为'0'表示没有该字段。
十六机制:C0
二进制:1100 0000
13.Additional_copy_info_flag附加版权信息标志字段:1位标志。置'1'时表示有附加拷贝信息字段;值为'0'表示没有该字段。
十六机制:C0
二进制:1100 0000
14. PES_CRC_flag PES CRC标志字段:1位标志。置'1'时表示CRC字段出现在PES分组标题中;值为'0'表示没有该字段。
十六机制:C0
二进制:1100 0000
15. PES_extension_flag PES扩展字段:1位标志。置'1'时表示PES分组标题中有扩展字段;值为'0'表示没有该字段。
十六机制:C0
二进制:1100 0000
16.PES_header_data_lengthPES标题数据长度字段:8位字段。指出包含在PES分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前的字节指出了有无可选字段。
十六机制:0A
十进制:10
17.Marker_bit标记位字段:值为'1'的1位字段。
18.PTS 展示时间戳字段:展现时间与解码时间的关系如下:PTS是一个编码在三个分离字段中的33位数字。它指出了基本流n的第k个展现单元在系统目标解码器中的展现时间tpn(k)。PTS的值以系统时钟频率的1/300(即90 kHz)为单位。展现时间由PTS根据式2-11计算而来。对编码展现时间戳频率的约束参见2.7.4。
PTS(k)=((system_clock_frequency×tpn(k)) DIV 300) % 233 (2-11)
其中,tpn(k)是展现单元Pn(k)的展现时间。
对音频而言,若PES分组标题中有PTS,则它是指PES分组中开始的第一个存取单元。若PES分组中有音频存取单元的首字节,则有一个音频存取单元开始于该PES分组中。
对视频而言,若PES分组标题中有PTS,则它是指包含PES分组中开始的第一个画面起始码的存取单元。若PES分组中有画面起始码的首字节,则有一个画面起始码开始于该PES分组中。
对音频展现单元(PU),low_delay序列中的视频PU以及B画面,展现时间tpn(k)应等于tdn(k)。
对于非low_delay中的I画面和P画面,在存取单元(AU) k和k'之间无解码不连续时,展现时间tpn(k)应等于下一个传输的I画面或P画面的解码时间tdn(k) (参见2.7.5)。若有解码不连续或流终止,则tpn(k)和tdn(k)之间的差别应与初始流一直延续,没有不连续也没有终止时完全相同。
注1: low_delay序列是low_delay标志被设置的视频序列(参见GB/T XXXX.2中的6.2.2.3)。
若音频中有滤波,则系统模型假定滤波不会导致延迟。因此,编码时PTS所涉及的采样与解码时PTS所涉及的采样是相同的。对于可伸缩编码,参见2.7.6。
十六机制:31 00 01 00 01
二进制:0011 0001 0000 0000 0000 0001 0000 0000 0000 0001
十进制:0
说明此处的PTS是0.
前面的0011取决于前面的PTS_DTS_flags PTS DTS标记,前面是11则这里是0011,前面是10则这里是0010.
19.DTS 解码时间戳字段:DTS是一个编码在三个分离字段中的33位数字。它指出了基本流n的第j个展现单元在系统目标解码器中的解码时间tdn(j)。DTS的值以系统时钟频率的1/300 (即90 kHz)为单位。解码时间由DTS根据式2-12计算而来:
DTS(j)=((system_clock_frequency×tdn(j))DIV 300) % 233 (2-12)
其中,tdn(j)是存取单元An(j)的解码时间。
对视频而言,若PES分组标题中有DTS,则它是指包含PES分组中开始的第一个画面起始码的存取单元。若PES分组中有画面起始码的首字节,则该画面起始码开始于该PES分组中。
对于可伸缩编码,参见2.7.6。
十六机制:11 00 01 00 01
二进制:0001 0001 0000 0000 0000 0001 0000 0000 0000 0001
十进制:0
说明此处的DTS是0.
20. ESCR_base ESCR_extension ESCR字段:
42位字段,分两部分编码。第一部分是一个长度为33位的字段,其值ESCR_base(i)由式2-14给出;第二部分是一个长度为9位的字段,其值ESCR_ext(i)由式2-15给出。ESCR字段指出了基本流中包含ESCR_base最后一个比特的字节到达PES-STD输出端的期望时间(参见2.5.2.4)。
特别地
ESCR(i)=ESCR_base(i)×300+ESCR_ext(i) (2-13)
其中:
ESCR_base(i)=((system_clock_frequency×t(i))DIV 300) % 233 (2-14)
ESCR _ext(i)=((system_clock_frequency×t(i))DIV 1) %300 (2-15)
ESCR和ES_rate字段(参见下面紧接的语义)包含与PES流序列相关的时间信息。这些字段应满足2.7.3中定义的约束
21. ES_rate 基本流速率字段:22位无符号整数。对于PES流而言,它指出了系统目标解码器接收PES分组的速率。该字段在它所属的PES分组以及同一个PES流的后续PES分组中一直有效,直到遇到一个新的ES_rate字段。该字段的值以50字节/秒为单位,且不能为0。该字段用于定义PES流的字节到达P-STD输入端的时间(参见2.5.2.4中的定义)。在各个PES分组中,编码在该字段中的值可能不同
22. Trick_mode_control 特技方式控制字段:3位字段。它表示作用于相关视频流的特技方式。对其它类型的基本流,该字段及其后5位的含义没有定义。一般不会在国标中出现该字段,前面标记为0.
23. Fast_forword 快进:
24. Freeze_frame 冻结帧:
25. Fast_reverse 快倒:
26. Slow_reverse 慢倒:
27. Filed_id 字段标识字段:
2位字段,表示应该显示哪个(些)字段。根据表2-21对其进行编码。
表2-21 field_id字段控制值
值 | 描 述 |
|---|---|
'00' | 仅自顶向下播放 |
'01' | 仅自底向上播放 |
'10' | 播放所有帧 |
'11' | 保留 |
28. Intra_slice_refresh 片内参考字段:
1位标志。置'1'时表示PES分组的视频数据编码片中可能有丢失的宏块;置'0'时,表示上述情况可能不出现。更多的信息可参见GB/T XXXX.2。解码器可以用前一个解码画面中同一个位置的宏块来代替丢失的宏块。
29. Frequency_truncation 频率截断字段:
2位字段。指出在对PES分组中数据进行编码时可能用到受限系数集合。其值定义于表2-22。
表2-22 系数选择值
值 | 描述 |
|---|---|
'00' | 仅DC系数非0 |
'01' | 仅前三个系数非0 |
'10' | 仅前六个系数非0 |
'11' | 所有系数均可能非0 |
30. Rep_contrl 显示次数控制字段:5位字段,指出隔行画面中每一字段的显示次数或渐进画面显示次数。对隔行画面而言,顶字段或底字段是否应首先显示是视频序列标题中trick_mode_control字段和top_field_first字段的功能。该字段值不能为'0'。
31. Additional_copy_info 附加版权信息字段:7位字段,包含与版权信息有关的专用数据。
32. Previous_PES_Packet_CRC 前PES分组CRC字段:
16位字段。在对前一个PES分组(不包括该PES分组的标题)进行处理后,该字段包含一个在解码器的16个寄存器中生成0输出的CRC值。该CRC值与附录A中所定义的相类似,但具有以下多项式:
x16+x12+x5+1
注2: 该CRC值是为了用于网络维护,例如将有间隙性错误的源隔离开来,而不是为了供基本流解码器使用。它仅用于计算数据字节,因为在传输过程中PES分组标题数据可能被修改。
33. PES_private_data_flag PES专用数据标志字段:1位标志。置'1'时表示PES分组标题中包含专用数据;置'0'时表示PES分组标题中无专用数据。
34. Pack_header_filed_flag 包标题域标志字段:1位标志。置'1'时表示PES分组标题中有GB/T AAAA.2包标题或节目流包标题。若该字段在包含于节目流中的PES分组中,其值应为'0'。在传输流中,当值为'0'时表示PES标题中无包标题。
35. Program_packet_sequence_counter_flag 节目分组序列计数标志字段:1位标志。值为'1'时表示PES分组有program_packet_sequence_counter,MPEG1_MPEG2_identifier和original_stuff_length字段。值为'0'时表示PES分组标题中无这些字段。
36. P-STD_buffer_flag P_STD缓冲区标志字段:1位标志。置'1'时表示PES分组标题中有P-STD_buffer_scale和P-STD_buffer_size字段。值为'0'时表示PES标题中无这些字段。
37. PES_extension_flag_2 PES扩展2标志字段:1位标志,置'1'时表示有PES_extension_field_length及相关字段。
38. PES_private_flag PES专用数据字段:16位字段。包含专用数据。这些数据与其前后的字段组合在一起时,不能与packet_start_code_prefix (0x000001)冲突。
39. Pack_filed_length 包字段长度字段:8位字段。表示pack_header_field()以字节为单位时的长度。
40. Program_packet_sequence_counter 节目分组序列计数字段:8位字段。表示pack_header_field()以字节为单位时的长度。
41. MPEG1_MPEG2_identifier MPEG1 MPEG2标识符字段:1位标志。置'1'时表示PES分组携带的信息来自于GB/T AAAA.1流;置'0'时表示PES分组携带的信息来自于节目流。
42. Original_stuff_scale 初始填充长度字段:6位字段。指定用于初始GB/T XXXX.1分组标题或初始GB/T AAAA.1分组标题中的填充字节数。
43. P-STD_buffer_scale p-STD缓冲区比例字段:1位字段。仅当该PES分组包含于节目流中时才有意义。它指出了用来解释后续P-STD_buffer_size字段的比例因子。若前面的stream_id表示一个音频流,该字段值应为'0';若前面的stream_id表示一个视频流,该字段值应为'1'。对于所有的其它流类型,其值可以为'0'或'1'
44. P-STD_buffer_size 缓冲区大小字段:13位无符号整数。仅当该PES分组包含于节目流中时才有意义。它定义了P-STD输入缓冲区的大小BSn。若P-STD_ buffer_scale的值为'0',那么P-STD_buffer_size以128字节为单位来度量缓冲区的大小。若P-STD_buffer_scale的值为'1',那么P-STD_buffer_size以1024字节为单位来度量缓冲区的大小。
45. PES_extension_filed_length PES扩展长度字段:7位字段。指出了跟在该字段之后在PES扩展字段中直到且包括任何保留字节的数据的字节长度。
46. Stuffing_byte 填充字节字段:8位字段,其值恒定为'1111 1111'。可以由编码器插入以满足通道的需求等。解码器丢弃该字段。一个PES分组标题中只能出现32个填充字节
47. PES_packet_data_byte PES分组数据字节字段:该字段应该是来自于由分组的stream_id或PID所指定的基本流的连续数据字节。
48. Padding_byte 填充字节字段:
总结
PES显然是里面所有头最复杂的,但是很多字段到底有没有定义,需要看第八个C0字节的六大标记,如果没有此标记解析起来相当简单。特别是PES扩展字段标记和特技方式字段。再后面是长度0A,后面的字节则就是PES重要信息PTS和DTS。当然有没有DTS还是要看前面的标记。一种时间戳占五个字节。
1. 先解析头的前缀四字节,根据stream_id判断是音频还是视频;
2. 紧接着长度,则为下一个ps包的起始位置;
3. 判断PTS_DTS_flags PTS DTS标记;
4. 解析PTS和DTS值即可;
================================================================
国标PS总结:
国标PS流虽然非常复杂,每个包头涉及字段很多。但是实际解析起来可以忽略大量字段,t同时如果是封装,对于不关心字段也就填充默认值来处理:
1. PS头即一般就是14字节,直接识别跳过即可;
2. 系统标题头也是识别前缀,直接读取长度跳过即可;
3. 节目映射头只有当PES里面承载的ES是IDR帧时才会存在,一般要解析是否还有音视频,同时了解他们的编码格式;
4. PES头虽然复杂,但是我们只解析里面的PTS和DTS,里面的六大标记字段只有是0,解析起来也是非常简单的,其中第7和第8字节是关键;