【问题标题】:RTP fragmentation vs UDP fragmentationRTP 分片与 UDP 分片
【发布时间】:2015-08-20 16:34:11
【问题描述】:

如果 UDP(或 IP)层进行分段,我不明白为什么我们还要在 RTP 级别进行分段。

据我了解,假设我们在以太网链路上,MTU 为 1500 字节。

如果我必须发送,例如,3880 字节,在 IP 层分片,将产生 3 个数据包,分别为 1500、1500 和 940 字节(IP 标头为 20 字节,因此总开销为 60 字节) .

如果我在 UDP 层这样做,开销将是 84 字节(3x 28 字节)。

在 RTP 层,它是 120 字节的开销。

在 H264/NAL 分包层,FU-A 模式多出 3 个字节(最终为 123 个字节)。

对于这么小的数据包,最终会增加初始数据包大小的 3.1%,而在 IP 层,总共只会浪费 1.5%。

是否有任何正当理由在 RTP 层制定如此复杂的打包规则,知道它总是比低层分段更糟糕?

【问题讨论】:

    标签: h.264 rtp fragmentation


    【解决方案1】:

    RTP 的设计考虑了 UDP。

    应用程序通常在 UDP 之上运行 RTP 以利用其 多路复用和校验和服务;这两个协议都贡献了一部分 传输协议功能。

    但是,添加到原始 UDP 的 RTP 服务(例如检测数据包重新排序、丢失和计时的能力)要求 UDP 数据由 RTP 有效负载和服务信息组成。

    互联网与其他数据包网络一样,偶尔会丢失和 重新排序数据包并将它们延迟可变的时间。应对 有了这些损伤,RTP 报头包含时序信息 和一个序列号,允许接收器重建 由源产生的时间,所以在这个例子中, 音频每 20 毫秒连续在扬声器中播放一次。这 对每个 RTP 源分别执行时序重构 会议中的数据包。序列号也可用于 接收方估计丢失了多少数据包。

    那么RTP被设计成可扩展的、通用的headers和数据特定的payload:

    RTP 是一个故意不完整的协议框架。本文档指定了那些预期在 RTP 适合的所有应用程序中通用的功能。与传统协议不同,在传统协议中,可以通过使协议更通用或添加需要的选项机制来适应附加功能 解析时,RTP 旨在根据需要通过对标头的修改和/或添加来定制。

    所有报价均来自RFC 1889 "RTP: A Transport Protocol for Real-Time Applications"

    也就是说,H.264 流的 RTP 开销不仅仅是对带宽的浪费。 RTP 标头和 H.264 有效负载格式允许以适中的成本以更可靠的方式处理视频数据流,同时利用定义明确且适用于不同类型数据的规范。

    【讨论】:

    • 虽然您所说的显然是正确的,但它并没有回答我提出的问题。我不问为什么要使用 RTP over UDP(我很清楚),但是 H264 的 FU-A 分段需要什么,因为发送一个大数据包最终会消耗更多的数据而不是使用 IP 层分段(这不会导致重新排序,因为完整的 RTP 数据包和标头将被保留)。
    • 您需要一些数据来识别您收到的数据包类型。乱码包?带有 NAL 单元的数据包? NAL 单元的一部分?显然应该保留几个字节来提供这样的标识。然后 H.264 传输的设计方式是不需要检测起始码来解析 NAL 单元(否则会再次消耗几个字节),因此应该清楚数据包是否是带有填充的 NAL 单元,或者一些 NAL 单元小到足以容纳单个 UDP 数据包。这就是 FU-A 的由来——一种安排 NAL 单元以将它们拆分成部分的方法。
    • 我的意思是如果我有一个 56kB 的 NAL,并发送一个 56kB 的 RTP 数据包,它将被 IP 层拆分为所需数量的 IP 数据包,并将作为单个 56kB 接收客户端的 RTP 数据包(按顺序,否则根本不存在)。 (初始)NAL 标头仍将存在。与发送 38x 1.5kB RTP 数据包和 FU-A 标记相比,该方案浪费的开销要少得多。此外,FU-A 标头中的开始和结束位与 RTP 的标头标记/序列号和时间戳是冗余的,因为它们必须匹配。
    • 好吧,假设您丢失了一个数据包,以及如何才能赶上您的方法以及定义的 RFC。
    • 如果您丢失了任何单个数据包(无论是 FU-A/RTP 还是 IP 的部分版本),您都无法重建 NAL 单元。最后,这两种方法都会得到相同的结果。因此,我不明白您的最后评论。
    【解决方案2】:

    好吧,经过深思熟虑,没有理由不使用基于 IP 的高达 64kB 的分段(如果您需要通过 STAP-A 聚合大量相同时间戳的 NAL 单元,就会发生这种情况例如)。

    RFC6184 很明确,您可以通过这种方式使用最多 64kB 的 NAL,因为每个 NAL 单元的大小正好 2 个字节(16 位)附加在实际 NAL 单元之前,尽管最好保持在 MTU 以下。

    如果“单次”NAL 单元的累积大小大于 64kB 会怎样? RFC6184 没有说,但是我猜你必须将你所有的 NAL 作为单独的 FU-A 数据包发送而不增加它们之间的时间戳(这是 only FU-A 标头中的 Start/End 位有用的原因,因为 End 位和 RTP 的标记位之间不再存在 1:1 匹配)。

    RFC 规定:

    一个聚合包可以 根据需要携带尽可能多的聚合单元;然而,总 聚合数据包中的数据量显然必须适合 IP 数据包,并且应该选择大小,以便生成的 IP 数据包 小于 MTU 大小

    当“每帧单个 NAL”大于 MTU(例如,以太网为 1460 字节)时,必须使用分段单元打包(例如,FU-A)对其进行拆分。

    但是,RFC 中没有任何内容规定该限制应为 1460 字节。并且在仅进行以太网流式传输(如上计算)时,具有更大的容量是有意义的

    如果您有一个大于 64kB 的 NAL 单元,那么您必须使用 FU-A 发送它,因为您无法将其放入单个 IP 数据报中。

    RFC 规定:

    这种有效载荷类型允许将一个 NAL 单元分成几个 RTP 数据包。在应用层这样做而不是依赖 较低层的分片(例如,通过 IP)具有以下优点:

    o 有效载荷格式能够传输更大的 NAL 单元 超过 64 kbytes 在 IPv4 网络上可能存在于 pre- 录制的视频,尤其是高清格式(有 每张图片的切片数量限制,这会导致 每张图片的 NAL 单元数限制,这可能会导致大 NAL 单位)。

    o 分片机制允许对单个 NAL 单元进行分片 并应用通用前向纠错,如 第 12.5 节。

    我的理解是:“如果你的 NAL 单元小于 64kbytes,并且你不关心 FEC,那么不要使用 FU-A,而是使用单个 RTP 数据包”

    另一种需要 FU-A 的情况是在接收具有 RTP over RTSP(交错模式)的 H264 流时。 “数据包”大小必须适合 2 个字节(16 位),因此即使在可靠的流套接字上发送,您也必须对更大的 NAL 单元进行分段。

    【讨论】:

    • 正在寻找不同的数据并偶然发现了这篇 2015 年的帖子。如果它有用:我没有看到这个讨论中提到的延迟。如果您使用 RTP 发送音频数据,您希望尽快将 20 毫秒的音频数据包从您的盒子中取出。您不希望您的听众在让 IP 处理碎片之前等待 64k 的 opus 编码数据。取决于编码压缩,接收器可能会在几秒钟内听不到您的声音。
    • 你是对的。然而,问题恰恰相反,如何在现有链路上有效地传输尽可能多的数据。在这种特定情况下,使用 IP 分段更有效。这仅在延迟不是问题时才有效(正如您提到的)。如果延迟是一个问题,那么小数据包会更好(但显然有更多的带宽开销)。音频不使用 H264 的 NAL、AFAIK。
    • 当通道上存在错误时,UDP 需要重新传输所有分片的整个数据包。相反,RTP 只需要重传丢失的片段。如果带宽不是问题,差异将转化为不同的延迟。
    • 如果不尝试重传,则差异将转化为丢失数据的数量。如果任何片段在同一个数据包中丢失,UDP 不会给你收到的片段。 RTP 将交付它收到的任何东西。最终,视频或音频的呈现质量会有所不同。
    【解决方案3】:

    除了第一个分片外,分片的 IP 流量不包含源端口号或目标端口号。相反,它使用序列 ID 将数据包粘合在一起。这使得需要重新安装 QoS 的无状态中间网络设备(交换机和路由器)不可能(因为 .1p 或 DSCP 标志已被另一台设备清除或根本不存在。)除非设备有资源来管理对于每个会话状态,它要么必须冒险限制来自不相关流的片段/优先考虑片段,要么不优先考虑任何片段,其中一些片段可以是语音/视频。

    除非网络中存在 MTU 不匹配,否则 AFAIK RTP 数据包永远不会出现 IP 碎片。因此,每个 UDP 标头都有源端口号和目标端口号,因此,如果您可以驯服客户端以使用已知端口范围,则可以根据此信息重新建立 QoS 标记,并且可以将 IP 片段作为普通流量传递,而不必担心丢弃语音/视频数据。

    【讨论】:

      【解决方案4】:

      我想补充一点,很多 RTP 服务器/发送器在发送拆分数据报时效率低下。

      • 他们在动态缓冲区上下文中使用了大量的 malloc/free。
      • 他们还为每个消息部分使用一个系统调用,而不是消息向量。
      • 为了雪上加霜,他们通常在发送数据报的每个部分之间进行大量时间计算/其他处理。

      这会导致更多的系统调用,有时甚至会长时间拉伸数据包,因为它们在数据包应该完成时没有上限,只有在发送下一批数据包之前完成。

      如果您想扩展吞吐量或在低功耗嵌入式 CPU 上运行,这样的低效行为会严重妨碍您。出于 bw、网络和 CPU 效率的原因,通常最好将整个数据报一次性发送到内核并让它处理碎片,而不是用户空间试图找出它。

      【讨论】:

        猜你喜欢
        • 2011-04-12
        • 1970-01-01
        • 2017-08-04
        • 2021-09-22
        • 1970-01-01
        • 2013-09-02
        • 2011-01-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多