欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

H.264 RTP封包规则及RTSP抓包分析

程序员文章站 2022-07-07 09:03:00
...

1.H264中NAL unit Header简介

原始码流都是由一个个的 NALU(Network Abstract Layer)网络抽象层 连续组成,其中NALU=[StartCode] + [NALU Header] + [NALU Payload]组成,其中

StartCode:表示一个NALU的开始,一般情况下是以4字节“00 00 00 01”或者3字节“00 00 01”,一般4字节居多。

NALU Header:表示一组视频编码的头部信息,具体下面分析。

Payload:表示原始字节序列的有效载荷。

NAL unit Header

下面对比一下原始码流的H264和H265的NAL unit header。其中H264的Nal Unit头是一个字节,具体每个位表示如下:

H264 NAL Header

--------------------
|0|1|2|3|4|5|6|7|
--------------------
|F|NRI|   Type  | Payload
F:forbidden_zero_bit NRI:nal_reference_idc type:nal_unit_type
1 bit 2 bits 5 bits
H.264 规范中规定了这一位必须为 0 VCL可以表征参考帧属性,参考帧非0,非参考帧0,Non-VCL 表征解码时的可丢弃与否,如SPS PPS不可丢弃 为1,SEI可丢弃为0 当前NAL的类型

VCL: Video Coding Layer

解析原始码流的H264 type方式:

//定义header[4]为去除掉“00 00 00 01”之后的那个字节
int type = header[4] & 0x1F

H.264常用的type类型

type type value
sps 7
pps 8
sei(增强信息帧,可以没有) 6
常用I帧 5
常用P帧 1

这里说明一下,有些编码是没有SEI帧的,SEI这个增强信息帧中一般填入一些,人脸识别的坐标等信息,也可以自定义一些信息。一般的流都是按照sps,pps,I ,p,I,P……的顺序发送的,其中流媒体发送的时候为了能够在任何点都能够解析编码,通常会在I帧前添加sps和pps两帧的单独包或者聚合包,之后再发送I帧。

2.RTP header 简介

RTP包发送的时候,就是将原始码流中的“00 00 00 01”去掉,按照封装格式封装,然后为每一个封装添加RTP header,再发送出去。下面我们介绍下其格式:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |V=2|P|X|  CC   |M|     PT      |       sequence number         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                           timestamp                           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |           synchronization source (SSRC) identifier            |
    +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
    |            contributing source (CSRC) identifiers             |
    |                             ....                              |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

完整的的header如上所示,来个表格介绍下都表示的含义:

位类型 位数 解释
V 2 bit Version版本号,通常为2,二进制位“10”
P 1 bit Padding填充标志,如果P=1,一般会在报文后边添加不是有效载荷的多个八位数组
X 1 bit 扩展标志
CC 4 bits CSRC计数器,指示CSRC 标识符的个数
M 1 bit Marker bit标志位,一般为1时,表示一帧的最后一个包
PT 7 bits Payload type效荷载类型,一般用来区分视频和音频的,例如视频为96,音频为97
SN 16 bits Sequence number***
timestamp 32 bits 时间戳,必须使用90 kHz 时钟频率。反映了该RTP报文的第一个
八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动,并进行同步控制。
SSRC 32 bits 同步信源,是随机选择的,参加同一视频会议的两个同步信源不能有相同的SSRC。
多个CSRC 每个32 bits 特约信源,此处有上面的CC决定数目,CSRC标识了包含在该RTP报文有效载荷中的所有特约信源。

其中CC 很多情况下为0,即没有CSRC信息,所以通常情况下RTP header是由12个字节组成的。

如果使用UDP传输的话,直接使用这个RTP header;但是在tcp传输的时候会在header前在添加四个字节:

​ [magic number]+[channel number]+[data length]

说明一下

magic number channel number data length
1个字节 1个字节 2个字节
就是一个“$”符号 00–视频RTP,01–视频RTCP
02–音频RTP,03–音频RTCP
RTP header+NAL unit的总长度

我们来抓取一帧数据来说明一下,如下图:

H.264 RTP封包规则及RTSP抓包分析
其中Payload是从67开始,就是数据的封装,具体怎么封装这个载荷的呢,下面就来介绍一下。

3.H.264 RTP 载荷封装格式

Type的类型说明

这里我们先说下Type的类型,RTP中增加了H264中23中以外的格式,先来看下这个header头部


    +---------------+
    |0|1|2|3|4|5|6|7|
    +-+-+-+-+-+-+-+-+
    |F|NRI|  Type   |
    +---------------+

这个Type类型用于区分封装的类型,H.264的帧类型1-23之间,之后RTP又有新增,如下:

    Type   Packet    Type name                       
    -------------------------------------------------
    0      undefined                                   
    1-23   NAL unit  Single NAL unit packet per H.264  
    24     STAP-A    Single-time aggregation packet    
    25     STAP-B    Single-time aggregation packet     
    26     MTAP16    Multi-time aggregation packet     
    27     MTAP24    Multi-time aggregation packet    
    28     FU-A      Fragmentation unit                
    29     FU-B      Fragmentation unit                 
    30-31  undefined                                    

这里有些类型分为A型和B型,区别在于是否含有(DON, DONB, DOND)信息,含有的为B型,不含有的为A型。我们一般情况下用到的都是A型,不含有(DON, DONB, DOND)信息的,是我们主要分析的对象。

所以从封装标准文档中看,分为三种格式分别为:Single NAL unit,aggregation packet,Fragmentation unit。

单个NAL包:Single NAL Unit Packet

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |F|NRI|  type   |                                               |
     +-+-+-+-+-+-+-+-+                                               |
     |                                                               |
     |               Bytes 2..n of a Single NAL unit                 |
     |                                                               |
     |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                               :...OPTIONAL RTP padding        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

这种很简单,一个 RTP 包仅由一个完整的 NALU 组成. 这种情况下 RTP NAL 头类型字段和原始的 H.264的
NALU 头类型字段是一样的,即在范围1到23之间。

聚合包:Aggregation Packets

单时间聚合包:Single-Time Aggregation Packet

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                          RTP Header                           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |STAP-A NAL HDR |         NALU 1 Size           | NALU 1 HDR    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                         NALU 1 Data                           |
     :                                                               |
     +               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |               | NALU 2 Size                   | NALU 2 HDR    |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                         NALU 2 Data                           |
     |                                                               |
     |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                               :...OPTIONAL RTP padding        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

多时间聚合包:Multi-Time Aggregation Packets (MTAPs)

多时间聚合包分为两种:MTAP16和MTAP24,区别在于每个NALU TS offset的长度一个是16位,一个是24位。

MTAP16格式

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                          RTP Header                           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |MTAP16 NAL HDR |  decoding order number base   | NALU 1 Size   |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  NALU 1 Size  |  NALU 1 DOND  |       NALU 1 TS offset        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  NALU 1 HDR   |  NALU 1 DATA                                  |
     +-+-+-+-+-+-+-+-+                                               +
     :                                                               |
     +               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |               | NALU 2 SIZE                   |  NALU 2 DOND  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |       NALU 2 TS offset        |  NALU 2 HDR   |  NALU 2 DATA  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               |
     |                                                               |
     |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                               :...OPTIONAL RTP padding        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

MTAP24格式

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                          RTP Header                           |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |MTAP24 NAL HDR |  decoding order number base   | NALU 1 Size   |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  NALU 1 Size  |  NALU 1 DOND  |       NALU 1 TS offs          |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |NALU 1 TS offs |  NALU 1 HDR   |  NALU 1 DATA                  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +
     :                                                               |
     +               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |               | NALU 2 SIZE                   |  NALU 2 DOND  |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |       NALU 2 TS offset                        |  NALU 2 HDR   |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |  NALU 2 DATA                                                  |
     |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     |                               :...OPTIONAL RTP padding        |
     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

小结:

​ 聚合包中,常用的打包类型为单时间聚合包STAP-A,通常用于将sps,pps,sei帧聚合为一个RTP包发送,而多时间聚合包不常用,一般自己制作rtspserver时也不会采用这种方式。

其中STAP-A NAL HDR一般是将Type设置为24,再拼装sps,pps,sei帧,代码如下:

private byte[] saveStapA(byte[] sps, byte[] pps) {
        byte[] stapA = new byte[sps.length + pps.length + 5];

        // STAP-A NAL header is 24
        stapA[0] = 24;

        // Write NALU 1 size into the array (NALU 1 is the SPS).
        stapA[1] = (byte) (sps.length >> 8);
        stapA[2] = (byte) (sps.length & 0xFF);

        // Write NALU 2 size into the array (NALU 2 is the PPS).
        stapA[sps.length + 3] = (byte) (pps.length >> 8);
        stapA[sps.length + 4] = (byte) (pps.length & 0xFF);

        // Write NALU 1 into the array, then write NALU 2 into the array.
        System.arraycopy(sps, 0, stapA, 3, sps.length);
        System.arraycopy(pps, 0, stapA, 5 + sps.length, pps.length);
        return stapA;
    }

分片单元:Fragmentation Units (FUs)

FU-A 的格式如下

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | FU indicator  |   FU header   |                               |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
    |                                                               |
    |                         FU payload                            |
    |                                                               |
    |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                               :...OPTIONAL RTP padding        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

下面是FU indicator,FU Indicator其实和NAL头部几乎一样,就是nal类型变成了分片包的类型28,原先的nal类型保存到了FU Header中的Type。

    +---------------+
    |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   |
    +---------------+
        //这里的type为原始码流h.264de NAL类型
*       第一個包:	 S=1,E=0;
*       中間包:      S=0,E=0
*       最後一包:    S=0,E=1
类型 S
start,为1表示分片包的第一包
E
end,为1时表示分片包的最后一包
R
保留,一直为0
起始包 1 0 0
中间包 0 0 0
最后包 0 1 0

这些内容填充好之后,就和payload数据合成一个RTP包,然后发送出去。

4.Wireshark抓包分析

single NAL unit:分析配置帧

sps,pps,sei帧这些都是采用single NAL unit发送,所以解析type=Payload[0]&0x1F
H.264 RTP封包规则及RTSP抓包分析
67&0x1F = 7,此为sps帧

H.264 RTP封包规则及RTSP抓包分析
68&0x1F = 8,此为pps帧

H.264 RTP封包规则及RTSP抓包分析
06&0x1F = 6,此为sei增强信息帧。

FU分片发送,分析type=5的I帧

首包

H.264 RTP封包规则及RTSP抓包分析
这个第一个字节为0x7C(二进制 0111 1100),定义如下:

+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI|  Type   |
+---------------+
    //这里的type为28

解析得到:

type= 0x7C&0x1F = 0x1C = 28,即分片type类型

NRI = 0x7C&0x60 = 0x60,二进制(0110 0000)

第二个字节为0x85(二进制 1000 0101),定义如下:

+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R|  Type   |
+---------------+
    //这里的type为原始码流h.264de NAL类型
*       第一個包:	 S=1,E=0;
*       中間包:      S=0,E=0
*       最後一包:    S=0,E=1

NALType=0x85&0x1F = 0x05 = 5 ,即为I帧

根据二进制位数可知,S= 1, E=0 , R=0,所以为起开始包

中间包

H.264 RTP封包规则及RTSP抓包分析

中间包的FU indicator,应该与首包相同,都为0x7C,

第二字节为0x05(二进制 0000 0101),即type=0x05=5

+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R|  Type   |
+---------------+
    //这里的type为原始码流h.264de NAL类型

第一個包:	 S=1,E=0;

中間包:      S=0,E=0

最後一包:    S=0,E=1

而且S=0,E=0,R=0,所以为中间包

尾包

H.264 RTP封包规则及RTSP抓包分析

尾包的FU indicator,应该与首包相同,都为0x7C,

第二字节为 0x45(二进制位 0100 0101)

type = 0x45 & 0x1F = 0x05 = 5

S=0,E=1,R=0,所以为最后一包数据。

参考文档:

H264 RTP封包标准.