h265、h264的RTP包封装区别
一、NAL单元
1、h264 NAL单元
/*
* h264 nal头部(1字节)
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |F|NRI| Type | NAL payload ……
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
名称 bit 备注
禁止位 1 为0的时候合法
优先级 2 数值越大优先级越高,表示越重要
NAL类型 5 一共有32种
(sps是7, pps是8, sei是6. I帧是5 P帧是1)
2、h265 NAL单元
/*
* h265 nal头部(2字节)
* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |F| Type | layerID | TID | NAL payload ……
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
名称 bit 备注
禁止位 1 为0的时候合法
NAL类型 6 一共有64种 一半为视频数据类型 一半为非视频数据类型
(vps是32, sps是33, pps是34, 前缀sei是39. IDR是19或20 后置图像帧是1)
视频层ID 6 目前设为0,以后扩展
TID 3 目前通常设为1,nuh_temporal_id_plus1.
可以看出来265的nal头部要比264多出了1字节(为了以后扩展多视点、立体视频源),封装rtp包时要注意相应数据的提取。
265的NAL类型也比264的多了一倍,主要区别是比264新增了vps(视频参数集),完整I帧的内容为vps+sps+pps+sei+I帧。
二、rtp包封装
1、小包封装(如sps、pps、sei等)
这些长度较短(小于1400字节)的NAL单元数据,只要在读取完该单元后,去掉起始码(0x 00 00 01或0x 00 00 00 01),就可以直接作为rtp负载内容加载到rtp头部后发送出去了(这里关于rtp头部内容就不说了,对于264和265 rtp头部的内容设置方法一样)。需要注意的地方是该rtp包的时间戳一定要设置,如h264的I帧前的sps、pps、sei,这三个的时间戳应该一致,而且和I帧的时间戳一致(本来编码的时候就是一起生成的)。
/* RTP包的封装格式
* RTP包头部最短为12字节(不用设置CSRC,即CC=0的时候)
* 0 1 2 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |V=2|P|X| CC |M| PT | sequence number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | timestamp |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | synchronization source (SSRC) identifier |
* +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
* | contributing source (CSRC) identifiers |
* : .... :
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | rtp负载内容 |
* : .... :
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
2、分包封装(如I帧等)
通常的I帧和P帧数据都是比较大的(比1400字节要大得多),这时候就需要分包处理,因为经过udp或tcp打包后,过长的数据(超过1500字节)到了ip数据报打包的时候会被分包,到了接收端会产生粘包等问题。
这里介绍一般常用的 FU-分片包
①264的分片包
/* 264的分片包和小包封装的差别是 原先的NAL头(1字节) 变为了现在的FU indicator+FU header(共2字节)
* 0 1 2
* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | FU indicator | FU header | FU payload ... |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/*
* FU Indicator
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* |F|NRI| Type | 这里的Type变为分片类型(28)
* +-+-+-+-+-+-+-+-+
*/
/*
* FU Header
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* |S|E|R| Type | 原先NAL头部的类型保存在这里的Type
* +-+-+-+-+-+-+-+-+
*/
FU Indicator其实和NAL头部几乎一样,就是nal类型变成了分片包的类型28,原先的nal类型保存到了FU Header中的Type。
FU Header中:
S(start) 为1时表示分片包的第一包
E( end) 为1时表示分片包的最后一包
R 这个值一直为0(保留)
因此可以知道,不是第一包或最后一包的时候 S、E都设为0。
FU payload就是原先nal数据的一部分(分片)数据了。
这些数据都填充好后就可以放到rtp包的负载内容发送了。
②265的分片包
/* h265的分片包,大致和264的一样,总的头部变为了3字节
* 0 1 2
* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | FU indicator | FU header | FU payload ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/*
* FU Indicator
* 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |F| Type | layerID | TID | 这里的Type=49(分片包类型)
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/*
* FU Header
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-+-+-+-+
* |S|E| Type | 原先的NAL类型同样保存在了这里
* +-+-+-+-+-+-+-+-+
*/
FU Indicator的填充大致和上面264的一样,修改的只是类型,而原先264中FU Header中的R被删掉了,S、E用法不变,这里的Type(6字节)也是填写该NAL单元原本的类型。
③小要点
每个RTP分片包的时间戳设置要注意,同一帧NAL单元的各个分片包时间戳应设为一致。
在264流中经常出现的是 I帧(类型5)、P帧(类型1)、B帧(较少接触),而在265流中目前接触到的是 I帧(类型19或20)、非I帧【包括前置图像帧(类型0),后置图像帧(类型1)】
三、RTSP传输时SDP内容差异
1、rtsp传输 h264 最简易的sdp内容(DESCRIBE请求回复)
v=0
o=- 91575441432 1 IN IP4 192.168.1.241
t=0 0
a=control:*
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=control:track0
2、rtsp传输 h265 最简易的sdp内容(DESCRIBE请求回复)
v=0
o=- 91575441714 1 IN IP4 192.168.1.241
t=0 0
a=control:*
m=video 0 RTP/AVP 96
a=rtpmap:96 H265/90000
a=control:track0
可以看出来sdp信息基本一致,变化的只是H264->H265(个人认为96是对应h264的,但是不修改同样可以推拉流h265,可在vlc播放)
其他的流程步骤基本和h264的一样。
下面放一张h265的rtsp推流抓包图,一开始的发送顺序是 vps、sps、pps、sei、I帧各分片、非I帧各分片,依次循环。
想了解更多RTSP相关知识可以参考该系列(入门级):
https://blog.csdn.net/weixin_42462202/article/details/98986535