音视频封装 - PS 封装解析示例
文章 音视频封装 - PS 封装格式 中分析了 PS 流的结构,但文章中中涉及到的元素过多,难以理解,故通过一段真实的码流的分析来加强对 PS 流结构的理解。
注 1:本文中的
PS码流 A 截取自一段经ffmpeg转码的网络视频,码流 B 截取自海康摄像机的GB28181码流。 注 2:本文中的数字一般为十进制表示,如果是其他进制数据,会在数字后携带(2)代表二进制,在数字前添加0x代表十六进制 注 3:截图中的码流信息均使用十六进制
PS Stream A
PS Header
PS 码流由多个 PS Pack 组成,而 PS Pack 的起始符为 0x00 00 01 ba。
接下来跳过 9 个字节,看第 10 个字节,其最后三位数字代表拓展内容的长度,此包中该位置后三位为 0,代表无拓展内容。
如果该位置后三位非 0,参见 PS Header Extern
继续读取数据,如果遇到了 0x00 00 01 bb,就代表到了码流中 PS System Header 部分。
码流中有可能会读取到
0x00 00 01 bc,参见 Program Stream Map
PS System Header
System Header 当且仅当数据包为第一个数据包时才存在,以 0x00 00 01 bb 的开始。
之后紧跟着的 0x00 0c 两个字节表示 System Header 的长度,换算为十进制,即为 12 个字节。
之后跳过 6 字节数据,之后如果 nextbit == 1,则该位置为一个 stream_id,stream_id 为 0xe0,指示该 PS 码流中包含视频流。
该部分内容 3 字节一个循环,下一个循环中 nextbit == 1,则该位置也为一个 stream_id,stream_id 为 0xc0,指示该 PS 码流中包含音频流。
至此 nextbit == 0,说明该循环结束,读取的长度也与上述 PS System Header 的长度值 12 一致。
继续读取数据,遇到了 0x00 00 01,PS 流中这三个字符可视为不同数据的分隔符,而其下一位数据指示了后续数据的类型。
PES Pack
如果读取 PS 流中遇到了 0x00 00 01 e0 或 0x00 00 01 c0,就代表读取到了 PES Pack。
PES Pack 分为两个部分,一部分是 Header,一部分是 Payload,Header 用于存储一些描述信息,而 Payload 部分为其存储的原始数据,PES 可能有多个。
0x00 00 01 e0 代表遇到的是一个 PES Video Pack,如果是 0x00 00 01 c0,代表遇到的是一个 PES Audio Pack。
再之后的 0x07 dc 表示长度,即其后的 2012 字节为数据内容长度。
之后参考 PES Pack 结构来解析数据,可参见 音视频封装 - PES 封装格式。
跳过 1 字节字段后,下一个字段为 PTS_DTS_flags 字段,占位 2bits。
PTS_DTS_flags:PTS和DTS标志位,占位 2bit;10(2)表示首部有PTS字段,11(2)表示有PTS和DTS字段,00(2)表示都没有,01(2)被禁止。PTS- 显示时间戳(Presentation Time Stamp),用来表示显示单元出现在系统目标解码器的时间。DTS- 解码时间戳(Decoding Time Stamp),用来表示将存取单元全部字节从解码缓存取走的时间。 如果PTS_DTS_flags不是00(2),就代表存在PTS或DTS参见 PTS_DTS_flags
故该 PES Pack 中无 PTS 和 DTS 字段
后面 6bits 也是 PES 包中可选字段的 Flag 参数,由上图可知,该 PES Pack 无这些可选字段。
继续读取数据,下一个字段就为 PES_header_data_length,代表 PES Header 中可选字段和填充字段的长度,占位 8bit,该 PES Pack 中该值为 03,即该 PES Pack 中,该字段之后 3 个字节为 PES Header 字段,剩余数据均为数据 Payload。
PS Stream B
PS Header Extern
如果 PS Pack 第 10 字节后三位不是 000(2),则代表其后有相应长度的拓展内容,比如下图码流中就有 6 字节拓展内容:
Program Stream Map
如果码流中遇到了 0x00 00 01 bc,则之后的数据为 Program Stream Map 数据。
Program Stream Map,即 PSM,其格式如下图所示:
可以看出,0x00 00 01 bc 后两位代表该部分数据长度,该 PSM 数据长度为 0x62,转化为十进制,即 98 字节。
结合上边的 PSM 结构,自 0x00 62 后,跳过两个字节的固定内容,就到了两个字节的 program_stream_info_length,其值为 0x00 2c,说明其后跟着的 descriptor 共占 44 字节。
跳过该长度信息和 44 字节后,接下来的值 0x00 2c 为 element_stream_map_length(基本流映射长度),也就是 44 字节,它表示接下来的 44 字节都是用来描述原始流信息的。
接下来进入了原始流描述的第一次循环,第一个字节 stream_type 值为 0x24,根据《GB/T 28181-2016 修改补充文件》可知其为 H.265 码流。
a) MPEG-4 视频流: 0x10
b) H.264 视频流: 0x1B
c) SVAC 视频流: 0x80
d) H.265 视频流: 0x24
e) G.711A 音频流: 0x90
f) G.711U 音频流: 0x91
g) G.722.1 音频流: 0x92
h) G.723.1 音频流: 0x93
i) G.729 音频流: 0x99
j) SVAC 音频流: 0x9B
之后的 0xe0 表示其为视频流。
之后的 0x00 10 代表该视频流描述 element_stream_info_length 占 16 字节。
同理,接着循环下去,接下来的字段 0x91 和 0xc0,代表着其为 G.711U 编码的音频流,并携带有 0x0c 即 12 字节的描述信息。
继续循环,接下来的字段 0xbd bd,是海康的私有数据,携带有 0 字节的描述信息。
继续循环,接下来的字段 0xbf bf,也是海康的私有数据,携带有 0 字节的描述信息。
以上四次循环后,他们占用的字节数已经等于 44,跟 element_stream_map_length 值相等。
接下来的内容就是 CRC_32,4bits,从以下码流中可以看出,海康 IPC 中没有对这些字节进行计算。
PTS_DTS_flags
在很多码流中,都会存在 PTS 或 DTS 字段,此时 PTS_DTS_flags 字段就会置位,比如以下码流中,PTS_DTS_flags 为 00(2)
对比 音视频封装 - PES 封装格式 中 PES 码流结构可知,之后由 5 字节的 PTS 数据
ES Stream
经过以上步骤分析出 PS 的各层封装后,就能将封装中的 ES 码流提取出来:
参考资料
- [1] 文中
PS码流A视频源文件:点击下载 - [2] 文中
PS码流B视频源文件:点击下载 - [3] 音视频封装 - PS 封装格式
- [4] 音视频封装 - PES 封装格式