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

[live555] RTP包 NALU FU-A等之间的关系

程序员文章站 2022-07-14 18:48:35
...

#前言
RTSP 协议流程 已经介绍在SETUP 确定了传输模式,而在PLAY的时候就开始传输RTP 包
并且,确定了第一个RTP url *** 和时间戳

 RTP-Info: url=rtsp://10.0.2.15/ss1.mkv/track1;seq=57885;rtptime=4285367567
 ,url=rtsp://10.0.2.15/ss1.mkv/track2;seq=41406;rtptime=1364100230

开始学习RTP包组成

RTP 包组成

  1. 协议基本知识
    看协议文档还是算了,初学者不建议直接看,全英文就算了,没有实践过,理解起来很难,还是等熟悉后,再反过来当参考资料比较好
    RTP协议地址 https://tools.ietf.org/html/rfc3550
    RTP负载格式H264视频 地址 https://tools.ietf.org/html/rfc6184

很多大佬的资料如下

  1. 流媒体协议 https://blog.csdn.net/fishmai/article/details/53676194
  2. 分包说明 https://blog.csdn.net/jwybobo2007/article/details/7235942
  3. RTP 封装 H264 https://blog.csdn.net/jwybobo2007/article/details/7054140
    上面的看完,估计就立即的差不多了,我自己总结一份

我记得计算机网络分为七层模型,后来变成常用的5层 每一层其实就是给数据 加上header ,RTP也是一样的。加一个RTP header 如下
首先了解RTP格式
RTP :Real Time Protocol 实时协议

 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             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
|                                                               |
|                                                               |
|               payload                                         |
|                                                               |
|                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               :...OPTIONAL RTP padding        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

一个RTP 包 由 RTP header + payload组成
RTP header 解释

  1. V:版本 v=2 占用2bit
  2. P:填充标志 占用1bit 如果 P=1 表示RTP包末尾有填充
  3. X:扩展标志 占用1bit 如果 X=1 表示RTP报头末尾有扩展报头
  4. CC:CSRC计数器,占4位,指示CSRC 标识符的个数
  5. M:标记 在包中标记 占用1bit
  6. PT:Payload type 说明负载类型 占用 7bit 一般来说 H264协议 视频是96 97 是音频 AMR-WB
  7. Sequence number:SN *** 随机生成的,表示一个RTP包的序列,后面依次+1
    上面就表示一行 4个字节
  8. timestamp:时间戳 占用 32bit 必须采用 90KHz 采样率 当前RTP 时间
  9. synchronization source (SSRC) identifier:同步信源标识 随机产生 唯一
  10. contributing source (CSRC) identifiers: 特约信源标识符: 每个CSRC标识符占32bits,最多有15个。用于多房间会议

NALU

payload 是RTP 包的载荷,而在视频中 ,RTP载荷的是NALU或者分包后的FU-A 或者PPS 和SPS
H264 是视频编解码标准
H264 视频码流 理解:视频经过H264 编码后,就是H264码流

但是 H264 码流太大,所以需要拆分多个码流,放在多个RTP包中,但是这些拆分后的码流需要NALU header(报头) 标识,才能放进RTP 负载中,这个就是NALU

NALU :网络提取层单元

所以完整的 payload = NALU = NAL header+ H264原始码流(RBSP)

NAL header 结构 占用 8 bit

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

NAL header 解释
F:forbidden_​​zero_bit 禁止位 占用1 bit ,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。
NRI :占用2bit 00~11 值越大 重要性越大,00 标识重要性很低,可以直接丢了,不影响恢复视频
Type:nal_unit_type 占用 5 bit, 指定 NALU的有效载荷类型 ,和上面的Payload type 不一样 ,这里的type 用来说明不同的载荷结构
nal_unit_type = 7 8 或者5 的时候 NRI必须是11

nal_unit_type 是什么?

nal_unit_type NAL类型
1 非IDR图像中不采用数据划分的片段
2 非IDR图像中A类数据划分片段
3 非IDR图像中B类数据划分片段
4 非IDR图像中C类数据划分片段
5 IDR图像的片
6 补充增强信息单元(SEI)
7 序列参数集
8 图像参数集
9 分界符
10 序列结束
11 码流结束
12 填充
13…23 保留
24…31 不保留(RTP打包时会用到)

比较重要的是
nal_unit_type = 7 RTP 负载的是序列参数集
nal_unit_type = 8 RTP 负载的是图像参数集
nal_unit_type = 5 IDR图像的片 (立即刷新图像,I帧给P帧和B帧作为参考)


但是超过23的时候 RTP打包开始用到,决定NALU 如何打包进 RTP。因为NALU 大小有可能远远小于RTP payload,也有可能正好等于RTP payload,或者远大于RTP payload ,那么NALU 就需要再次拆分包,即一帧拆开发送

      Table 3.  Summary of allowed NAL unit types for each packetization
                mode (yes = allowed, no = disallowed, ig = ignore)

      Payload Packet    Single NAL    Non-Interleaved    Interleaved
      Type    Type      Unit Mode           Mode             Mode
      -------------------------------------------------------------
      0      reserved      ig               ig               ig
      1-23   NAL unit     yes              yes               no
      24     STAP-A        no              yes               no
      25     STAP-B        no               no              yes
      26     MTAP16        no               no              yes
      27     MTAP24        no               no              yes
      28     FU-A          no              yes              yes
      29     FU-B          no               no              yes
      30-31  reserved      ig               ig               ig

上面的payload type指的是 nal_unit_type ,容易混淆

NALU 几种情况

  • nal_unit_type = 1-23 就是:单一NAL单元模式

一般NALU的SIZE 小于 MTU {MTU 最大传输单元 一般是1500byte}

此时 单个NALU 放入 RTP payload中
H.264 NALU格式 [Start Code] [NALU Header] [NALU Payload]

H.264 NALU 单元 一般 包含 [Start Code]“0x00 00 00 01” 或 “0x00 00 01” 标识NALU header开始,再打包进RTP PAYLOAD的时候会去除 ,作用:检测出NAL的分界

例子:
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F … ]
这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 内容.
封装成 RTP 包将如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]
即只要去掉 4 个字节的开始码就可以了.

小知识:

H.264引入了防止竞争机制,如果编码器检测到NAL数据存在0x000001或0x000000时,编码器会在最后个字节前插入一个新的字节0x03,这样:
0x000000->0x00000300
0x000001->0x00000301
0x000002->0x00000302
0x000003->0x00000303
解码器检测到0x000003时,把03抛弃,恢复原始数据(脱壳操作)。解码器在解码时,首先逐个字节读取NAL的数据,统计NAL的长度,然后再开始解码。

PS: 当NALU 放在网络中传输,即加入NALU header 的时候,会取出0x00000001 机制,因为有header 来区分,但是存放磁盘的时候,媒体文件会加start code

所以单一NAL单元模式 结构:

 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             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI|  type   |                                               |
+-+-+-+-+-+-+-+-+                                               |
|                                                               |
|               Bytes 2..n of a Single NAL unit                 |
|                                                               |
|                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               :...OPTIONAL RTP padding        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • 聚合包 nal_unit_type = 24~27
    本类型用于聚合多个NAL单元到单个RTP荷载中。本包有四种版本,单时间聚合包类型A (STAP-A),单时间聚合包类型B (STAP-B),多时间聚合包类型(MTAP)16位位移(MTAP16), 多时间聚合包类型(MTAP)24位位移(MTAP24)。赋予STAP-A, STAP-B, MTAP16, MTAP24的NAL单元类型号分别是 24,25, 26, 27

  • 分片单元 nal_unit_type = 28 或者 29
    将NALU 单元拆分到多个RTP包中发送 典型的就是FU-A,而FU-B,没遇到过
    当 单个NALU size 大于 MTU 时候就要拆分 使用FU-A

NALU 因为过大,按照 FU-A分片的流程 (参考
1)第一个FU-A包的FU indicator:F应该为当前NALU头的F,而NRI应该为当前NALU头的NRI,Type则等于28,表明它是FU-A包。FU header生成方法:S = 1,E = 0,R = 0,Type则等于NALU头中的Type。
2)后续的N个FU-A包的FU indicator和第一个是完全一样的,如果不是最后一个包,则FU header应该为:S = 0,E = 0,R = 0,Type等于NALU头中的Type。
3)最后一个FU-A包FU header应该为:S = 0,E = 1,R = 0,Type等于NALU头中的Type。

同一个NALU分包厚的FU indicator头是完全一致的,FU header只有S以及E位有区别,分别标记开始和结束,它们的RTP分包的***应该是依次递增的,并且它们的时间戳必须一致,而负载数据为NALU包去掉1个字节的NALU头后对剩余数据的拆分,这点很关键,你可以认为NALU头被拆分成了FU indicator和FU header,所以不再需要1字节的NALU头了

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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           synchronization source (SSRC) identifier            |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|            contributing source (CSRC) identifiers             |
|                             ....                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator  |   FU header   |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
|                                                               |
|                         FU payload                            |
|                                                               |
|                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                               :...OPTIONAL RTP padding        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

这里的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   |
+---------------+
S E: 10 表示 分片开始(start) 即FU-A的开始 而 01 表示分片结束 即FU-A 结束 (end)
R:占用1bit R=0
type  判断 是什么类型的帧

上面的 [start code]和小知识同样适用所有的NALU

WireShark 抓的RTP包

因为有audio 和 video 我在wireshark中 video采用的是H264协议解码,而音频aac-hdr 使用的AMR-WB 解码的

video:
[live555] RTP包 NALU FU-A等之间的关系

第一帧是SPS 然后是PPS 、SEI帧,开始第一个I帧 很关键,后面就是非I帧,参考I帧

上面讲的FU-A 分片下图就有
[live555] RTP包 NALU FU-A等之间的关系

FU-A NALU header S 和 E 标记开始和结束 可以区分出来, RTP标记M 也显示出来了

audio
[live555] RTP包 NALU FU-A等之间的关系

后面只会分析H264 具体协议

保存这份log,以防以后再拿出来,还是自己手动操作一遍,心里踏实很多。

log地址 https://download.csdn.net/download/engineer_james/10609273