FFMPEG之海康实时回调出来的PS流格式
参考博客:PS流格式解析
PES包码流分析提供
本人PS流解析成H264的其余文章:
01PS流格式
02封装时遇到的重点问题
03海康PS转H264的编码思想(带图码流解释)
前言:
我们知道,利用海康的实时预览接口可以实时监控,并且可以设置回调函数取出码流数据。而根据海康的参考文档中,回调函数的数据类型有三种,一个是系统头;一个是码流数据;另一个是其它数据,这三个值分别对应宏值为0,1,2。但是我们需要注意,当值=1时,该码流数据并不是h264类型(好像有部分设备是支持直接输出H264,我不太记得了),而是被封装成PS流格式的类型。所以我们在推流或者保存的过程中,需要将PS流转成H264码流。
海康针对H264 做如下PS 封装:
每个IDR NALU 前一般都会包含SPS、PPS 等NALU,因此将SPS、PPS、IDR 的NALU 封装为一个PS 包。但该PS包不是完整的PS包,需要再包括ps 头,然后加上PS system header,PS system map,PES header+h264 raw data。
所以一个IDR NALU PS 包由外到内顺序是:PSheader | PS system header | PS system Map | PES header | h264 raw data。对于其它非关键帧的PS 包,就简单多了,直接加上PS头和PES 头就可以了。因为非I帧是不包括系统映射字段的,且系统头是当切仅当为PS流的第一个包才有。所以顺序为:PS header | PES header | h264raw data。以上是对只有视频video 的情况,如果要把音频Audio也打包进PS 封装,也可以。当有音频数据时,将音频数据加上PES header 放到视频PES 后就可以了。顺序如下:PS 包 = PS 头 | PES (video) | PES(audio),再用RTP 封装发送就可以了。
GB28181对RTP传输的数据负载类型有规定(参考GB28181 附录B),负载类型中96-127
== RFC2250 建议96 表示PS 封装,建议97 为MPEG-4,建议98为H264==
即我们接收到的RTP 包首先需要判断负载类型,若负载类型为96,则采用PS 解复用,将音视频分开解码。若负载类型为98,直接按照H264 的解码类型解码。
注:此方法不一定准确,取决于打包格式是否标准。
还有就是,PS 包中的流类型(stream type)只在映射字段出现,所以想分析该流类型,必须再I帧中寻找判断。
下面分析PS流格式每一部分的字段
1 首先是PSheader部分:(来自MPEG2标准中文文档)
上面可以看到4B的起始码和共42bit的SRC,还有22bit的节目复用速率program_Mux_Rate。但实际上42bit的SRC是由6B组成,其中有2bit是固定01的位,4bit是标记码用于分割。22bit的PMR也一样,由3B组成,最后2bit是标记码。所以我们看下面的实际存储情况。分别给出MPEG2标准中文文档的图和内存的存储情况。前者可以参考作为建立结构体。
MPEG2标准中文文档图:指出比较重要的字段。
PS包头在内存的存储情况:
起始码:
系统时钟基准两部分:
PS复用速率:
PS包头各字段解析
1)包起始码字段 pack_start_code
值为'0000 0000 0000 0000 0000 0001 1011 1010' (即0x000001BA)的位串,用来标志一个PS包的开始。
2)系统时钟参考字段 system_clock_reference_base、system_clock_reference_extenstion 。
系统时钟参考(SCR)是一个分两部分编码的42位字段。
第一部分system_clock_reference_base是一个长度为33位的字段,其值SCR_base(i)由式2-19给出(查看MPEG2文档);
第二部分system_clock_reference_extenstion是一个长度为9位的字段,其值SCR_ext(i)由式2-20给出。SCR字段指出了基本流中包含ESCR_base最后一位的字节到达节目目标解码器输入端的期望时间。SCR字段的编码频率要求见2.7.1。该字段具体使用时可自行查看文档。
3)标记位字段 marker_bit
1bit字段,取值'1'。
4)节目复合速率字段 program_mux_rate
一个22位整数,规定P-STD在含有该字段的包,期间接收节目流的速率。其值以50字节/秒为单位。不允许取0值。该字段所表示的值用于在2.5.2中定义P-STD输入端的字节到达时间。该字段值在本标准中的节目多路复合流的不同包中取值可能不同。
5)包保留字段 reserved
5位整数。
6)包填充长度字段 pack_stuffing_length
3位整数,规定该字段后填充字节的个数。
以上为固定12字节
7)填充字节字段 stuffing_byte
8位字段,取值恒为'1111 1111'。该字段能由编码器插入,例如为了满足通道的要求。它由解码器丢弃。在每个包标题中最多只允许有7个填充字节。
以上忽略标记码字节总数为13B,但是7)填充字节总共1B可以不要,所以PS头最少为12B。但我们分析时必须带上标记码分析,所以实际分析4+6+3+1=14个字节。
PS头码流分析:
00 00 01 ba 7e ff 3e fb 44 01 00 5f 6b f8 00 00 01 e0 14
1)起始码4B
00 00 01 ba;
2)42bit共6字节SCR及扩展
7e ff 3e fb 44 01//大家可以自己分析具体位数
3)22bit的PS复用速率,单位为50B/s
00 5f 6b//列出二进制码后,右移两位舍弃掉标记码,求出该数的大小,为0x17DA。所以速率rate=0x17DA*50。
额外小提示,因为我在做项目时在VLC拉流播放是黑屏的,所以当我获取PS流分析时,结果速率为0,即这三字节为00 00 03;//3=011为标记码
这是不允许的,所以这应该是在解析PS流时出错,导致无法发送数据,所以速率为0,这也给大家提供一个解决黑屏的思路。
一般跳过ba头的方法是:4+6+3+1+补充字节长度。
补充字节长度为:
//ps头,14字节
struct program_stream_pack_header{
pack_start_code PackStart;// 4
unsigned char Buf[9];
unsigned char stuffinglen;
};
//psHead为program_stream_pack_header类型
int stuffLen = psHead->stuffinglen & '\x07';
由于这里的psHead[14]= f8;
所以stuffLen = f8 & '\x07';补充字节的长度为0.
2 接下来看看PS system header(即PS系统头:节目流系统标题)部分的定义:(来自MPEG-2标准文档)
注:Systemheader当且仅当pack是第一个数据包时才存在,即PS包头之后就是系统标题。取值0x000001BB的位串,指出系统标题的开始,暂时不需要处理,读取Header Length直接跳过即可。
语 法 | 位数 | 助记符 |
---|---|---|
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 |
} | ||
} |
1)系统标题起始码字段
取值'0000 0000 0000 0000 0000 0001 1011 1011' (0x000001BB)的位串,指出系统标题的开始。
2)标题长度字段 header_length
16位字段。指出该字段后的系统标题的字节长度。在本规范将来的扩充中可能扩展该字段。可利用该字段跳出系统头。
3)速率界限字段 rate_bound
22位字段,取值不小于编码在节目流的任何包中的program_mux_rate字段的最大值。即在编码时,值要大于等于PS流包中的PMR字段的最大值。该字段可被解码器用于估计是否有能力对整个流解码。
4)音频界限字段 audio_bound
6位字段,取值是在从0到32的闭区间中的整数,且不小于节目流中解码过程同时活动的GB/T XXXX.3和GB/T AAAA.3音频流的最大数目。在本小节中,若STD缓冲区非空或展现单元正在P-STD模型中展现,则GB/T XXXX.3和GB/T AAAA.3音频流的解码过程是活动的。
5)固定标志字段 fixed_flag
1位标志位。置'1'时表示比特率恒定的操作;置'0'时,表示操作的比特率可变。在恒定比特率的操作期间,复合的GB/T XXXX.1流中的system_clock_reference字段值应遵从下面的线性公式:
SCR_base(i)=((c1×i+c2) DIV 300) % 233 (2-22)
SCR_ext(i)=((c1×i+c2) DIV 300) % 300 (2-23)
其中:
c1 对所有i均有效的实型常数;
c2 对所有i均有效的实型常数;
i在GB/T XXXX.1复合流中包含任何system_clock_reference字段的最后一位的字节索引。
6)CSPS标志字段 CSPS_flag
1位字段。置'1'时,节目流符合2.7.9中定义的限制。
7)系统音频锁定标志字段 system_audio_lock_flag
1位字段。表示在系统目标解码器的音频采样率和system_clock_frequency之间存在规定的比率。system_clock_frequency在2.5.2.1中定义而音频采样率由GB/T XXXX.3规定。如果对节目流中所有音频基本流的所有展现单元进行展现,那么system_clock_frequency和实际音频采样率的比例SCASR是恒定的;若再满足音频流中所指出的标准采样率和下表中数值相等,则该字段只能为'1'。
SCASR=(system_clock_frequency) / audio_sample_rate_in_the_P-STD (2-24)
记号X/Y表示实数除法。
标准音频采样频率(kHz) | 16 | 32 | 22.05 | 44.1 | 24 | 48 |
---|---|---|---|---|---|---|
SCASR | 27 000 000 /16 000 | 27 000 000 /32 000 | 27 000 000 /22 050 | 27 000 000 /44 100 | 27 000 000 /24 000 | 27 000 000 /48 000 |
8)系统视频锁定标志字段 system_video_lock_flag
1位字段。表示在系统目标解码器的视频帧速率和system_clock_frequency之间存在规定的比率。system_clock_frequency在2.5.2.1中定义而视频帧速率由GB/T XXXX.2规定。如果对GB/T XXXX.1中所有视频基本流的所有展现单元进行展现,那么system_clock_frequency和实际视频帧速率的比例SCFR是恒定的;若再满足视频流中所指出的标准帧速率和下表中数值相等,则该字段只能为'1'。
SCFR=system_clock_frequency / frame_rate_in_the_P-STD (2-25)
标准帧速率(Hz) | 23.976 | 24 | 25 | 29.97 | 30 | 50 | 59.94 | 60 |
---|---|---|---|---|---|---|---|---|
SCFR | 1126125 | 1125000 | 1080000 | 900900 | 900000 | 540000 | 450450 | 450000 |
比率SCFR的值是精确的。对于23.976,29.97或59.94帧/秒的标准速率,实际的帧速率与标准速率略有不同。
9)视频界限字段 video_bound
5位字段,取值是在从0到16的闭区间中的整数且不小于节目流中解码过程同时活动的GB/T XXXX.2和GB/T AAAA.2流的最大数目。在本小节中,若P-STD缓冲区非空或展现单元正在P-STD模型中展现,则GB/T XXXX.2和GB/T AAAA.2视频流的解码过程是活动的。
10)分组速率限制标志字段packet_rate_restriction_flag
1位标志位。若CSPS标识为'1',则该字段表示2.7.9中规定的哪个限制适用于分组速率。若CSPS标识为'0',则该字段的含义未定义。
11)保留位字段 reserved_bits
7位字段。被保留供ISO/IEC将来使用。它的值应为'111 1111',除非ISO/IEC对它作出其它规定。
12)流标识字段 stream_id
8位字段。指示其后的P-STD_buffer_bound_scale和P-STD_buffer_size_bound字段所涉及的流的编码和基本流号码。 若取值'1011 1000',则其后的P-STD_buffer_bound_scale和P-STD_buffer_size_bound字段指节目流中所有的音频流。
若取值'1011 1001',则其后的P-STD_buffer_bound_scale和P-STD_buffer_size_bound字段指节目流中所有的视频流。
若stream_id取其它值,则应该是大于或等于'1011 1100'的一字节值且应根据表2-18解释为流的编码和基本流号码。
节目流中的每个基本流应在每个系统标题中通过这种机制精确地规定一次它的P-STD_buffer_bound_scale和P-STD_buffer_size_bound。
13)P-STD缓冲区界限比例字段 P-STD_buffer_bound_scale
1位字段。表示用于解释后续P-STD_buffer_size_bound字段的比例系数。若前面的stream_id表示一个音频流,则该字段值为'0'。若表示一个视频流,则该字段值为'1'。对于所有其它的流类型,该字段值可以为'0'也可以为'1'。
14)P-STD缓冲区大小界限字段 P-STD_buffer_size_bound
13位无符号整数,取值不小于节目流中流n的所有分组的P-STD缓冲区大小BSn的最大值。即BSn≤P-STD_buffer_size_bound,实际STD_buffer_size_bound还会以一定单位度量。
若P-STD_buffer_bound_scale的值为'0',则该字段以128字节为单位来度量缓冲区大小的边界。
若P-STD_buffer_bound_scale的值为'1',则该字段以1024字节为单位来度量缓冲区大小的边界。
因此:
if (P-STD_buffer_bound_scale = = 0)
BSn≤P-STD_buffer_size_bound×128
else
BSn≤P-STD_buffer_size_bound×1024
所以对于系统头部的解析,我们一般只要先首先判断是否存在系统头(根据系统头的起始码0x000001BB),然后我们读取系统头的头部长度,即PS SYSTEM HEADER LENGTH部分,然后根据头部的长度,跳过PS系统头。进入下一个部分,即PS 节目流映射头。注:PS映射头只有I帧时才有。
3 接着看看PS流的节目映射流部分(节目流映射)定义:(来自MPEG-2标准文档)
只有I帧才存在的PS包字段。
表2-35 节目流映射
语 法 | 位数 | 助记符 |
---|---|---|
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 |
} |
节目流映射中各字段的语义定义:
1)分组起始码前缀字段 packet_start_code_prefix
24位码。它和跟随其后的map_stream_id共同组成一个分组起始码以标志分组的开始。该字段是值为'0000 0000 0000 0000 0000 0001' (0x000001)的位串。
2)映射流标识字段 map_stream_id
8位字段,值为0xBC。
3)节目流映射长度字段 program_stream_map_length
16位字段。指示紧跟在该字段后的program_stream_map中的字节数。该字段的最大值为1018(0x3FA)。
4)当前下一个指示符字段 current_next_indicator
1位字段。置'1'时表示传送的节目流映射当前是可用的。置'0'时表示传送的节目流映射还不可用,但它将是下一个生效的表。
5)节目流映射版本字段 program_stream_map_version
5位字段,表示整个节目流映射的版本号。一旦节目流映射的定义发生变化,该字段将递增1,并对32取模。在current_next_indicator为'1'时,该字段应该是当前适用的节目流映射的版本号;在current_next_indicator为'0'时,该字段应该是下一个适用的节目流映射的版本号。
6)节目流信息长度字段 program_stream_info_length
16位字段,指出紧跟在该字段后的描述符的总长度。
7)标记位字段 marker_bit
1位字段,取值为'1'。
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可以得到后面定义了几个流类型信息。)
9)流类型字段 stream_type
8位字段,根据表2-29规定了流的类型。该字段只能标志包含在PES分组中的基本流且取值不能为0x05。
(这里我们暂时根据国标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。
因为节目映射流字段只有在关键帧打包的时候,才会存在,所以如果要判断PS打包的流编码类型,就根据这个字段来判断。)
这个是比较关键的字段
10)基本流标识字段 elementary_stream_id
8位字段,指出该基本流所在PES分组的PES分组标题中stream_id字段的值。
(这个字段的定义,其中0x(C0~DF)指音频,0x(E0 ~ EF)为视频)
11)基本流信息长度字段 elmentary_stream_info_length
16位字段,指出紧跟在该字段后的描述符的字节长度。(即这个类型的流描述长度。这个字段后面的长度不在elementary_stream_map_length指定的范围类。)
12)CRC 32字段 CRC_32
32位字段,循环冗余校验,它包含CRC值以在处理完整个节目流映射后在附录A中定义的解码器寄存器产生0输出值。
这里长度比较多,列出来,大家自己分析一下各自的意义:
1)program_stream_map_length:
map的长度;
2)program_stream_info_length:
该字段后的描述符的总长度。
3)elementary_stream_map_length:
16位字段,指出在该节目流映射中的所有基本流信息的字节长度。它只包括stream_type、elementary_stream_id和elementary_stream_info_length字段。
4)elmentary_stream_info_length:
16位字段,指出紧跟在该字段后的描述符的字节长度。
对于这个字段的解析,我们需要取值0x000001BC的位串(前两个字段),指出节目流映射的开始,暂时不需要处理,读取program_stream_map_length直接跳过即可,如果需要解析流编码类型,必须详细解析这个字段。
例子,取一段码流分析系统标题和节目映射流:
00 00 01 ba 45 a9 d4 5c 34 01 00 5f 6b f8 00 00 …?E??\4…_k?..
01 bb 00 0c 80 cc f5 04 e1 7f e0 e0 e8 c0 c0 20 .?..€??.?.???
以下开始是映射流部分
00 00 01 bc 00 1e e1 ff 00 00 00 18 1b e0 00 0c …?..?..?..
2a 0a 7f ff 00 00 07 08 1f fe a0 5a 90 c0 00 00 *…??Z??..
00 00 00 00 00 00 01 e0 7f e0 80 80 0521 6a 75 …?.?€€.!ju
前14个字节是PS包头(注意,没有扩展);
//1)系统头开始码
00 00 01 bb是系统标题起始码;
//2)系统头后面的数据长度header_length,2B
00 0c说明了系统标题的长度(不包括起始码和长度字节本身);
//3)系统标题内容长度
接下来的12个字节是系统标题的具体内容,这里不做解析;
至此,PS头和PS系统头分析完毕。
4)节目映射流起始码
00 00 01 bc,这是节目映射流起始码;
//5)program_stream_map_length,2B
紧接着的00 1e同样代表该映射流的数据长度,共30字节,即去到00 00 01 e0之前 ;
//6)
e1 ff,包含current_next_indicator、reserved、program_stream_map_version、reserved、marker_bit共两字节。基本没用;
//7)
后面的00 00代表program_stream_info_length流信息长度,这里为0代表没有该流信息。
若存在的话,上面表格可以看到我们需要用循环跳过该长度信息,N=program_stream_info_length。或者用文件指针移动。
//8)
接下来是00 18,代表elementary_stream_map_length基本流长度,说明了后面还有24个字节;即同样去到00 00 01 e0之前 ;
来到这里同样需要循环判断多个基本流(视频流和音频流)
N1=elementary_stream_map_length。
//9)
接下来的1b是stream_type大小一个字节,意思是H264编码格式;
//10)
下一个字节为elementary_stream_id,e0意思是视频流;
//11)
接下来00 0c,是elementary_stream_info_length基本流信息的长度,同样需要循环跳过该信息12个字节。
该字段需要循环跳过。
这里总结一下,上面的是两字节的映射流总长度30,再加上两字节的映射流信息长度0;以上是针对映射流的。
然后是两字节的基本流数据长度24,这里是两字节的基本流信息长度,基本流的长度都需要循环判断或者跳过。
4 接下来我们来分析下PES包的内容:PES包=PES header+code raw data;
上面给出了总体框架图,部分字段可能不是特别详细。下面我们再对着图来加强记忆。下图比上图更详细的列出PES包的各个字段。需要你自己认真的静下心来看。看时可以对照上图,例如
1)先看6B的包头;
2)再看基本流特有信息;其中PES包头识别标志为2B;PES包头长为1B;PES信息区为46B;填充字节stuffing bytes最多为256B。
3)最后就是可变包数据,最大长度为65526。
接着我们来看看解析步骤图:(来自MPEG-2标准文档)
表2-17 PES分组:略。
其中:
表2-18 Stream_id赋值:略。
因为这两个表格太长了,没必要再弄。如若用到可参考上面博客链接的图。
PES分组中各字段的语义定义
1)分组起始码前缀字段 packet_start_code_prefix
24位代码,它和后面的stream_id构成了标识分组开始的分组起始码。它是一个值为'0000 0000 0000 0000 0000 0001' (0x000001)的位串。
2)流标识字段 stream_id
在节目流中,它规定了基本流的号码和类型。0x(C0~DF)指音频,0x(E0~EF)为视频。定义参见表2-18。在传输流中,它可以被设定为正确描述表2-18中定义的基本流类型的任何有效值。在传输流中,基本流类型在2.4.4的节目特定信息中作了规定。
3)PES分组长度字段 PES_packet_length
16位字段,指出了PES分组中跟在该字段后的字节数目。值为0表示PES分组长度要么没有规定要么没有限制。这种情况只允许出现在有效负载包含来源于传输流分组中某个视频基本流的字节的PES分组中。
4)PES加扰控制字段 PES_scrambling_control
2位字段,表示PES分组有效负载的加扰方式。当加扰发生在PES层,PES分组标题,如果有可选字段的话也包括在内,不应被加扰(参见2-19)。
5)PES优先级字段 PES_priority
1位字段,指示PES分组中有效负载的优先级。'1'表示PES分组中有效负载的优先级高于该字段为'0'的PES分组有效负载。多路复合器能使用该字段来区分安排基本流中数据的优先级。传输机制不应改动该字段。
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'时,没有定义是否有任何此种的对齐。
7)版权字段 copyright
1位字段。置'1'时表示相关PES分组有效负载的材料受到版权保护。当值为'0'时,没有定义该材料是否受到版权保护。2.6.24中描述的版权描述符与包含PES分组的基本流相关。若描述符作用于包含PES分组的材料,则版权标志被置为'1'。
8)原始或拷贝字段 original_or_copy
1位字段。置'1'时表示相关PES分组有效负载的内容是原始的;值为'0'表示相关PES分组有效负载的内容是一份拷贝。
9)PTS DTS标志字段 PTS_DTS_flags
2位字段。当值为'10'时,PTS字段应出现在PES分组标题中;当值为'11'时,PTS字段和DTS字段都应出现在PES分组标题中;当值为'00'时,PTS字段和DTS字段都不出现在PES分组标题中。值'01'是不允许的。
10)ESCR标志字段 ESCR_flag
1位标志。置'1'时表示ESCR基础和扩展字段出现在PES分组标题中;值为'0'表示没有ESCR字段。
11)ES速率标志字段 ES_rate_flag
1位标志。置'1'时表示ES_rate字段出现在PES分组标题中;值为'0'表示没有ES_rate字段。
12)DSM特技方式标志字段 DSM_trick_mode_flag
1位标志。置'1'时表示有8位特技方式字段;值为'0'表示没有该字段。
13)附加版权信息标志字段 additional_copy_info_flag
1位标志。置'1'时表示有附加拷贝信息字段;值为'0'表示没有该字段。
14)PES CRC标志字段 PES_CRC_flag
1位标志。置'1'时表示CRC字段出现在PES分组标题中;值为'0'表示没有该字段。
15)PES扩展标志字段 PES_extension_flag
1位标志。置'1'时表示PES分组标题中有扩展字段;值为'0'表示没有该字段。
16)PES标题数据长度字段 PES_header_data_length
8位字段。指出包含在PES分组标题中的可选字段和任何填充字节所占用的总字节数。该字段之前的字节指出了有无可选字段。
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。
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。
20)ESCR字段 ESCR_base,ESCR_extension
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位的含义没有定义。trick_mode状态的定义参见2.4.2.3的特技模式部分。
当trick_mode状态为假时,对GB/T XXXX.2视频而言,解码过程输出渐进序列中每幅画面的次数N由repeat_first_field和top_field_first字段来规定。对GB/T AAAA.2视频而言,由序列标题决定。
对于隔行序列,当trick_mode状态为假时,对GB/T XXXX.2视频而言,次数N由repeat_first_field和progressive_frame字段来规定。
当trick_mode状态为真时,画面的播放次数依赖于值N。
表2-20 特技方式控制值
值 | 描 述 |
---|---|
‘000’ | 快进 |
‘001’ | 慢动作 |
‘010’ | 冻结帧 |
‘011’ | 快倒 |
‘100’ | 慢倒 |
‘101-111’ | 保留 |
当该字段值发生变化或特技模式操作停止时,可能会出现下列情况的任意组合:
l 时基不连续;
l 解码不连续;
l 连续性计数器不连续;
在特技模式的情况下,解码和展现的非标准速度可能会导致视频基本流数据中定义的某些字段值不正确。同样,片段结构的语义约束也可能无效。这些例外所涉及的视频语法元素为:
l bit_rate;
l vbv_delay;
l repeat_first_field;
l v_axis_positive;
l field_sequence;
l subcarrier;
l burst_amplitude;
l subcarrier_phase;
在特技模式中,解码器不应该依赖于编码在这些字段中的值。
标准并不要求解码器能解码trick_mode_control字段。但是,能解码该字段的解码器应能满足以下标准要求。
快进fast forword
trick_mode_control字段中的值'000'。当该值出现时,它表示一个快进视频流并定义了PES分组标题中后续5位的含义。intra_slice_refresh位可以被设定为'1'以指出可能有丢失的宏块。解码器可以用前一个解码画面中相同位置的宏块来代替。表2-21中定义的field_id字段,表示应该显示哪个或哪些字段。frequency_truncation字段指出了可能包括的一个系数受限集合。该字段值的含义如表2-22所示。
慢动作 slow motion
trick_mode_control字段中的值'001'。当该值出现时,它表示一个慢动作视频流,并定义了PES分组标题中后续5个比特的含义。对渐进序列而言,该画面应被显示N×rep_cntrl时间,其中N定义如上。
对GB/T AAAA.2视频和GB/T XXXX.2视频渐进序列而言,该画面应被显示N×rep_cntrl时间,其中N定义如上。
对GB/T XXXX.2隔行序列而言,该画面应被显示N×rep_cntrl时间。若该画面是一个帧画面,则待显示的第一个字段在top_field_first为1时应该是顶字段,在top_field_first为0时,应该是底字段(参见GB/T XXXX.2)。该字段被显示N×rep_cntrl / 2时间。该画面的其它字段被显示N-N×rep_cntrl / 2时间。
冻结帧 freeze frame
trick_mode_control字段中的值'010'。当该值出现时,它表示冻结帧视频流,并定义了PES分组标题中后续5位的含义。表2-21中定义的field_id字段,表示应该显示哪个(些)字段。field_id字段指出了包含该字段的PES分组中开始的第一个视频存取单元,除非该PES分组包含0个有效负载字节。在后一种情况下,field_id字段指出了最近的前一个视频存取单元。
快倒 fast reverse
trick_mode_control字段中的值'011'。当该值出现时,它表示一个快倒视频流并定义了PES分组标题中后续5位的含义。intra_slice_refresh位可以被设定为'1'以指出可能有丢失的宏块。解码器可以用前一个解码画面中相同位置的宏块来代替。表2-21中定义的field_id字段,表示应该显示哪个或哪些字段。frequency_truncation字段指出了可能包括的一个系数受限集合。该字段值的含义如表2-22“系数选择值”所示。
慢倒 slow reverse
trick_mode_control字段中的值'100'。当该值出现时,它表示一个慢倒视频流并定义了PES分组标题中后续5位的含义。对GB/T AAAA.2视频和GB/T XXXX.2视频渐进序列而言,该画面应被显示N×rep_cntrl时间,其中N定义如上。
对GB/T XXXX.2隔行序列而言,该画面应被显示N×rep_cntrl时间。若该画面是一个帧画面,则待显示的第一个字段在top_field_first为1时应该是底字段,在top_field_first为'0'时,应该是顶字段(参见GB/T XXXX.2)。该字段被显示N×rep_cntrl/2时间。该画面的其它字段被显示N-N×rep_cntrl / 2时间。
23)字段标识字段 field_id
2位字段,表示应该显示哪个(些)字段。根据表2-21对其进行编码。
表2-21 field_id字段控制值
值 | 描 述 |
---|---|
‘00’ | 仅自顶向下播放 |
‘01’ | 仅自底向上播放 |
‘10’ | 播放所有帧 |
‘11’ | 保留 |
24)片内参考字段 intra_slice_refresh
1位标志。置'1'时表示PES分组的视频数据编码片中可能有丢失的宏块;置'0'时,表示上述情况可能不出现。更多的信息可参见GB/T XXXX.2。解码器可以用前一个解码画面中同一个位置的宏块来代替丢失的宏块。
25)频率截断字段 frequency_truncation
2位字段。指出在对PES分组中数据进行编码时可能用到受限系数集合。其值定义于表2-22。
表2-22 系数选择值
值 | 描 述 |
---|---|
‘00’ | 仅DC系数非0 |
‘01’ | 仅前三个系数非0 |
‘10’ | 仅前六个系数非0 |
‘11’ | 所有系数均可能非0 |
26)显示次数控制字段 rep_cntrl
5位字段,指出隔行画面中每一字段的显示次数或渐进画面显示次数。对隔行画面而言,顶字段或底字段是否应首先显示是视频序列标题中trick_mode_control字段和top_field_first字段的功能。该字段值不能为'0'。
27)附加版权信息字段 additional_copy_info
7位字段,包含与版权信息有关的专用数据。
28)前PES分组CRC字段 previous_PES_packet_CRC
16位字段。在对前一个PES分组(不包括该PES分组的标题)进行处理后,该字段包含一个在解码器的16个寄存器中生成0输出的CRC值。该CRC值与附录A中所定义的相类似,但具有以下多项式:
x16+x12+x5+1
注2: 该CRC值是为了用于网络维护,例如将有间隙性错误的源隔离开来,而不是为了供基本流解码器使用。它仅用于计算数据字节,因为在传输过程中PES分组标题数据可能被修改。
29)PES专用数据标志字段 PES_private_data_flag
1位标志。置'1'时表示PES分组标题中包含专用数据;置'0'时表示PES分组标题中无专用数据。
30)包标题字段标志字段 pack_header_field_flag
1位标志。置'1'时表示PES分组标题中有GB/T AAAA.2包标题或节目流包标题。若该字段在包含于节目流中的PES分组中,其值应为'0'。在传输流中,当值为'0'时表示PES标题中无包标题。
31)节目分组序列计数标志字段 program_packet_sequence_counter_flag
1位标志。值为'1'时表示PES分组有program_packet_sequence_counter,MPEG1_MPEG2_identifier和original_stuff_length字段。值为'0'时表示PES分组标题中无这些字段。
32)P-STD缓冲区标志字段 P-STD_buffer_flag
1位标志。置'1'时表示PES分组标题中有P-STD_buffer_scale和P-STD_buffer_size字段。值为'0'时表示PES标题中无这些字段。
33)PES扩展标志字段 PES_extension_flag_2
1位标志,置'1'时表示有PES_extension_field_length及相关字段。
34)PES专用数据字段 PES_private_data
16位字段。包含专用数据。这些数据与其前后的字段组合在一起时,不能与packet_start_code_prefix (0x000001)冲突。
35)包字段长度字段 pack_field_length
8位字段。表示pack_header_field()以字节为单位时的长度。
36)节目分组序列计数字段 program_packet_sequence_counter
7位字段。它是一个可选的计数器,随着来自于节目流或GB/T AAAA.1流的每一个后续的PES分组或传输流中具有单个节目定义的PES分组而递增,以提供与连续性计数器(参见2.4.3.2)相似的功能。它能用于检索节目流或原始GB/T AAAA.1流中的初始PES分组序列。该计数器在达到最大值后回卷为0。PES分组不能出现重复。因此,复合节目中任何两个连续的PES分组不应具有相同的program_packet_sequence_counter值。
37)MPEG1 MPEG2标识符字段 MPEG1_MPEG2_identifier
1位标志。置'1'时表示PES分组携带的信息来自于GB/T AAAA.1流;置'0'时表示PES分组携带的信息来自于节目流。
38)初始填充长度字段 original_stuff_length
6位字段。指定用于初始GB/T XXXX.1分组标题或初始GB/T AAAA.1分组标题中的填充字节数。
39)P-STD缓冲区比例字段 P-STD_ buffer_scale
1位字段。仅当该PES分组包含于节目流中时才有意义。它指出了用来解释后续P-STD_buffer_size字段的比例因子。若前面的stream_id表示一个音频流,该字段值应为'0';若前面的stream_id表示一个视频流,该字段值应为'1'。对于所有的其它流类型,其值可以为'0'或'1'。
40)P-STD缓冲区大小字段 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字节为单位来度量缓冲区的大小。因此:
if ( P-STD_buffer_scale == 0)
BSn=P-STD_buffer_size×128 (式2-16)
else
BSn=P-STD_buffer_size×1024 (式2-17)
当该字段被GB/T XXXX.1系统目标解码器收到后,其编码值立即生效。
41)PES扩展字段长度字段 PES_extension_field_length
7位字段。指出了跟在该字段之后在PES扩展字段中直到且包括任何保留字节的数据的字节长度。
42)填充字节字段 stuffing_byte
8位字段,其值恒定为'1111 1111'。可以由编码器插入以满足通道的需求等。解码器丢弃该字段。一个PES分组标题中只能出现32个填充字节。
43)PES分组数据字节字段 PES_packet_data_byte
该字段应该是来自于由分组的stream_id或PID所指定的基本流的连续数据字节。当基本流数据符合GB/T XXXX.2或GB/T XXXX.3时,该字段应该是与本标准的字节相对齐的字节。基本流的字节序应得到保持。该字段的字节数N由PES_packet_length字段规定。N应等于PES_packet_length减去在PES_packet_length字段的最后一个字节与第一个 PES_packet_data_byte间的字节数。
44)填料字节字段 padding_byte
8位字段,其值恒定为'1111 1111'。该字段被解码器丢弃。
5 例题
例1,PES包码流:
00 00 01 e0 21 33 80 80 05 2b 5f df 5c 95 71 84 …?!3€€.+_??q?
aa e4 e9 e9 ec 40 cc17 e0 68 7b 23 f6 89 df 90 ???@?.?h{#???
a9d4 be 74 b9 67 ad 34 6d f0 92 0d 5a 48 dd 13 ???t?g?4m??.ZH?.
1)前6字节分析:
00 00 01是起始码;//3B
e0是视频流;//1B,stream_id
21 33 是帧长度;//2B,PES包长度,与下面1B的包头长度不一样
2)PES包头识别标志两个字节的分析,它由多位组成:
接下来的两个80 80见下面的二进制解析;
第7、8字节的二进制如下:
1000 0000 1000 0000
按顺序解析:
第7个字节:
10 是标志位,必须是10;
00 是加扰控制字段,‘00’表示没有加密,剩下的01,10,11由用户自定义;
0 是优先级,1为高,0为低;
0 是数据对齐指示字段;
0 是版权字段;
0 是原始或拷贝字段。置’1’时表示相关PES分组有效负载的内容是原始的;'0’表示内容是一份拷贝;
第8个字节:
10 是PTS_DTS字段,这里是10,表示有PTS,没有DTS;
0 是ESCR标志字段,这里为0,表示没有该段;
0 是ES速率标志字段,,这里为0,表示没有该段;
0 是DSM特技方式标志字段,,这里为0,表示没有该段;
0 是附加版权信息标志字段,,这里为0,表示没有该段;
0 是PESCRC标志字段,,这里为0,表示没有该段;
0 是PES扩展标志字段,,这里为0,表示没有该段;
3)包头的长度(1B)分析:
05指出了可选字段的长度,前一字节指出了有无可选字段;
4)5B的PTS分析:
接下来的5字节是PTS;即
2b 5f df 5c 95,对应上图的PTS的五个矩形。具体对应数字查看文档即可,或者上面的图。
但是上面的例子没有告诉我们怎么从PES视频包截取h264数据。看下图:
要获取h264的数据,只需要对pes视频包这样操作:
nHeaderLen = 9 + (int)pBuffer[8];
pH264Buffer = pBuffer + nHeaderLen;//pH264Buffer此时指向h264的nalu