RTSP中TS流结构分析
- UDP数据包整体结构
- RTP Header结构
- TS Header结构
- PAT结构
- PMT结构
- PES Header结构
- ES层
- Wireshark抓取TS包
UDP数据包整体结构
在使用UDP传输TS流时,整个数据包结构如下:
RTP Header结构
RTP结构如下图:
各字段的含义如下:
字段 | 含义 |
---|---|
V | RTP协议的版本号,2位,当前协议版本号为2 |
P | 填充标志,1位,如果P=1,则在该报文的尾部填充一个或多个额外的八位组 |
X | 扩展标志,1位,如果X=1,则在RTP报头后跟有一个扩展报头 |
CC | CSRC计数器,4位,指示CSRC 标识符的个数 |
M | 标记,1位,不同的有效载荷有不同的含义,对于视频,标记一帧的结束。 |
PT | 有效荷载类型,7位,用于说明RTP报文中有效载荷的类型,如GSM音频、JPEM图像等,在流媒体中大部分是用来区分音频流和视频流的,这样便于客户端进行解析。 |
Sequence number | 序列号,16位,用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1。这个字段当下层的承载协议用UDP的时候,网络状况不好的时候可以用来检查丢包。同时出现网络抖动的情况可以用来对数据进行重新排序,序列号的初始值是随机的,同时音频包和视频包的sequence是分别记数的。 |
timestamp | 时戳(Timestamp),32位,必须使用90 kHz 时钟频率。时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。 |
SSRC | 同步信源(SSRC)标识符:32位,用于标识同步信源。该标识符是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。 |
CSRC | 特约信源(CSRC)标识符:每个CSRC标识符占32位,可以有0~15个。每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。 |
TS Header结构
TS结构如下图:
各字段的含义如下:
字段 | 含义 |
---|---|
sync_byte | 8位,同步字节,固定为0x47 |
transport_error_indicator | 1位,传输错误指示符,表明在ts头的adapt域后有一个无用字节,通常都为0,这个字节算在adapt域长度内 |
payload_unit_start_indicator | 1位,表示TS包的有效净荷带有PES或PSI数据的情况;当TS包的有效净荷带有PES包数据时,payload_unit_start_indicator为1,表示TS包的有效净荷以PES包的第一个字节开始;为0,表示TS包的开始不是PES包;当TS包带有PSI数据时,payload_unit_start_indicator为1,表示TS包带有PSI部分的第一个字节,即第一个字节带有指针pointer_field,为0表示TS包不带有一个PSI部分的第一个字节,即在有效净荷中没有指针pointer_field;对于空包的包,play_unit_start_indicator应该置为0; |
transport_priority | 1位,传输优先级,0为低优先级,1为高优先级,通常取0 |
pid | 13位,PID值 |
transport_scrambling_control | 2位,传输加扰控制,00表示未加密 |
adaptation_field_control | 2位,是否包含自适应区,‘00’保留;‘01’为无自适应域,仅含有效负载;‘10’为仅含自适应域,无有效负载;‘11’为同时带有自适应域和有效负载。 |
continuity_counter | 4位,递增计数器,从0-f,起始值不一定取0,但必须是连续的 |
PAT结构
PAT说明了此TS流中有多少个节目以及每个节目对应的PID,每个节目都有一个对应的PMT表,然后根据此PID可以查找PMT,PMT有该节目对应的视频PID和音频PID以及对应的编码格式。
字段 | 含义 |
---|---|
table_id | 8位,PAT表固定为0x00 |
section_syntax_indicator | 1位,固定为1 |
zero | 1位,固定为0 |
reserved | 2位,固定为11 |
section_length | 12位,后面数据的长度 |
transport_stream_id | 16位,传输流ID,固定为0x0001 |
reserved | 2位,固定为11 |
version_number | 5位,版本号,固定为00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1位,固定为1,表示这个PAT表可以用,如果为0则要等待下一个PAT表 |
section_number | 8位,固定为0x00 |
last_section_number | 8位,固定为0x00 |
开始循环 | |
program_number | 16位,节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PMT |
reserved | 3位,固定为111 |
PID | 13位,节目号对应的PID值,解析出此PID后,就可以找到PMT表了 |
结束循环 | |
CRC32 | 32位,前面数据的CRC32校验码 |
剩余的数据 | 未满188byte,填充0xff |
PMT结构
字段 | 含义 |
---|---|
table_id | 8位,PMT表取值随意,0x02 |
section_syntax_indicator | 1位,固定为1 |
zero | 1位,固定为0 |
reserved | 2位,固定为11 |
section_length | 12位,后面数据的长度 |
program_number | 16位,频道号码,表示当前的PMT关联到的频道 |
reserved | 2位,固定为11 |
version_number | 5位,版本号,固定为00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1位,固定为1 |
section_number | 8位,固定为0x00 |
last_section_number | 8位,固定为0x00 |
reserved | 3位,固定为111 |
PCR_PID | 13位,PCR(节目参考时钟)所在TS分组的PID,指定为视频PID |
reserved | 4位,固定为1111 |
program_info_length | 12位,0x000表示没有 |
开始循环 | |
stream_type | 8位,流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03 |
reserved | 3位,固定为111 |
elementary_PID | 13位,与stream_type对应的PID |
reserved | 4位,固定为1111 |
ES_info_length | 12位,描述信息,0x000表示没有 |
结束循环 | |
CRC32 | 32位,前面数据的CRC32校验码 |
剩余的数据 | 未满188byte,填充0xff |
PES Header结构
字段 | 含义 |
---|---|
pes start code | 3byte,固定为0x000001 |
stream id | 1byte,音频取值(0xc0-0xdf),通常为0xc0,视频取值(0xe0-0xef),通常为0xe0 |
pes packet length | 2byte,后面pes数据的长度,0表示长度不限制,只有视频数据长度会超过0xffff |
flag | 1byte,通常取值0x80,表示数据不加密、无优先级、备份的数据 |
flag | 1byte,取值0x80表示只含有pts,取值0xc0表示含有pts和dts |
pes data length | 1byte,后面数据的长度,取值5或10 |
pts | 5byte,33bit值,显示时间戳 |
dts | 5byte,33bit值,解码时间戳 |
ES层
ES层包含音视频数据
H.264视频数据:
H.264数据由多个nalu构成,nalu包括nalu header,nalu data,nalu 之间用0x00000001(帧开始)或0x000001(帧中)分割。h.264的数据是由slice组成,slice的内容包括:视频,sps,pps等。nalu header中的type决定了后面的数据内容。
字段 | 含义 |
---|---|
F | 1位,forbidden_zero_bit,h.264规定必须取0 |
NRI | 2位,nal_ref_idc,取值0~3,指示这个nalu的重要性,I帧、sps、pps通常取3,P帧通常取2,B帧通常取0 |
type | 5位,参考下表 |
nal_unit_type | 说明 |
---|---|
0 | 未使用 |
1 | 非IDR图像,IDR指关键帧 |
2 | 片分区A |
3 | 片分区B |
4 | 片分区C |
5 | IDR图像片,即关键帧 |
6 | 补充增强信息单元(SEI) |
7 | SPS序列参数集 |
8 | PPS图像参数集 |
9 | 分解符 |
10 | 序列结束 |
11 | 码流结束 |
12 | 填充 |
13~23 | 保留 |
24~31 | 未使用 |
打包es层数据时pes头和es数据之间要加入一个type=9的nalu,关键帧slice前必须要加入type=7和type=8的nalu,而且是紧邻。
Wireshark抓取TS包
如下图: