WebRTC-NACK、Pacer和拥塞控制和FEC

news2024/11/15 9:10:56

NACK机制

发送端实现NACK的三个重点流程:

发送RTP报文,实时存储报文到packet_history_队列

处理接收到的RTCP NACK报文

把nack包里的序号放到nack_sequence_numbers丢包队列

重发NACK反馈的RTP报文

重发报文这里有三点需要注意:

1)会判断上次重传报文时间和当前时间差是否大于RTT,若小于则不重传。

2)NACK重新发送媒体数据有两种方式:单独RTX通道发送、与媒体数据混在一起发送

两种形式对单纯的NACK抗性影响不太大,但是与媒体数据混在一起发送模式,接收端无法区分是NACK重传报文,还是正常媒体数据,会导致接收端反馈的丢包率低于实际值,影响gcc算法探测码率,及发送端FEC冗余度配置。所以建议还是以RTX通道单独发送。

3)RTPSender::ReSendPacket在将重传数据加入pacer队列,会设置报文优先级,为了保证实时性,NACK重传报文需要按照高优先级重传。

优先级配置在set_packet_type,发送报文时,会根据kRetransmission获取发送优先级。

接收端NACK

webrtc接收端触发发送NACK报文有两处:

1、接收RTP报文,对序列号进行检测,发现有丢包,立即触发发送NACK报文。(NackModule::OnReceivedPacket->NackModule::GetNackBatch)

2、定时检查nack_list_队列,发现丢包满足申请重传条件,立即触发发送NACK报文。(其中NackModule::Process是挂载在接收RTP报文线程的一个定时任务,调用NackModule::GetNackBatch,调度周期是kProcessIntervalMs(默认20ms))

重点函数:

核心函数

NackModule::AddPacketsToNack、NackModule::GetNackBatch是NACK核心函数。

NackModule::AddPacketsToNack

决定是否将该报文放入NACK队列

1、nack_list的最大长度为kMaxNackPackets,多的包会循环清空nack_list中关键帧之前的包

2、nack_list中的最新加入的包和最老的包序号之间不超过kMaxPacketAge,以保证nack请求列表中不会有太老旧的包号。

NackModule::GetNackBatch

决定是否发生NACK请求重传该报文。两种触发方式都是调用这个函数决定是否发送NACK请求重传。

该函数的中心思想是:

1、因为报文有可能出现乱序抖动情况,不能说检测出丢包就立即重传,需要等待send_nack_delay_ms_,当等待时间大于send_nack_delay_ms_,申请重传。

2、发送NACK报文有两种实现方式:一种是基于序列号,判断丢包的序列号小于当前序列号,另一种基于时间发送端重发丢失的包,当两次发送NACK重传请求时间大于rtt_ms_时,才会申请再次重传。

3、视频会议场景对实时性要求很高,当报文一直处于丢包状态,不能持续申请重传,最大重传次数为kMaxNackRetries,超过最大重传次数,放弃该报文。不再重传。

pacer实现

背景介绍

  • 一帧音频数据本身不大,不会超过以太网的最大报文长度。一个RTP报文可以搞定,按照打包时长的节奏发送就可以。但视频数据不能按照音频数据的思路发送,一帧视频可能很大,远大于以太网的1500byte,需要分别封装在几个RTP报文中,若这些视频帧RTP报文一起发送到网络上,必然会导致网络瞬间拥塞。产生丢包抖动等异常。

  • 大多数编解码格式下,一帧音频数据长度固定,音频码率持续平稳。码率不会出现忽高忽低现象。但是一帧视频数据长度受内容影响严重。I、P、B帧间的长度相差非常大。直接发送网络波动幅度很大。因为编码器特别是视频编码出来的码率是波动的,但是发送速率需要保持稳定

Pacer的目的就是让视频数据按照评估码率均匀的在各个时间片发送出去。

设计PACER模块主要解决三个问题:怎么发送报文、什么时候发报文、每次发多少数据量。

怎么发送报文

音频、视频、NACK、FEC、Padding报文都要统一从Pacer模块发送。所以音视频编码数据RTP切分打包后,首先将RTP报文存在pace queue队列,并将报文元数据(packet id, size, timestamp, 重传标示)送到pacer queue进行排队等待发送,插入队列的元数据会进行优先级排序。

pace queue是一个基于优先级排序的多维链表,它并不是一个先进先出的fifo,而是一个按优先级排序的list。报文优先级规则是:

  • 优先级高的报文排在fifo的前面,低的排在后面;

  • 首先判断报文的priority等级,等级越小的优先级越高(priority等级根据报文类型进行分类);

根据报文类型确定数据优先级处理函数如下:(音频>重传>视频>fec>padding)

int GetPriorityForType(RtpPacketMediaType type) {

// Lower number takes priority over higher.

switch (type) {

case RtpPacketMediaType::kAudio:

// Audio is always prioritized over other packet types.

return kFirstPriority + 1;

case RtpPacketMediaType::kRetransmission:

// Send retransmissions before new media.

return kFirstPriority + 2;

case RtpPacketMediaType::kVideo:

case RtpPacketMediaType::kForwardErrorCorrection:

// Video has "normal" priority, in the old speak.

// Send redundancy concurrently to video. If it is delayed it might have a

// lower chance of being useful.

return kFirstPriority + 3;

case RtpPacketMediaType::kPadding:

// Packets that are in themselves likely useless, only sent to keep the

// BWE high.

return kFirstPriority + 4;

}

RTC_CHECK_NOTREACHED();

}

Pacer每次触发发送事件时先从queue的最前面取出优先级最高的报文进行发送,这样做的目的是让视频在传输的过程中延迟尽量小,重传的报文尽快能到达防止等待卡顿。pace queue还可以设置最大延迟,如果超过最大延迟,会计算queue中数据发送所需要的码率,并且会把这个码率替代target bitrate作为budget参考码率来加速发送。

什么时候发报文

PacingController::NextSendTime、PacingController::ProcessPackets是PACER模块两个核心函数,PacingController::ProcessPackets按照PacingController::NextSendTime控制的节奏周期调用。完成PACER平滑发送功能。

PacingController::NextSendTime在控制发送节奏上,有两种模式kPeriodic、kDynamic。kDynamic还没理解透,这里先记录kPeriodic实现方式。kPeriodic模式下,固定每隔5ms调用一次发送报文任务。

每次发多少数据量

media_budget_算出当前时间片网络可以发送多少数据,然后从pacer queue当中取出报文元数据进行网络发送。

media_budget_根据评估出来的目标码率和时间差计算这次定时事件能发送多少字节:

delta time:上次检查时间点和这次检查时间点的时间差。

target bitrate:pacer的参考码率,是由probe模块根据网络探测带宽评估出来的。

remain_bytes:每次触发发包时会减去发送报文的长度size,如果remain_bytes > 0,继续从pace queue中取下一个报文进行发送,直到remain_bytes <=0 或者 pace queue没有更多的报文。如果pacer queue没有更多待发送的报文,但media_budget_计算出还可以发送更多的数据,这个时候pacer会进行padding报文补充

Pacer模块引入延时规避方法

max_pacing_delay:

PacingController::ProcessPackets会实时计算当前处理方式会引入的系统延时,当延时大于设定目标上限值(max_pacing_delay),需要及时调整Pacer目标码率,保证当前数据都能及时发送出去,保证Pacer模块引入延时时间可控。

编码算法码控模块配合:

Pacer模块实现不复杂,但是要想真正做好Pacer功能,仅仅靠一个Pacer模块是玩不转的,需要与视频编码器的码控模块配合:

  • 首先探测模块配置码率给编码器,编码器一定要保证在可控周期内码率收敛到配置的码率参数值以内,否则会给Pacer模块造成的累计延时越来越大压力;

  • 另外IP帧rate也要在合理范围内。若I帧超大,势必导致关键帧传输延时变大,影响端到端系统延时。

拥塞算法--googCC算法(webrtc中使用)

  • 控制发送端的发送码率

  • WebRTC防止拥塞的根基是有准确的带宽评估方法。它提供了两种带宽评估方法,一种是基于丢包的带宽评估,另一种是基于延时的带宽评估而基于延时的评估方法又分为接收端(Goog-REMB)和发送端(TCC,transportCC)的带宽评估方法,目前默认采用的是Goog-TCC方法,因为其相对来说更为精准。

发送端基于丢包率的码率控制

接收端基于延迟的码率控制

基于延时的带宽评估方法比基于丢包的评估更好一些,因为它可以提前预估是否发生了拥塞。基于丢包的评估丢包率一旦超过10%就说明可能已经发生拥塞了,而网络一旦拥塞,再想恢复回原来的状态,需要花费一段时间,而这段时间就会影响音视频的服务质量。

而基于延时的带宽评估就不会产生这种情况。它的基本原理是,如果接收到的数据包的网络传输时延在持续增长,就说明网络变差了,当达到一定程度时,就要将评估的带宽值降下来,以防止发生网络拥塞。它的计算公式是根据状态机来的(状态机比较复杂,我这里就不讲了),当状态非常好时,需要增加带宽,同丢包增加带宽一样,每次增加8%;如果延时一直累加,则需要降低带宽,带宽降为原来85%,其它情况就保持当前带宽,无增无减。

为什么TCC比REMB准确?

TCC和REMB主要有两个区别。第一是计算的端不同,REMB是在接收端计算的,接收端计算后再将结果返回给发送端进行控制,而在回传结果时,可能网络又发生了新的变化,这就造成了REMB的及时性不够;TCC是将所有数据都交给发送端做计算和控制,因此及时性和准确度会更高。第二是滤波器不同,REMB是卡尔曼滤波器(Kalman),TCC是最小二乘法滤波器(Trend line)。最小二乘法滤波器在网络延时评估这方面比卡尔曼滤波器效果更好一些。

FEC算法

发送端将负载数据加上一定的冗余纠错码一起发送,接收端根据接收到的纠错码对数据进行差错检测,如果发现差错,则利用纠错码进行纠错。buildRedPacket()

基础理论很简单,利用异或的原理进行数据恢复,当然有一些代价,就是需要多占用一些带宽,用来发送冗余数据

一、概述

webrtc冗余打包方式有三种:Red(rfc2198)、Ulpfec(rfc5109)、Flexfec(草案)。其中Red和Ulpfec要成对使用。

二、RedFEC

简单将老报文打包到新包上。如下图所示,冗余度为1时,RFC2198打包情况:

这种方法在音视频领域几乎不使用,因为冗余包只能保护特定一个报文,这种方法带宽占用量很大,恢复能力有限,性价比很低。只是早期的T38传真、RFC2833收号会使用该协议,因为传真和收号的数据量比较小。

webrtc里面说使用了RFC2198冗余,实际上仅仅是借用该协议的封装格式,封装FEC冗余报文。

三、UlpFEC

将一组M个报文进行异或,生成N(N就是FEC的冗余度)个FEC报文,打包出去。这组报文任意丢其中的N个,都可以通过这组(M-N)个报文+FEC冗余包恢复回来,比简单的RFC2198保护的范围扩大了很多。例如下面示意图:D为媒体包,R为冗余包,该图所示的冗余度为1。

若UlpFEC异或所有报文,带宽占用量也比较大,在实际应用会根据网络情况进行适当取舍。

四.FlexFEC

五.FEC算法汇总

FEC是无线传输领域的一个前向纠错的算法。网上搜资料的时候经常把无线的算法看的云里雾里的,研究半天都不知道这个和视频传输有什么关系。

无线传输领域的FEC算法主要有TURBO、LDPC、POLAR这三种。

音视频传输领域的FEC算法有如下几种:

1、webrtc的opus音频使用的是inband FEC和交织编码

2、webrtc的视频ulpfec使用的是异或XOR

3、Reed Solomon算法比较复杂,理论上数据恢复能力比较强。

六、webrtc代码分析

webrtc默认使能Red+Ulp的FEC。Flex仅在实验阶段,还不能正式使用。

将UlpFEC编码数据封装为RED包

原文:WebRTC-NACK、Pacer和拥塞控制和FEC - 资料 - 我爱音视频网 - 构建全国最权威的音视频技术交流分享论坛

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

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

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

相关文章

Allegro如何查看PCB上器件的库路径操作指导

Allegro如何查看PCB上器件的库路径操作指导 在做PCB设计的时候,有时需要检查PCB上器件使用的库的路径是否正确,Allegro支持快速将PCB上所有器件的库路径都列出来 如下图 如何显示这个报表,具体操作如下 点击Tools点击Report

蓝桥杯-求和问题

蓝桥杯-求和问题1、问题描述2、解法一&#xff1a;暴力解法(两层循环)3、解法二&#xff1a;结合律(一层循环解决)1、问题描述 给定 n 个整数 a1,a2,...,ana_1,a_2,...,a_na1​,a2​,...,an​&#xff0c;求它们两两相乘再相加的和&#xff0c;即&#xff1a; Sa1.a2a1.a3...a…

Presto 在美图的实践

导读&#xff1a;本文的主题是Presto高性能引擎在美图的实践&#xff0c;首先将介绍美图在处理ad-hoc场景下为何选择Presto&#xff0c;其次我们如何通过外部组件对Presto高可用与稳定性的增强。然后介绍在美图业务中如何做到合理与高效的利用集群资源&#xff0c;最后如何利用…

项目管理的主要内容包括哪些?盘点好用的项目管理系统软件

阅读本文您将了解&#xff1a;1、项目管理的主要内容包括哪些2、好用的项目管理软件 项目管理是为了实施一个特定目标&#xff0c;所实施的一系列针对项目要素的管理过程&#xff0c;包括过程、手段以及技术等。 通过项目管理&#xff0c;我们能够提前安排和控制项目的时间、…

深度解析:我如何用300并发把数据库压挂了

问&#xff1a;为什么300的并发能把支持最大连接数4000数据库压死&#xff1f; 买了一台数据库&#xff0c;最大连接数的参数是 4000&#xff0c;看起来很棒&#xff01;但是 cpu 和内存并不咋好&#xff01;是 2c4g的超低配制。但是想着反正业务量也不大&#xff0c;不如先扛…

【vulhub漏洞复现】CVE-2018-2894 Weblogic任意文件上传漏洞

一、漏洞详情影响版本weblogic 10.3.6.0、weblogic 12.1.3.0、weblogic 12.2.1.2、weblogic 12.2.1.3WebLogic是美国Oracle公司出品的一个application server&#xff0c;确切的说是一个基于JAVAEE架构的中间件&#xff0c;WebLogic是用于开发、集成、部署和管理大型分布式Web应…

Oracle Primavera P6 登录提示错误“该用户已经登录“(SQLite)

目录 引言 解决思路 使用工具 处理办法 引言 在使用Oracle Primavera P6 非正常退出后&#xff0c;Professional再次登录或出现异常&#xff0c;体现为“该用户已经登录。请使用另一个用户名” 以上为近期一个朋友请教的问题&#xff0c;为了给后续出现同样问题朋友给予解…

有限元中四面体的一些积分公式

文章目录有限元中四面体的相关积分公式有限元中四面体的相关积分公式 在 xyzxyzxyz 坐标系中通过四个点 (xi,yi,zi),(xj,yj,zj),(xm,ym,zm),(xp,yp,zp)(x_i, y_i, z_i), (x_j, y_j, z_j), (x_m, y_m, z_m), (x_p, y_p, z_p)(xi​,yi​,zi​),(xj​,yj​,zj​),(xm​,ym​,zm​…

解决PyCharm下OpenCV没有自动补全、函数提示的问题!

Content找到Python环境下的OpenCV安装目录中的“cv2.pyd”文件复制cv2.pyd文件到site-packages文件夹中重启PyCharm&#xff0c;cv2就可以正常使用了最近使用PyCharm编写一段需要使用Opencv库的代码&#xff0c;却发现cv2没有自动补全和函数提示了。博主自己找到以下解决办法&a…

ThreadLocal使用

1、简介ThreadLocal类用来提供线程内部的局部变量&#xff0c;不同的线程之间不会相互干扰这种变量在多线程环境下访问&#xff08;通过get和set方法访问&#xff09;时能保证各个线程的变量相对独立于其他线程内的变量在线程的生命周期内起作用&#xff0c;可以减少同一个线程…

CentOS系统编译安装PHP-5.6.27版本

一、手动安装编译工具&#xff1a; yum install -y gcc gcc-c 二、添加用户和用户组&#xff1a; groupadd web useradd -M -s /sbin/nologin -g web php 三、yum安装依赖&#xff1a; yum -y install libmcrypt libmcrypt-devel mcrypt mhash libxml2-devel libpng-devel l…

适应多场景的云桌面metaScreenshare1.0 sdk

概述 metaRTC新推出云桌面metaScreenshare1.0 sdk版本&#xff0c;基于metaIPC1.0搭建&#xff0c;基于mqtt通信&#xff0c;同时支持windows/linux/android操作系统远程桌面控制&#xff0c;支持Nvidia和Intel的GPU编码&#xff0c;适应多种业务场景&#xff0c;可方便集成到…

ElasticSearch 学习笔记总结(四)

文章目录一、ES继承 Spring Data 框架二、SpringData 功能集成三、ES SpringData 文档搜索四、ES 优化 硬件选择五、ES 优化 分片策略六、ES 优化 路由选择七、ES 优化 写入速度优化七、ES 优化 内存设置八、ES 优化 重要配置一、ES继承 Spring Data 框架 Spring Data 是一个用…

【案例教程】拉格朗日粒子扩散模式FLEXPART

拉格朗日粒子扩散模式FLEXPART通过计算点、线、面或体积源释放的大量粒子的轨迹&#xff0c;来描述示踪物在大气中长距离、中尺度的传输、扩散、干湿沉降和辐射衰减等过程。该模式既可以通过时间的前向运算来模拟示踪物由源区向周围的扩散&#xff0c;也可以通过后向运算来确定…

CKKS自举笔记(CKKS Bootstrapping)

文章目录CKKS Bootstrapping流程流程的框架如何做同态取模操作直接泰勒展开&#xff08;naive idea&#xff09;采用二倍角公式来拟合&#xff08;欧密2018&#xff09;如何做同态编码或解码CKKS的编码和解码基础知识&#xff08;明文下面怎么做&#xff09;同态的旋转、共轭&a…

Linux 进程:进程控制

目录一、进程创建1.fork2.vfork二、进程终止三、进程等待四、进程替换1.理解程序替换2.子进程在程序替换中的作用Linux的进程控制分为四部分&#xff1a; 进程创建进程终止进程等待进程替换 一、进程创建 常见的创建进程的函数有两个&#xff1a; pid_t fork(void)pid_t vf…

一篇文章帮助你初步了解CDN内容分发网络

文章目录CDN内容分发网络CDN内容分发网络的工作原理CDN的作用CDN如何实现内容的加速CDN内容分发网络 CDN&#xff08;Content Delivery Network&#xff09;内容分发网络。CDN 是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过…

手撕CSDN博文:学用curl命令获取博文页面源码,学不会爬虫先手剥CSDN博文阅读点赞收藏和评论数量

学用curl命令获取博文页面源码&#xff0c;学不会爬虫先手剥CSDN博文阅读点赞收藏和评论数量。 (本文获得CSDN质量评分【xx】)【学习的细节是欢悦的历程】Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&…

客户服务软件推荐榜:28款!

在这个竞争激烈的时代&#xff0c;做到服务对企业的存亡有着深刻的意义。改善客户服务&#xff0c;做好客户服务工作&#xff0c;是关键&#xff0c;因为客户服务团队代表着企业的形象&#xff0c;面孔&#xff0c;客户有可能 不大会记得企业的某个东西&#xff0c;但是他们将会…

module java.base does not “opens java.xxx“ to unnamed module @xxxx

错误截图 在springboot集成dubbo中 消费者服务和生产者复核都报错 错误原因 高版本JDK禁止了报错所提示的几个包的反射 而dubbo里用到了 解决 看自己的报错里有几个包被禁止了 我这有两个java.math和java.lang 添加两个JVM启动参数 –add-opens java.base/java.mathALL…