mediasoup丢包重传机制的实现

2023-11-23 04:21

本文主要是介绍mediasoup丢包重传机制的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一. 前言

二. 丢包重传的工作机制

三. NACK报文

四. RTX报文

五. mediasoup server处理丢包重传的源码剖析

1. 判断包的前后关系

2. 判断丢包产生NACK报文的逻辑

3. 接收处理NACK报文并重传RTX的逻辑


一. 前言

        在音视频通话中,我们常常使用 RTP 协议包荷载音视频数据,底层经常使用 UDP 作为其传输层协议,而 UDP 并不保证数据可靠性,因此当 RTP 产生丢包时,接收端需要告知源端哪些序号的包认为丢失了,请重传一遍过来。

二. 丢包重传的工作机制

         如上所示,A 与 B 进行媒体协商时会协商媒体的 payload type 以及如果使用重传时使用哪个  payload type 的包作为该 payload 的重传包,上图中 payload type=96 表示 VP8 的 RTP 包,rtcp-fb:96 表示使用 nack,payload type 97 作为 payload type 96 的重传包。

        A 发送 RTP 报文给 B,B 接收过程中判断到包有丢失时会生成 NACK 报文给 A,告诉 A 哪些序号的包丢失了需要重传,A 收到之后 NACK 报文后从发送历史队列中取出丢失序号的包,打成 RTX 包重传给 B。

三. NACK报文

        在这篇文章我们介绍了 RTCP 协议,它经常跟 RTP 协议配合使用以改善传输质量,RTCP 协议的 RTPFB 报文是用于传输层质量反馈的。

        RTPFB 称为传输层反馈报文,当 PT=205,FMT=1 时表示该协议包为丢包反馈报文,又称 NACK 报文。SSRC 表示源端发送者的 SSRC,RTPFB 协议包 FMT 不同时 FCI 包含的具体字段也不同, 对于 NACK 反馈包 FCI 包含两个字段(PID,BLP),具体参考 RFC4585。

PID(Packet Identifier):发生丢失的 RTP 包序号

BLP(Bitmap of Lost Packets):从 PID 开始接下来 16 个 RTP 数据包的丢失情况,一个 NACK 报文可以携带多个连续 RTP 包丢失的信息

例如当 PID = 100,BLP = 0b0000100010001000 时表示序号为 104,108,112 的 RTP 包丢失了。

四. RTX报文

RTX 包可以用来重传丢失的包,协议格式如上,关于 RTX 包的 RTP Header 字段,有如下需要说明的点。

1. RTX 包有自己的 SSRC,与原始 RTP 包使用的 SSRC 不同

2. RTX 包有自己的 payload type,与原始 RTP 包的 payload type 不同

3. RTX 包的 sequence number 是按自己的顺序排序的,并不是丢失包的 sequence number

OSN(Origin Sequence Number):表示对应哪个丢失序号的 RTP 包

Origin RTP Packet Payload:表示原先丢失序号的包的负载

协议更多细节可以参考 RFC4588。

五. mediasoup server处理丢包重传的源码剖析

1. 判断包的前后关系

        在 RTP 协议中序列号是使用无符号 16 位表示的,它可以表示 [0, 65535],当序列号使用到最大值时又要回溯到 0 重新开始增长,而回溯后的序列号为 0 的包是在位于序列号为 65535 之后的,因此不能简单使用 <,> 进行比较,mediasoup 中 SeqManager 模板类处理该逻辑。

template<typename T>
bool SeqManager<T>::SeqLowerThan::operator()(const T lhs, const T rhs) const
{return ((rhs > lhs) && (rhs - lhs <= MaxValue / 2)) ||((lhs > rhs) && (lhs - rhs > MaxValue / 2));
}template<typename T>
bool SeqManager<T>::SeqHigherThan::operator()(const T lhs, const T rhs) const
{return ((lhs > rhs) && (lhs - rhs <= MaxValue / 2)) ||((rhs > lhs) && (rhs - lhs > MaxValue / 2));
}template<typename T>
const typename SeqManager<T>::SeqLowerThan SeqManager<T>::isSeqLowerThan{};template<typename T>
const typename SeqManager<T>::SeqHigherThan SeqManager<T>::isSeqHigherThan{};template<typename T>
bool SeqManager<T>::IsSeqLowerThan(const T lhs, const T rhs)
{return isSeqLowerThan(lhs, rhs);
}template<typename T>
bool SeqManager<T>::IsSeqHigherThan(const T lhs, const T rhs)
{return isSeqHigherThan(lhs, rhs);
}

        对于 SeqManager<T>::IsSeqLowerThan(const T lhs, const T rhs),如果 rhs 大于 lhs 并且差值小于 T 类型所能表示的最大值的一半,或者 lhs 大于 rhs 但是差值大于 T 类型所能表示的最大值的一半,就认为 lhs 是小于 rhs 的。

        可以这样理解,lhs 小于 rhs 时,rhs-lhs 的差值需要较小才能认为 lhs 在 rhs 之前,不然当 lhs=0,rhs=65535 时显然序号已经产生了回溯,没有差值较小的约束就会误认为 lhs 序号的包是在 rhs 序号的包之前的,同理如果 lhs 大于 rhs,lhs-rhs 的差值需要较大才能认为 lhs 在 rhs 之前。

2. 判断丢包产生NACK报文的逻辑

        由于网络存在丢包,抖动等问题,mediasoup server 在接收 RTP 报文时并非理想地按序号递增一的方式接收,当上一次接收到的序号为 lastSeq,而本次接收到的序号 seq > lastSeq + 1 时,mediasoup server 就认为 [lastSeq+1, seq) 区间的 RTP 数据包丢失了,主动产生 NACK 报文发送回源端请求重传,如果经过发送重传请求后 [lastSeq+1, seq) 区间的 RTP 报文仍然一直没有到达服务器,mediasoup server 最多请求 10 次,而且请求间隔不会小于当前 RTT,源码分析如下。

        每次收到 RTP 报文后传入 NackGenerator 模块判断是否需要生成 NACK 报文,如果当前 RTP 报文的 seq 小于上一次接收到的 RTP 报文序号,就在 nackList 查找并删除该 seq,以免之前设置了该 seq 的重传请求后仍然一直在请求重传。

         接下来运行到 AddPacketsToNackList 将 [lastSeq+1, seq) 区间的序号加入到 nackList 中,如果新添加序号后 nackList 大小超过设定阈值 MaxNackPackets(默认配置为 1000),则清理序号较前的 RTP NACK 通知。

         接下来调用 GetNackBatch 获取需要 NACK 的序号集合 nackBatch,并调用 OnNackGeneratorNackRequired(nackBatch) 产生 RTCP-RTPFB-NACK 报文发送给源端。

        由于我们发送的 NACK 报文可能会丢失,源端不一定能知道某些序号的包需要重传,又或者即使源端收到了 NACK 请求,由于网络原因重传的 RTP 包又丢失了,因此需要定时请求 NACK。

        NackGenerator 维护了一个定时器,用于检查在 nackList 集合里的序号上一次 NACK 时间与当前是否已经超过一个 RTT 时间,如果是则放入 nackBatch 集合,等待再次发送 NACK。

        当接收端收到在 nackList 中的该乱序包或者重传恢复包后会将该 seq 从 nackList 中删除,因此如果该包恢复不会一直请求重传至最大次数。

3. 接收处理NACK报文并重传RTX的逻辑

        服务器在发送 RTP 给用户端时也存在丢包乱序等问题,如果开启了 NACK,用户在判断丢包时则会发送 NACK 报文给服务器,服务器接收到该报文后主动重传用户端需要的 RTP 包,源码分析如下。

        首先判断 RTCP 报文类型为 RTPFB NACK 报文后,调用 consumer->ReceiveNack 处理,它会调用 RtpStreamSend::ReceiveNack 处理。

        FillRetransmissionContainer 取出 NACK 报文中要求重传的 RTP 序号,首先判断发送缓存是否有该序号的包,如果服务端已经没有缓存该序号的包则不做处理,接下来如果是有缓存的情况下,判断要求重传序号的 RTP 报文与服务器收到的发送端的最后一个报文的时间差是否超过 MaxRetransmissionDelay(代码里该值为 2s),如果是则不做处理,即可以理解为不能要求重传源端已经产生超过 2s 的数据。

接下来再判断如果要求重传的 RTP 包在该 rtt 周期内已经重传过,也不做处理。

其余情况则将 RTP 包打包成 RTX 重传包然后存放到 RetransmissionContrainer 数组中,等待遍历重传 RTP 包。

RTX 重传包设置逻辑如下,主要是将 RTP 包的 payloadType 换成重传的 payloadType,ssrc 换成重传的 ssrc,序号换成重传维护的序号,然后将 RTP 包负载数据后移两字节,并在负载前两字节位置设置 OSN。

这篇关于mediasoup丢包重传机制的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/415325

相关文章

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too