H264中的NAL、NALU,以及RTP封装方式
H.264主要由视频编码层(VCL)和网络适配层(NAL)组成
NAL包格式如下1
2
3
4
5 NAL Header NALU
0 1 2 3 4 5 6 7 8 9
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+
|F|NRI| Type | a single NAL unit ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-++-+-+-+-+-+-+-+-+
NAL包由头部和NALU组成,VCL有效载荷数据被封装在NALU中1
2
3
4
5
6
7
8
9
10
11
12struct NalHeader
{
uint8_t type : 5; // 5 位,指出NALU 的类型 SEI:6, SPS: 7, PPS: 8
uint8_t nal_ref_idc : 2; // 2 位,用来指示该NALU 的重要性等级。值越大,表示当前NALU越重要。具体大于0 时取何值,没有明确规定
uint8_t forbidden_zero_bit : 1; // 1 位,初始为0。当网络识别此单元存在比特错误时,可将其设为 1,以便接收方丢掉该单元
};
struct NalPacket
{
struct NalHeader nalHeader;
uint8_t nalu[0]; // NAL unit
};
对于NAL包,通常放入RTP的有效载荷中进行传输
对于因特网IP层规定最大传输单元(MTU)为1500字节,IP报头20字节,UDP报头8字节,TCP报头20-60字节,RTP报头12字节
则RTP有效载荷最大长度1460(UDP)-1408(TCP)字节
我们可灵活地定义最大长度为1400
对于不超过1400的NAL包,我们可以直接将其装入RTP载荷
当一个NAL大小超过1400时,我们需要对NAL进行分片处理
分片格式如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 0 1 2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | FU payload ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
FU Indicator
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
FU Header
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
对于FU Indicator,F和NRI都直接继承了NAL包的相同数据,而Type将直接置28
对于FU Header,包含三个标记位
S用于标记分片的第一个RTP包,第一个包置1,和0x80做&运算
E用于标记分片的最后一个RTP包,最后一个包置1,和0x40做&运算
R置0
Type直接继承NAL包的Type,可首先直接保留后5位,和0x1F做&运算,后对S、E、R做处理1
2
3
4
5
6
7
8
9
10// FU Indicator
// F置0
rtpPacket->payload[0] = (naluType & 0x60) | 28;
// FU Header (非第一个或最后一个RTP包,只要后5位,S E R全置0)
rtpPacket->payload[1] = (naluType & 0x1F);
if (i == 0) // 第一个 S置1
rtpPacket->payload[1] |= 0x80;
else if (remainPktSize == 0 && i == pktNum - 1) // 最后一个 E置1
rtpPacket->payload[1] |= 0x40;