前言:
大家好,我是飞一样的成长,在昨天的文章里面,我给大家分享了rtp打包的规则;今天给大家分享一下rtp解包的规则,也就是接收端接收了rtp包,怎么解包?
De-Packetization Process(解分组化过程):
去封装(De-packetization)过程是依赖具体实现的。因此,下面的描述应被视为一种合适的实现示例。只要对于相同的输入,得到的输出与下述过程一致,其他的实现方式也可以使用。所谓“输出一致”,是指最终得到的 NAL 单元及其顺序完全相同。在此基础上,相比描述的算法,进行优化是可能的。 此外,所有正常的 RTP 机制(比如缓冲区管理)都适用。特别是,应移除重复的或过时的 RTP 包(可以通过 RTP 的序列号和时间戳来判断)。为了确定准确的解码时间,还必须考虑一些因素,比如为了实现正确的多流同步而引入的有意延迟等。
single NAL Unit and Non-Interleave Mode:
接收端包含一个接收缓冲区,用于补偿传输延迟抖动。接收端将接收到的数据包按照接收顺序存入接收缓冲区中。数据包根据 RTP 序列号顺序进行去封装(de-packetize)。
具体处理方式如下:
-
如果去封装出来的是单一 NAL 单元包(Single NAL Unit Packet),那么该 NAL 单元会直接传递给解码器;
-
如果去封装出来的是STAP-A 包,那么其中包含的多个 NAL 单元,会按照它们在包中封装的顺序依次传递给解码器;
-
对于所有包含同一个 NAL 单元片段的FU-A 包,去封装时,需要按照发送顺序将这些片段拼接起来,恢复成完整的 NAL 单元,再将其传递给解码器。
说明性备注(Informative note):
如果解码器支持任意片段顺序(Arbitrary Slice Order),那么一张图片(帧)中的编码片段可以以任意顺序传递到解码器,而不必严格按照它们的接收顺序或发送顺序。

-
步骤1-3:到达 ➔ 入缓冲 ➔ RTP序号排序,保证数据正确拼装。
-
步骤4:分类:
-
单一NAL包 ➔ 直接提取;
-
STAP-A ➔ 逐个取出里面的小NAL;
-
FU-A ➔ 把所有片段按发送顺序连起来恢复NAL。
-
步骤5:最终得到的 NAL 单元送去给解码器。
-
步骤6:如果解码器支持任意Slice顺序,可以不按接收顺序送进去,优化延迟。
RTP Payload格式 ➔ 去封装流程详细图 :
- 1、 单一NAL单元包 Single NAL Unit Packet:
结构示意:
[RTP Header][NALU Header + NALU Payload数据]
处理流程:
接收RTP包 ↓ 跳过RTP Header ↓ 读取1字节 NALU Header ↓ NALU Payload(剩余部分) ↓ 直接送给解码器(一个完整NAL单元)
- 2、 STAP-A包 Single-Time Aggregation Packet A:
结构示意:
[RTP Header][STAP-A Header][NALU1 size][NALU1][NALU2 size][NALU2]...
详细字段:
-
STAP-A Header (1字节)
-
后续是多个 {2字节NALU长度 + NALU数据} 结构重复
处理流程:
接收RTP包 ↓ 跳过RTP Header ↓ 读取1字节 STAP-A Header ↓ 循环: 读取2字节 -> NALU长度 ↓ 读取NALU长度字节数 -> 得到一个NALU ↓ 送给解码器 直到数据读完
- 3、 FU-A包 Fragmentation Unit A:
结构示意:
[RTP Header][FU Indicator][FU Header][NALU Fragment Payload]
详细字段:
-
FU Indicator (1字节):基本上和原NALU头长得差不多(F, NRI, Type=28)
-
FU Header (1字节):包含S(起始)、E(结束)、R(保留0)、Type(原NALU的Type)
处理流程:
接收RTP包 ↓ 跳过RTP Header ↓ 读取1字节 FU Indicator 读取1字节 FU Header ↓ 检查S、E位: - 如果S=1:开始新的NAL单元 - 重建1字节NALU Header: (FU Indicator的F/NRI) + (FU Header的Type) - 把重建头写入NALU缓存 - 追加NALU Fragment Payload数据到NALU缓存 - 如果E=1:本NALU片段结束 - 将缓存中的完整NALU送去解码器
总结:

接收到 RTP包 ↓ 判断包类型 ↓ ┌───────────────────────────────────────────────┐ │ 单NALU包:直接读取NAL头 + Payload,送解码器 │ ├───────────────────────────────────────────────┤ │ STAP-A包:读取STAP头→循环取(长度+NALU)送解码器 │ ├───────────────────────────────────────────────┤ │ FU-A包:根据S/E位组装NAL单元→完整后送解码器 │ └───────────────────────────────────────────────┘
Interleaved Mode:
这些解包规则背后的总体概念是:将 NAL 单元从传输顺序重新排列为 NAL 单元的解码顺序。
接收端包含一个接收缓冲区,用于补偿传输延迟抖动并将 NAL 单元从传输顺序重新排序到解码顺序。在本节中,描述接收端操作时假设不存在传输延迟抖动。为了区分用于描述的接收缓冲区与实际系统中同时用于补偿传输延迟抖动的接收缓冲区,本节将此缓冲区称为去交错缓冲区(de-interleaving buffer)。 接收端还应当做好处理传输延迟抖动的准备,即:
-
要么为传输延迟抖动缓存和去交错缓存分别预留独立的缓冲区,
-
要么使用一个接收缓冲区同时处理传输延迟抖动和去交错的功能。
此外,接收端在缓冲操作时也应考虑传输延迟抖动的影响,例如,通过在开始解码和播放前进行额外的初始缓冲。
1、Size of the De-Interleaving Buffer:
无论在 Offer/Answer 模式还是 声明式(declarative)Session Description Protocol (SDP) 使用中,sprop-deint-buf-req 媒体类型参数用于指示去交错缓冲区(de-interleaving buffer)所需的大小。
因此,建议(RECOMMENDED)将实际设置的去交错缓冲区大小(以字节数计)设置为不小于 sprop-deint-buf-req 媒体类型参数的值。
当在会话建立时使用 SDP Offer/Answer 模型或其他能力交换过程(capability exchange procedure)时,接收到的流的属性应当(SHOULD)保证不会超出接收端的能力。 在 SDP Offer/Answer 模型中,接收端可以通过 deint-buf-cap 媒体类型参数指示其能够分配的去交错缓冲区大小的能力。 关于 deint-buf-cap 和 sprop-deint-buf-req 这两个媒体类型参数的更多信息见后面讲解。
2、De-Interleaving Process:
接收端有两种缓冲状态:初始缓冲(initial buffering) 和 播放过程中的缓冲(buffering while playing):
-
初始缓冲发生在 RTP 会话初始化时。
-
初始缓冲结束后,开始解码和播放,随后切换到播放过程中的缓冲模式。
无论处于哪种缓冲状态,接收端都按照接收顺序将收到的 NAL 单元存入去交错缓冲区(de-interleaving buffer),具体规则如下:
-
聚合包(Aggregation Packets)中的每个 NAL 单元都单独存储到去交错缓冲区中。
-
对每个 NAL 单元,计算并存储其 DON(Decoding Order Number)值。
接收端的具体操作依赖于以下函数和常量:
-
函数 AbsDON:在第 8.1 节中定义。
-
函数 don_diff:在第 5.5 节中定义。
-
常量 N:为可选参数 sprop-interleaving-depth 的值加 1(见第 8.1 节)。
3、从去交错缓冲区中移除 NAL 单元的规则:
当需要从去交错缓冲区中取出 NAL 单元并送到解码器时,按以下规则:
-
如果去交错缓冲区中至少有 N 个 VCL NAL 单元,则按下文指定的顺序,移除并送入解码器,直到剩余 N-1 个 VCL NAL 单元;
-
如果存在 sprop-max-don-diff 参数,则所有 don_diff(m,n) 大于 sprop-max
-
don-diff 的 NAL 单元需要移除并按顺序送到解码器;
- 其中 n 是当前去交错缓冲区中AbsDON 最大的那个 NAL 单元。
4、向解码器送 NAL 单元的顺序规则:
-
定义变量 PDON,在 RTP 会话开始时初始化为 0。
-
对每个 NAL 单元,依据其 DON 计算 DON 距离(DON distance),计算方法如下:
-
如果 NAL 单元的 DON 值大于 PDON,则 DON 距离 = DON - PDON;
-
否则,DON 距离 = 65535 - PDON + DON + 1。
-
按照 DON 距离的升序 将 NAL 单元送到解码器;
-
如果多个 NAL 单元具有相同的 DON 距离,可以以任意顺序送到解码器;
-
送完一批 NAL 单元后,将 PDON 更新为最后一个送入解码器的 NAL 单元的 DON 值。
NAL 单元接收与去交错缓冲处理流程图 :
RTP 包到达 ↓ 提取 NAL 单元(或拆分聚合包里的各个 NAL) ↓ 为每个 NAL 单元计算 DON(Decoding Order Number) ↓ 将 NAL 单元按接收顺序存入【去交错缓冲区】 ↓ 检测当前缓冲状态: ┌─────────────────────────────────────────────┐ │ 是否是「初始缓冲阶段」? │ │ │ │ 【判断条件】: │ │ 1. 去交错缓冲区中是否 ≥ N 个 VCL NAL? │ │ 2. don_diff(m, n) 是否 > sprop-max-don-diff? │ │ 3. 缓冲时间是否 ≥ sprop-init-buf-time? │ └─────────────────────────────────────────────┘ ↓ 是 否 ↓ ↓ 【结束初始缓冲】 【缓冲-播放阶段】 开始解码播放 ↓ ↓ (继续接收和缓存) ────────────────────────────────────── 当满足以下任一条件时: - 缓冲区 ≥ N 个 VCL NAL - don_diff 超过 sprop-max-don-diff 开始从缓冲区取出 NAL 单元送给解码器 ↓ 【取出顺序规则】: - 维护 PDON(上一次送出 NAL 的 DON) - 计算每个 NAL 的 DON 距离 - DON > PDON:DON distance = DON - PDON - DON ≤ PDON:DON distance = 65535 - PDON + DON + 1 - 按 DON 距离升序送出 ↓ 送入解码器,继续播放 ↓ 更新 PDON = 最新送出的 NAL 的 DON ↓ 回到继续缓冲/接收处理
-
初始缓冲阶段是确保一开始播放不会因为乱序、延迟而花屏。
-
N 个 VCL NAL 单元是为了形成足够稳定的数据量(N = sprop-interleaving-depth + 1)。
-
sprop-max-don-diff是保护机制:如果解码顺序太错乱,提前启动解码。
-
PDON / DON distance,是保证数据正确解码顺序的关键。
Additional De-Packetization Guidelines:
为了实现一个可用的 H.264 解包器(de-packetizer),可以使用以下附加的解包规则:
-
智能型 RTP 接收器(例如在网关中的接收器)可以识别丢失的编码片段数据分区 A(DPA)。如果检测到 DPA 丢失(在考虑了可能的重传和前向纠错(FEC)后),网关可以决定不发送对应的编码片段数据分区 B 和 C,因为对于 H.264 解码器来说,这些分区在没有 DPA 的情况下已经没有意义。通过这种方式,MANE(媒体感知网络元素)可以在不解析复杂比特流的情况下,通过丢弃无意义的数据包来降低网络负载。
-
智能型 RTP 接收器可以识别丢失的FU(Fragmentation Units,片段单元)。
如果发现某个 FU 丢失,网关可以决定不发送同一被片段化的 NAL 单元的后续 FU,因为在前面部分丢失的情况下,后续部分对 H.264 解码器已经无意义。 这样,同样可以在不解析复杂比特流的前提下,减少无用数据,提高网络效率。
-
智能接收器在需要丢弃数据包或 NAL 单元(NALU)时,应遵循以下优先级:
-
首先丢弃 NRI(NAL 单元头部中的重要性指示位)字段值为 0 的数据包或 NAL 单元,因为这些数据通常对解码器参考图像无关紧要,可以最小化对用户体验的影响;
-
如果还需要继续丢弃数据,应优先丢弃 NRI 数值较低的包,保留 NRI 数值较高的包;
-
但是,丢弃 NRI 大于 0 的数据包很有可能导致解码器漂移(decoder drift),因此应该尽量避免。
总结一下:
-
丢了 DPA ➔ 后续的 DPB/DPC 没必要传。
-
丢了 FU 的某个片 ➔ 同一个 NALU 后面的片也不要传。
-
如果必须丢包 ➔ 先丢 NRI=0 的包 ➔ 再根据 NRI从小到大丢 ➔ 避免丢 NRI>0 的重要包。