RTP包由两部分组成,RTP头和RTP载荷:
RTP头
RTP头的 结构如下:
代码结构:
typedef struct RtpHdr
{
uint8_t cc : 4, // CSRC count
x : 1, // header extend
p : 1, // padding flag
version : 2; // version
uint8_t pt : 7, // payload type
m : 1; // mark bit
uint16_t seq; // sequence number;
uint32_t ts; // timestamp
uint32_t ssrc; // sync source
}RtpHdr;
v : RTP协议的版本号,当前的RTP版本号为2.
p : 填充标志, 如果p=1,则在该报文的尾部填充一个或多个额外的八位组,它们不是有效载荷的一部分。
x : 扩展标志,如果x=1,则在RTP报头后跟有一个扩展报头.
cc : CSRC计数器,指示CSRC 标识符的个数
m : 标记,编辑帧的开始和结束,例如一帧的数据比较大,分成了好多RTP包进行传输,则最后一包m=1带包一帧数据传输完成,其他包m=0。
pt : 有效荷载类型,用于说明RTP报文中有效载荷的类型。一般都是用96-127.
seq : 用于标识发送者所发送的RTP报文的序列号,每发送一个报文,序列号增1, 不同的源有独立的计数体制,如音频视频的seq是独立的。这个序列号的初始值可以为0但是也可以为其它随机值,只要符合+1就行;
ts : 时间戳
ssrc : 用于标识同步信源。该信源的值是自定义的,可以使用time(NULL)赋值。
RTP载荷
RTP载荷根据帧数据的大小可以分为三种模式:单包,分片包,聚合包
单包:
帧数据和RTP头总数不大于网络传输MTU,就可以采用单包,一个RTP包包含一帧数据。
分片包:
帧数据比较大,在网络传输时由于MTU的限制就会采用分片策略,将一个数据帧分成多个RTP包进行传输。
分片传输有两种类型:
FU-A:
数据组成:
载荷会有两个字节的RTP载荷信息描述。
FU indication:
|0|1|2 |3|4|5|6|7|
|F|NRI| Type |
FU indication 的前三个bit(F NRI)和H264开始码后的第一个字节的前三个bit是一样的,H264类型字节:
nal_unit_head{
forbidden_zero_bit(1bit):禁止位 == N
nal_ref_idc(2bit): == NRI
nal_unit_type(5bit):NALU类型
};
Type是FU-A的类型28.
FU header
|0| 1|2|3|4|5|6|7|
|S|E|R| Type |
S:帧数据的第一个RTP包,该标志置1, 其他RTP包置0.
E:帧数据的最后一个RTP包,该标志置1,其他RTP包置0.
R: 设置为0
Type:H264的NALU的第一个字节的类型,及上图中的nal_unit_type(5bit):NALU类型
FU indication和FU header之后便是H264的载荷数据,该载荷数据去掉了H264的起始码和起始码后的第一个字节, 因为该字节的信息已经全部由FU indication和FU header包含。
实例:
H264数据:
00 00 00 01 65 xx xx xx xx xx xx xx...
RTP数据:
RTP头 FUindication FUheader xx xx xx xx xx xx xx...
FU-B:
相较于FU-A,该模式多了个两字节的DON(解码顺序号)字段, FU-B的FU indication的Type是29。一般都是用FU-A.
聚合包:
即帧数据比较少,多个帧打包在一个RTP包中。
现实中,单包,分片包比较常用。