网络抽象层单元 (NALU)
NALU头
NALU 头由1个byte组成, 它的语法如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
- F : 1 个比特. forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
- NRI: 2 个比特. nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如 00 的 NALU 解码器可以丢弃它而不影响图像,不过一般情况下不太关心这个属性.
- Type: 5 个比特.nal_unit_type表示这个 NALU 单元的类型.
NALU头中表示的type类型
| Type取值 | 包类型名称 |
| 0 | 未定义 |
| 1-23 | 为NALU包,且该NALU内包含一个完整的H264帧 |
| 1 | 不分区,非IDR图像的片 |
| 2 | 片分区A |
| 3 | 片分区B |
| 4 | 片分区C |
| 5 | IDR图像中的片 |
| 6 | 补充增强信息单元(SEI) |
| 7 | SPS |
| 8 | PPS |
| 9 | 序列结束 |
| 10 | 序列结束 |
| 11 | 码流借宿 |
| 12 | 填充 |
| 13-23 | 保留 |
| 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 |
上述type中,单个mtu中包含一个独立完整的h264帧和多个mtu组成一个单独完整的h264帧的情形比较容易见到。以下图为例,就是我们从三光吊舱拿到的payload:

因为我使用了wireshark的extract_h264插件,所以这里显示的是H.264字段,如果没有使用该插件,则显示的是RTP的payload字段。同图中可以看到FU identifier是一个字节0x3C,对应的是0011 1100,对照上述的NALU 头的解释,F位为0,NRI为01,表示重要性;type为28,表示这个包为FU-A分片单元,也说明这个是某个h264帧的一个分片,该帧需要多个分片构成分片方式为FU-A。
也就是说,一个使用RTP封装的h264的包的payload和h264的帧存在3种对应关系:
- Single NAL unit packet(单NALU包): 也就是实际的NAL类型,可以理解为一个包就是一帧H264数据,这个在实际中是比较多的。
- Aggregation packet (聚合包):一包数据中含有多个H264帧,封装在Aggregation packet中的 NAL单元大小为65535字节。
- STAP-A 包内的帧含有相同的NALU-Time,没有DON;
- STAP-B 包内的帧含有相同的NALU-Time,有DON
- MTAP16 包内的帧含有不同的NALU-Time,timestamp offset = 16
- MTAP24 包内的帧含有不同的NALU-Time,timestamp offset = 24
- Fragmentation unit(分片包): 一帧数据被分为多个RTP包,这也是很常见的,特别是对于关键帧。现存两个版本FU-A,FU-B。实际应用就是要加上个H264 STREAM 的头h264_stream_head = 0x00,0x00,0x00,0x01 4字节,送去解码即可。
分包规则
这里讲解上述三种情况下,h264的数据和payload的关系;
单个NAL单元包(type值为1-23)
对于 NALU 的长度小于MTU 大小的包, 一般采用单个NAL 单元模式。
一个原始的 H.264 NALU (也就是还原后的应该有的样子)单元常由 [Start Code] [NALU Header] [NALU Payload]三部分组成, 其中 Start Code 用于标示这是一个 NALU 单元的开始, 必须是 "00 00 00 01" 或 "00 00 01", NALU 头(上述的0x3C)仅一个字节, 其后都是 NALU 单元内容。
对于这样的包,在rtp打包时只是去除了 "00 00 01" 或 "00 00 00 01" 的开始码(Start Code), 把其他数据(包括NALU header和NALU payload)封包的 RTP 包即可。
一个封装单个NAL单元包到RTP的NAL单元流的RTP序号必须符合NAL单元的解码顺序。单个NAL单元包的结构显示如图。(NAL单元的第一字节和RTP荷载头第一个字节重合)
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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&n

本文详细解读了NALU头的结构,包括禁止位、重要性标识和类型,深入剖析了不同类型NALU单元(如单个帧、聚合包、分片单元)的含义及在RTP中的应用。重点介绍了如何处理SPS/PPS、FU-A/FU-B,以及如何利用ffmpeg解码H264数据。
最低0.47元/天 解锁文章
3917

被折叠的 条评论
为什么被折叠?



