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

文章目录

  • 1.H264中NAL unit Header简介
    • NAL unit Header
      • H264 NAL Header
    • 解析原始码流的H264 type方式:
      • H.264常用的type类型
  • 2.RTP header 简介
  • 3.H.264 RTP 载荷封装格式
    • Type的类型说明
    • 单个NAL包:Single NAL Unit Packet
    • 聚合包:Aggregation Packets
      • 单时间聚合包:Single-Time Aggregation Packet
      • 多时间聚合包:Multi-Time Aggregation Packets (MTAPs)
      • 小结:
    • 分片单元:Fragmentation Units (FUs)
  • 4.Wireshark抓包分析
    • single NAL unit:分析配置帧
    • FU分片发送,分析type=5的I帧
      • 首包
      • 中间包
      • 尾包
  • 参考文档:

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抓包分析_第1张图片
其中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抓包分析_第2张图片
67&0x1F = 7,此为sps帧

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

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

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

首包

H.264 RTP封包规则及RTSP抓包分析_第5张图片
这个第一个字节为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抓包分析_第6张图片

中间包的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抓包分析_第7张图片

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

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

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

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

参考文档:

H264 RTP封包标准.

你可能感兴趣的:(多媒体Multimedia,h.264,rtp)