WebRTC服务质量(08)- 重传机制(05) RTX机制

一、前言:

RTX协议(Retransmission,即重传协议)是 WebRTC 中用于处理丢包恢复的一部分。由于网络通信中的丢包不可避免,WebRTC RTP协议栈支持多种丢包恢复机制,其中之一便是通过RTX协议实现的重传机制

RTX协议会根据 RTP 丢包检测机制(具体由 RTCP 的 NACK 包触发),将数据包重传给接收端。RTX 的核心思想是发送端将丢失的数据包重新封装并发送,而接收端根据识别重新封装的数据包进行补偿处理,从而还原完整的流媒体内容。

二、RTX协议的工作原理:

RTX 的工作紧密依赖于 RTP 和 RTCP 协议。以下是 RTX 的通信流程:

  1. 发送端发送数据包: 发送端按照 RTP 协议发送音视频流,每个 RTP 数据包都有唯一的序列号(sequence number),用于接收端检测是否丢包。
  2. 接收端检测丢包: 接收端通过接收 RTP 包的序列号,以及 RTP/RTCP 的统计信息,检测数据是否丢失。一旦丢包,接收端会发送 RTCP NACK(Negative Acknowledgment)反馈包给发送端,并指定丢失数据包的序列号。
  3. 发送端重新发送丢失的数据包: 发送端根据接收到的 RTCP NACK 请求,使用 RTX 流重传对应的丢失数据包。RTX 流不同于主流(primary stream),通常会有独立的 SSRC(Synchronization Source Identifier)用于区分两者。
  4. 接收端合并重传包: 接收端接收到 RTX 包后,根据 RTX 的特殊封装格式提取出原始 Payload(数据载荷)并通过序列号将其合并到主流中。

RTX 的核心点在于:

  • 独立的 SSRC:RTX 使用独立的 SSRC,和主流区分开,这样可以识别重传流。
  • 修改过的 RTP 包头:RTX 包在 RTP 层修改了一些元信息,用于兼容重传流和主流的处理。
  • RTX包有自己的payload type
  • RTX包是按照自己的Sequence Number进行排序的。

三、如何找到RTX包:

  • 找到Offer/Answer这种SDP信息。
  • 从SDP中找到RTX的SSRC。
  • 然后抓包,根据SSRC过滤出RTX包。

四、RTX协议格式:

在这里插入图片描述

  1. RTP Header:和前面介绍协议时候的RTP Header是一样的;
  2. OSN:重传的是哪个原始数据的序列包,注意我们RTX属于RTP协议,这儿的Original指的是RTP包;
  3. 可以看出RTX就是普通的RTP协议加了2字节的OSN,注意不是RTCP;

五、抓取RTX包:

5.1、获取本机的RTX流:

在这里插入图片描述

看看RTX的seq:

在这里插入图片描述

seq独立可以更好的统计出丢包。

5.2、看看原始流哪些包丢失了:

在这里插入图片描述

BLP是0x0001。知道BLP是什么吗?

1)NACK PID和NACK BLP:

1)概念:

在 NACK 中,NACK PID 和 NACK BLP 是两个关键的字段,用于标识和管理需要重传的数据包。

  • NACK PID:NACK PID(Packet ID)是 NACK 报文中的一个字段,用于指示需要重传的丢失数据包的序列号。当接收端检测到数据包丢失时,会发送 NACK 报文,其中 NACK PID 指示了具体丢失的数据包的序列号。
  • NACK BLP:NACK BLP(Bitmask of Lost Packets)是 NACK 报文中的另一个字段,用于指示一连串连续丢失的数据包。NACK BLP 是一个比特掩码,每个比特位对应一个数据包序列号,用于表示一段连续的丢失数据包范围。

2)举个例子:

假设在一个 WebRTC 实时通信会话中,发送端发送了一系列 RTP 数据包给接收端,序列号分别为 10、11、12、13、14、15。接收端在接收过程中发现数据包 11 和 13 丢失了。接收端会发送 NACK 报文给发送端,请求重传这两个丢失的数据包。

在这个例子中,NACK 报文中的 NACK PID 和 NACK BLP 可能会被设置如下:

  • NACK PID:11, 13
    • NACK PID 指示需要重传的具体丢失数据包的序列号,即数据包 11 和数据包 13。
  • NACK BLP:010010
    • 在这个二进制掩码中,每个比特位对应一个数据包序列号。接收端标记了丢失的数据包范围,从 10 到 15 中的第 2 和第 4 个数据包丢失。
    • 这表示数据包 11 和数据包 13 丢失,而其他数据包正常接收。

3)本文丢包情况:

NACK 报文中 NACK PID 的值为 6806,而 NACK BLP 的值为 0x0001,可以解释如下:

  • NACK PID:6806
    • NACK PID 表示需要重传的具体丢失数据包的序号,即数据包序号为 6806 的数据包需要进行重传。
  • NACK BLP:0x0001
    • NACK BLP 是一个十六进制数,转换为二进制为 0000 0000 0000 0001。
    • 在这个二进制掩码中,每个比特位对应一个数据包序号。由于只有一个比特位为 1,表示只有一个数据包丢失。
    • 在这种情况下,第一个(最低位)的 1 表示序号为 6806 的数据包丢失,其它数据包接收正常。

5.3、使用RTX进行重传:

如果你在抓包软件中找不到RTX包,记得使用时间戳来找。

在这里插入图片描述

因此发送的RTX一定在这个点之后;

在这里插入图片描述

  1. 可以看出872691小于收到NACK的时间点876269,肯定不是。
  2. 881999大于收到NACK的时间点,可能是。
  3. 通过前面协议我们知道RTX的前2个字节是OSN,表示原始数据的序列号,因此,我们将0x1a96转换为十进制看是多少;

在这里插入图片描述

看到没有,重传的额就是6806这个数据包。

六、发送RTX的过程:

我们得先看下NACK接收流程:

在这里插入图片描述

会来到这里:

void RTPSender::OnReceivedNack(const std::vector<uint16_t>& nack_sequence_numbers,int64_t avg_rtt) {packet_history_->SetRtt(5 + avg_rtt);// 遍历nack中每个需要重传的seq,进行重传for (uint16_t seq_no : nack_sequence_numbers) {// 发送RTXconst int32_t bytes_sent = ReSendPacket(seq_no);if (bytes_sent < 0) {// Failed to send one Sequence number. Give up the rest in this nack.RTC_LOG(LS_WARNING) << "Failed resending RTP packet " << seq_no<< ", Discard rest of packets.";break;}}
}

看看具体怎么重传的:

int32_t RTPSender::ReSendPacket(uint16_t packet_id) {absl::optional<RtpPacketHistory::PacketState> stored_packet =packet_history_->GetPacketState(packet_id);if (!stored_packet || stored_packet->pending_transmission) {return 0;}const int32_t packet_size = static_cast<int32_t>(stored_packet->packet_size);const bool rtx = (RtxStatus() & kRtxRetransmitted) > 0;std::unique_ptr<RtpPacketToSend> packet =packet_history_->GetPacketAndMarkAsPending(packet_id, [&](const RtpPacketToSend& stored_packet) {std::unique_ptr<RtpPacketToSend> retransmit_packet;if (retransmission_rate_limiter_ &&!retransmission_rate_limiter_->TryUseRate(packet_size)) {return retransmit_packet;}if (rtx) {retransmit_packet = BuildRtxPacket(stored_packet);} else {retransmit_packet =std::make_unique<RtpPacketToSend>(stored_packet);}if (retransmit_packet) {retransmit_packet->set_retransmitted_sequence_number(stored_packet.SequenceNumber());}return retransmit_packet;});if (!packet) {return -1;}packet->set_packet_type(RtpPacketMediaType::kRetransmission);packet->set_fec_protect_packet(false);std::vector<std::unique_ptr<RtpPacketToSend>> packets;packets.emplace_back(std::move(packet));paced_sender_->EnqueuePackets(std::move(packets));return packet_size;
}

我们根据代码逻辑发现,并不一定要使用RTX:

  1. 如果启用了 RTX(Retransmission),则构造并发送独立的 RTX 包。
  2. 如果没有启用 RTX,则直接从包历史记录中取出丢失的原始 RTP 包并重传。

代码比较负责,关键部分展开说明下:

1)构造重传包:

  • 匿名函数:

    std::unique_ptr<RtpPacketToSend> packet =packet_history_->GetPacketAndMarkAsPending(packet_id, [&](const RtpPacketToSend& stored_packet) {std::unique_ptr<RtpPacketToSend> retransmit_packet;// 检查重传速率限制if (retransmission_rate_limiter_ &&!retransmission_rate_limiter_->TryUseRate(packet_size)) {return retransmit_packet;}if (rtx) {// 如果支持 RTX,构造一个 RTX 包retransmit_packet = BuildRtxPacket(stored_packet);} else {// 否则直接使用原始 RTP 包retransmit_packet =std::make_unique<RtpPacketToSend>(stored_packet);}if (retransmit_packet) {retransmit_packet->set_retransmitted_sequence_number(stored_packet.SequenceNumber());}return retransmit_packet;});

    这段代码的核心是获取丢失的历史包并将其打包为重传包:

    1. 调用 packet_history_->GetPacketAndMarkAsPending
      • 从历史记录中取出丢失的 RTP 包,并标记该包已进入待发送状态(防止重复重传)。
    2. 使用匿名函数对包进行加工处理
      • 重传速率限制:
        • 如果当前重传速率超出了允许的带宽,则直接丢弃该重传包,不做进一步处理。
      • RTX 构造:
        • 如果启用了 RTX,通过调用 BuildRtxPacket 方法,将原始包封装为 RTX 包。
        • 关键点:RTX 包的特殊结构包含原始包的 OSN(Original Sequence Number),用于表示其原始 RTP 包的序列号。
      • 普通重传包:
        • 如果没有启用 RTX,则直接复制原始 RTP 包来重发。
      • 设置序列号:
        • 调用 set_retransmitted_sequence_number,设置重传包所对应的原始序列号。

2)重传包的最终处理:

if (!packet) {return -1;
}packet->set_packet_type(RtpPacketMediaType::kRetransmission);
packet->set_fec_protect_packet(false);
std::vector<std::unique_ptr<RtpPacketToSend>> packets;
packets.emplace_back(std::move(packet));// 将重传包加入到 paced sender 的发送队列
paced_sender_->EnqueuePackets(std::move(packets));
  1. 检查是否成功获取重传包:
    • 如果没有生成有效的重传包(可能被带宽限制丢弃),返回 -1
  2. 设置包类型:
    • 通过 set_packet_type 设置为 kRetransmission,标明这是一个重传包。
    • set_fec_protect_packet(false):通知不对重传包应用 FEC(前向纠错)保护,因为 RTX 本身是另一个冗余机制。
  3. 插入发送队列:
    • 将重传包添加到 paced_sender_(节奏发送器)模块中,以有节奏地发送包,避免瞬时大量重传耗尽带宽。

3)小结:

  1. RTX 重传的实现流程
    • RTX 重传包通过函数 BuildRtxPacket 生成。
    • RTX 中包含一个特殊的字段 OSN,用于恢复原始包的序列号。
    • 通过独立的 SSRC 和 Payload Type 区分 RTX 包与普通主流包。
  2. 带宽管理和速率控制
    • retransmission_rate_limiter_ 限制了重传包的发送速率,防止网络因重传过载。
  3. 灵活处理重传策略
    • 支持两种模式:RTX 和普通 RTP 重传。
    • RTX 是更成熟的丢包恢复机制,但需要双方支持;否则退回到简单的 RTP 重传方式。

七、总结:

RTPSender::ReSendPacket 函数是 WebRTC 中 RTP 协议层实现丢包恢复的重要部分。它利用 RtpPacketHistory 记录和 RTX 协议实现高效的重传机制,同时考虑到带宽管理、速率限制和两种重传策略(RTX 和普通 RTP)的兼容性。在具体代码实现中,它通过灵活的封装和严格的速率控制,保证了无损恢复的同时避免了网络拥塞情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/64956.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国自然联合项目|影像组学智能分析理论与关键技术|基金申请·24-12-25

小罗碎碎念 该项目为国自然联合基金项目&#xff0c;执行年限为2019年1月至2022年12月&#xff0c;直接费用为204万元。 项目研究内容包括影像组学分析、智能计算、医疗风险评估等&#xff0c;旨在通过模拟医生诊断过程&#xff0c;推动人工智能在医疗领域的创新。 项目取得了…

轮播图带详情插件、uniApp插件

超级好用的轮播图 介绍访问地址参数介绍使用方法&#xff08;简单使用&#xff0c;参数结构点击链接查看详情&#xff09;图片展示 介绍 带有底部物品介绍以及价格的轮播图组件&#xff0c;持续维护&#xff0c;uniApp插件&#xff0c;直接下载填充数据就可以在项目里面使用 …

Java 本地缓存实现:Guava Cache、Caffeine、Ehcache 和 Spring Cache

文章目录 一、引言二、Guava Cache理论介绍实战演示 三、Caffeine理论介绍实战演示 四、Ehcache理论介绍实战演示 五、Spring Cache理论介绍实战演示 六、总结 一、引言 在现代应用程序开发中&#xff0c;缓存是提高性能和响应速度的关键技术之一。Java 提供了多种本地缓存解决…

计算机网络B重修班-期末复习

[TOC] (计算机网络B重修班-期末复习&#xff09; 一、单选 &#xff08;20题&#xff0c;1分/题&#xff0c;共20分&#xff09; 二、判断 &#xff08;10题&#xff0c;1分/题&#xff0c;共10分&#xff09; 三、填空 &#xff08;10题&#xff0c;1分/题&#xff0c;共10…

QT的前景与互联网岗位发展

qt是用来干什么的 --》桌面应用开发&#xff08;做电脑的应用程序&#xff0c;面对客户端&#xff09;。 主要用于开发跨平台的应用程序和用户界面&#xff08;UI&#xff09;。它是一个全面的C库集合&#xff0c;提供了构建软件应用所需的各种工具和功能。 客户端开发的重…

重温设计模式--单例模式

文章目录 单例模式&#xff08;Singleton Pattern&#xff09;概述单例模式的实现方式及代码示例1. 饿汉式单例&#xff08;在程序启动时就创建实例&#xff09;2. 懒汉式单例&#xff08;在第一次使用时才创建实例&#xff09; 单例模式的注意事项应用场景 C代码懒汉模式-经典…

Java字符串的|分隔符转List实现方案

字符串处理 问题背景代码实现代码优化原因分析实现方案 注意事项异常处理Maven未识别异常 问题背景 在项目组对账流程中&#xff0c;接收对方系统的对账文件&#xff0c;数据以|为分隔符&#xff0c;读取文件内容&#xff0c;分条入库。 代码实现 Java中将字符串转给list&am…

项目底链华为链切换长安链经验总结

项目底链华为链切换长安链经验总结 前言业务需求分析智能合约重写k-v存储结构设计设计上链存储的结构体使用迭代器查询历史记录长安链合约编辑器历史记录返回错误材料上链非必传字段 Int 类型自动赋值长安链cmc工具部署合约ca证书需齐全分页查询截取处理&#xff0c;返回 nil处…

【机器学习】从流动到恒常,无穷中归一:积分的数学诗意

文章目录 微积分基础&#xff1a;理解变化与累积的数学前言一、积分概述与基础概念1.1 积分的定义与重要性1.1.1 积分的基本组成1.1.2 积分在机器学习中的应用 1.2 积分的历史与发展 二、积分的基本概念与计算2.1 不定积分2.1.1 不定积分的定义2.1.2 不定积分的计算方法2.1.3 实…

在瑞芯微RK3588平台上使用RKNN部署YOLOv8Pose模型的C++实战指南

在人工智能和计算机视觉领域,人体姿态估计是一项极具挑战性的任务,它对于理解人类行为、增强人机交互等方面具有重要意义。YOLOv8Pose作为YOLO系列中的新成员,以其高效和准确性在人体姿态估计任务中脱颖而出。本文将详细介绍如何在瑞芯微RK3588平台上,使用RKNN(Rockchip N…

电磁兼容(EMC):一文解读磁芯复合材料——塑磁

目录 01 塑磁的定义 02 塑磁的常见规格型号 03 塑磁材料的优点 04 塑磁的应用 塑磁,也称为注塑磁,是一种将磁性粉末注入到塑料基体中制成的复合磁体材料。以下是塑磁的定义、应用和材料特性的总结: 01 塑磁的定义 塑磁是以塑料为基体,通过特殊工艺在其中加入磁性粒子(…

五种msvcr100.dll丢失的解决方法,有效修复msvcr100.dll丢失错误!跟msvcr100.dll错误问题说拜拜!

在日常电脑使用过程中&#xff0c;尤其是运行某些应用程序或游戏时&#xff0c;可能会遇到“msvcr100.dll丢失”的错误提示。这个动态链接库&#xff08;DLL&#xff09;文件是Microsoft Visual C Redistributable for Visual Studio 2010的一部分&#xff0c;对于许多程序的正…

redis数据类型:list

数据结构 源码版本&#xff1a;7.2.2路径&#xff1a;src/adlist.h 关于list的 头文件中涉及到的这三个结构体如下 /* Node, List, and Iterator are the only data structures used currently. */ # 节点 typedef struct listNode {struct listNode *prev; # 前元素的指针s…

计算机网络(网络层)

1、ARP协议(已知ip地址得到mac地址) 在传输一个 IP 数据报的时候&#xff0c;确定了源 IP 地址和目标 IP 地址后&#xff0c;就会 通过主机「路由表」确定 IP 数据包下一跳。然而&#xff0c;网络层的下一层是数据链路层&#xff0c;所以我们还要知道「下一跳」的 MAC 地址。由…

Linux下学【MySQL】表中插入和查询的进阶操作(配实操图和SQL语句通俗易懂)

绪论​ 每日激励&#xff1a;挫折是会让我们变得越来越强大的重点是我们敢于积极的面对它。—Jack叔叔 绪论​&#xff1a; 本章是表操作的进阶篇章&#xff08;没看过入门的这里是传送门&#xff0c;本章将带你进阶的去学习表的插入insert和查找select&#xff0c;本质也就是…

[Rust开发]actix_webmiddleware 中间件

actix_web::middleware 在 Actix Web 框架中扮演着重要的角色&#xff0c;它允许开发者在处理 HTTP 请求和响应的过程中插入自定义的逻辑。中间件可以在请求到达处理函数之前或响应返回给客户端之前执行&#xff0c;从而实现日志记录、身份验证、数据验证、错误处理等功能。 为…

WebLogic T3反序列化漏洞(CVE-2018-2628)--vulhub

WebLogic T3反序列化漏洞(CVE-2018-2628) WebLogic在通信过程中使用T3协议传输数据&#xff0c;涉及到了序列化和反序列化操作。 T3协议概述 T3协议是Oracle的私有协议&#xff0c;所以公开的相关资料比较少&#xff0c;这里结合其他师傅的博客简单对T3协议进行一个简要分析…

OpenFeign快速入门 示例:黑马商城

使用起因 之前我们利用了Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。这样一来购物车虽然通过远程调用实现了调用商品服务的方法,但是远程调用的代码太复杂了: 解决方法 并且这种调用方式比较复杂&#xff0c;一会儿远程调用&#xff0c;一会儿本地调用。 因…

计算机的错误计算(一百九十)

摘要 用两个大模型计算cot(1.234). 其中&#xff0c;1.234是以弧度为单位的角度。结果保留10位有效数字。实验表明&#xff0c;两个的计算公式虽然不同&#xff0c;但是都是正确的。然而&#xff0c;数值计算则是有问题的---包括每一个中间运算与结果。 例1. 计算cot(1.234)…

【QSS样式表 - ⑥】:QPushButton控件样式

文章目录 QPushBUtton控件样式QSS示例 QPushBUtton控件样式 常用子控件 常用伪状态 QSS示例 代码&#xff1a; QPushButton {background-color: #99B5D1;color: white;font-weigth: bold;border-radius: 20px; }QPushButton:hover {background-color: red; }QPushButton:p…