视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
PS流的格式和解析总结
2020-11-27 14:49:49 责编:小采
文档
 对于PS流,最近因为工作需要,所以MPEG2中的PS流格式和解包过程进行了学习。

首先我们需要知道PS包流格式是怎么样的:

针对H2 做如下PS 封装:每个IDR NALU 前一般都会包含SPS、PPS 等NALU,因此将SPS、PPS、IDR 的NALU 封装为一个PS 包,包括ps 头,然后加上PS system header,PS system map,PES header+h2 raw data。所以一个IDR NALU PS 包由外到内顺序是:PSheader| PS system header | PS system Map | PES header | h2 raw data。对于其它非关键帧的PS 包,就简单多了,直接加上PS头和PES 头就可以了。顺序为:PS header | PES header | h2raw data。以上是对只有视频video 的情况,如果要把音频Audio也打包进PS 封装,也可以。当有音频数据时,将数据加上PES header 放到视频PES 后就可以了。顺序如下:PS 包=PS头|PES(video)|PES(audio),再用RTP 封装发送就可以了。

上面这是对数据流的一个整体封装格式,下面我们来看看每个封装中的每个部分:

首先是PSheader部分:(来自MPEG2标准中文文档)

语法

位数

助记符

pack_header() {

pack_start_code

32

bslbf

'01'

2

bslbf

system_clock_reference_base[32..30]

3

bslbf

marker_bit

1

bslbf

system_clock_reference_base[29..15]

15

bslbf

marker_bit

1

bslbf

system_clock_reference_base[14..0]

15

bslbf

marker_bit

1

bslbf

system_clock_reference_extension

9

uimsbf

marker_bit

1

bslbf

program_mux_rate

22

uimsbf

marker_bit

1

bslbf

marker_bit

1

bslbf

reserved

5

bslbf

pack_stuffing_length

3

uimsbf

for (i=0;i<pack_stuffing_length;i++){

stuffing_byte

8

bslbf

}

if (nextbits()==system_header_start_code) {

system_header()

}

}

节目流包(PS header)中各字段的语义定义:

包起始码字段 pack_start_code

值为'0000 0000 0000 0000 0000 0001 1011 1010' (0x000001BA)的位串,用来标志一个包的开始。

系统时钟参考字段 system_clock_reference_base,system_clock_reference_extenstion

系统时钟参考(SCR)是一个分两部分编码的42位字段。第一部分system_clock_reference_base是一个长度为33位的字段,其值SCR_base(i)由式2-19给出;第二部分system_clock_reference_extenstion是一个长度为9位的字段,其值SCR_ext(i)由式2-20给出。SCR字段指出了基本流中包含ESCR_base最后一位的字节到达节目目标解码器输入端的期望时间。

SCR字段的编码频率要求见2.7.1。

标记位字段 marker_bit

1位字段,取值'1'。

节目复合速率字段 program_mux_rate

一个22位整数,规定P-STD在包含该字段的包期间接收节目流的速率。其值以50字节/秒为单位。不允许取0值。该字段所表示的值用于在2.5.2中定义P-STD输入端的字节到达时间。该字段值在本标准中的节目多路复合流的不同包中取值可能不同。

包填充长度字段 pack_stuffing_length

3位整数,规定该字段后填充字节的个数。

填充字节字段 stuffing_byte

8位字段,取值恒为'1111 1111'。该字段能由编码器插入,例如为了满足通道的要求。它由解码器丢弃。在每个包标题中最多只允许有7个填充字节。

它的定义字节顺序,如下所示:

4B的包起始码:

byte 0

byte 1

byte 2

byte 3

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

0000 0000 0000 0000 0000 0001
start code

1011 1010
PACK identifier

PACK identifier -- 0xBA

系统时钟基准(SCR-System Clock Reference)的基本部分、SCR的扩展部分:

byte 4

byte 5

byte 6

byte 7

byte 8

byte 9

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

01

SCR 32..30

1

SCR 29..15

1

SCR 14..00

1

SCR_ext

1

PS复用速率:

byte 10

byte 11

byte 12

byte 13

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

7

6

5

4

3

2

1

0

Program_Mux_Rate

1

1

reserved

pack_stuffing_length

==============================================================

接下来看看PS system header(即PS系统头:节目流系统标题)部分的定义:(来自MPEG-2标准文档)

表2-34 节目流系统标题

语 法

位数

助记符

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

}

}

系统标题中各字段的语义定义:

系统标题起始码字段 system_header_start_code

取值'0000 0000 0000 0000 0000 0001 1011 1011' (0x000001BB)的位串,指出系统标题的开始。

标题长度字段 header_length

16位字段。指出该字段后的系统标题的字节长度。在本规范将来的扩充中可能扩展该字段。

速率界限字段 rate_bound

22位字段,取值不小于编码在节目流的任何包中的program_mux_rate字段的最大值。该字段可被解码器用于估计是否有能力对整个流解码。

音频界限字段 audio_bound

6位字段,取值是在从0到32的闭区间中的整数,且不小于节目流中解码过程同时活动的GB/T XXXX.3和GB/T AAAA.3音频流的最大数目。在本小节中,若STD缓冲区非空或展现单元正在P-STD模型中展现,则GB/T XXXX.3和GB/T AAAA.3音频流的解码过程是活动的。

固定标志字段 fixed_flag

1位标志位。置'1'时表示比特率恒定的操作;置'0'时,表示操作的比特率可变。在恒定比特率的操作期间,复合的GB/T XXXX.1流中的system_clock_reference字段值应遵从下面的线性公式:

SCR_base(i)=((c1×ic2) p 300) % 233 (2-22)

SCR_ext(i)=((c1×ic2) p 300) % 300 (2-23)

其中:

c1 对所有i均有效的实型常数;

c2 对所有i均有效的实型常数;

i 在GB/T XXXX.1复合流中包含任何system_clock_reference字段的最后一位的字节索引。

CSPS标志字段 CSPS_flag

1位字段。置'1'时,节目流符合2.7.9中定义的。

系统音频锁定标志字段 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

系统视频锁定标志字段 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'。

SCFRsystem_clock_frequency / frame_rate_in_the_P-STD (2-25)

标准帧速率(Hz)

23.976

24

25

29.97

30

50

59.94

60

SCFR

1 126 125

1 125 000

1 080 000

900 900

900 000

540 000

450 450

450 000

比率SCFR的值是精确的。对于23.976,29.97或59.94帧/秒的标准速率,实际的帧速率与标准速率略有不同。

视频界限字段 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视频流的解码过程是活动的。

分组速率标志字段 packet_rate_restriction_flag

1位标志位。若CSPS标识为'1',则该字段表示2.7.9中规定的哪个适用于分组速率。若CSPS标识为'0',则该字段的含义未定义。

保留位字段 reserved_bits

7位字段。被保留供ISO/IEC将来使用。它的值应为'111 1111',除非ISO/IEC对它作出其它规定。

流标识字段 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。

P-STD缓冲区界限比例字段 P-STD_buffer_bound_scale

1位字段。表示用于解释后续P-STD_buffer_size_bound字段的比例系数。若前面的stream_id表示一个音频流,则该字段值为'0'。若表示一个视频流,则该字段值为'1'。对于所有其它的流类型,该字段值可以为'0'也可以为'1'。

P-STD缓冲区大小界限字段 P-STD_buffer_size_bound

13位无符号整数,取值不小于节目流中流n的所有分组的P-STD缓冲区大小BSn的最大值。若P-STD_buffer_bound_scale的值为'0',则该字段以128字节为单位来度量缓冲区大小的边界。若P-STD_buffer_bound_scale的值为'1',则该字段以1024字节为单位来度量缓冲区大小的边界。因此:

if (P-STD_buffer_bound_scale = = 0)

BSnP-STD_buffer_size_bound×128

else

BSnP-STD_buffer_size_bound×1024

所以对于系统头部的解析,我们一般只要先首先判断是否存在系统头(根据系统头的起始码0x000001BB),然后我们读取系统头的头度,即PS SYSTEM HEADER LENGTH部分,然后根据头部的长度,跳过PS系统头。进入下一个部分,即PS 节目流映射头。

==========================================================

接着看看PS流的节目映射流部分(节目流映射)定义:(来自MPEG-2标准文档)

表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

}

节目流映射中各字段的语义定义:

分组起始码前缀字段 packet_start_code_prefix

24位码。它和跟随其后的map_stream_id共同组成一个分组起始码以标志分组的开始。该字段是值为'0000 0000 0000 0000 0000 0001' (0x000001)的位串。

映射流标识字段 map_stream_id

8位字段,值为0xBC。

节目流映射长度字段 program_stream_map_length

16位字段。指示紧跟在该字段后的program_stream_map中的字节数。该字段的最大值为1018(0x3FA)。

当前下一个指示符字段 current_next_indicator

1位字段。置'1'时表示传送的节目流映射当前是可用的。置'0'时表示传送的节目流映射还不可用,但它将是下一个生效的表。

节目流映射版本字段 program_stream_map_version

5位字段,表示整个节目流映射的版本号。一旦节目流映射的定义发生变化,该字段将递增1,并对32取模。在current_next_indicator为'1'时,该字段应该是当前适用的节目流映射的版本号;在current_next_indicator为'0'时,该字段应该是下一个适用的节目流映射的版本号。

节目流信息长度字段 program_stream_info_length

16位字段,指出紧跟在该字段后的描述符的总长度。

标记位字段 marker_bit

1位字段,取值为'1'。

基本流映射长度字段 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可以得到后面定义了几个流类型信息。)

流类型字段 stream_type

8位字段,根据表2-29规定了流的类型。该字段只能标志包含在PES分组中的基本流且取值不能为0x05。

(这里我们暂时根据国标GB28181中的定义可以知道

1、MPEG-4 视频流: 0x10;

2、H.2 视频流: 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打包的流编码类型,就根据这个字段来判断。)

基本流标识字段 elementary_stream_id

8位字段,指出该基本流所在PES分组的PES分组标题中stream_id字段的值。

(这个字段的定义,其中0x(C0~DF)指音频,0x(E0~EF)为视频)

基本流信息长度字段 elmentary_stream_info_length

16位字段,指出紧跟在该字段后的描述符的字节长度。(即这个类型的流描述长度。这个后面的字段后面的指定长度不在elementary_stream_map_length指定的范围类。)

CRC 32字段 CRC_32

32位字段,它包含CRC值以在处理完整个节目流映射后在附录A中定义的解码器寄存器产生0输出值。

对于这个字段的解析,我们需要取值0x000001BC的位串,指出节目流映射的开始,暂时不需要处理,读取Header Length直接跳过即可,如果需要解析流编码类型,必须详细解析这个字段。

=================PES包=============================

接下来我们来分析下PES包的内容:PES包=PES header+code raw data;

先看下PES header:(来自http://www.gxlcms.com/)

接着我们来看看解析步骤:(来自MPEG-2标准文档)

表2-17 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

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(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’){

下载本文
显示全文