WebRTC Simulcast介绍

news2024/12/23 15:16:49

原文地址👇

https://blog.livekit.io/an-introduction-to-webrtc-simulcast-6c5f1f6402eb/

你想知道的关于Simulcast的一切

Simulcast是WebRTC中最酷的功能之一,它允许WebRTC会议在参与者网络连接不可预测的情况下进行扩展。在这篇文章中,我们将深入探讨Simulcast的工作原理,以及它如何与像LiveKit这样的SFU协作,以支持更大规模的WebRTC会议。

“慢速”用户(网速慢)的挑战

Selective Forwarding Units(SFU)已经成为基于WebRTC的会议的主流架构。它们的作用是将一个用户的数据转发给房间中的其他用户,从而显著减少每个用户必须发送的数据量。近年来由于SFU扩展相对简单,获得了相当大的普及。由于它们不需要对媒体进行编码/解码,因此转发数据通常只需要很少的CPU开销。

虽然直接转发接收到的流使得架构和程序变得简单,但真实的网络条件提出了一些挑战。具体来说,并非每个人都拥有足够快的互联网连接来接收其他人发布的流。从“慢速”用户加入会议的那一刻起,这个问题就变得非常明显。

用户C将收到断断续续的流

由于慢速用户的下行带宽不足以获取其他用户正在发送的高质量流,所以他们在接收端会遭受高数据包丢失,导致画面质量不连贯或出现黑屏/空白帧。随着下游带宽达到饱和,拥塞也会影响用户上传自己视频的能力。

更糟糕的是,慢速用户的WebRTC客户端将不断向其他发布参与者发送PLI(画面丢失指示)消息(通过SFU),因为它无法接收足够的数据包来渲染帧。当其他WebRTC客户端看到PLI数据包时,它们会通过生成新的关键帧来响应,这些关键帧需要发送给每个人,增加整个会话的带宽需求。增加的带宽需求可能触发级联效应,当它超过其他参与者的带宽限制时。

在一个大量人员的会议中,房间中有人连接欠佳的可能性会增加。每个会议最终都会遇到这个慢速用户的问题。所以,为了确保平滑和高质量的传输,我们有三个选择:

  • 使用可缩放编解码器,如VP9或AV1
  • 降低每个人的数据流比特率,以免超载慢速用户(即最低公共比特率)
  • 向参与者发送独立的数据流,以适应每个用户的可用带宽

可伸缩视频编解码器

值得一提的是一些较新的视频编解码器及其权衡。VP9和AV1都内置了可伸缩性:它们以一种可以适应多个目标比特率的方式进行编码。但是,它们确实存在一些缺点(截至2021年8月):

  • VP9不支持Safari,且CPU密集型更高
  • AV1仅支持Chrome

虽然未来前景光明,但如今,这两种编解码器在动态比特率方面都不是实际的解决方案。

Simulcast登场

Simulcast允许WebRTC客户端发布同一源轨道的多个版本,具有不同的编码(即空间层)。在LiveKit中,参与者发布高分辨率、中分辨率和低分辨率版本的同一视频,这些视频以不同的比特率进行编码。

Simulcast旨在与SFU一起使用,其中SFU接收跟踪的所有三层,并对每个订阅者选择转发哪一层。

SFU选择向User C发送低分辨率流

从订阅者的角度来看,Simulcast是不可见的。WebRTC轨道设计时就支持动态调整分辨率。对于订阅者来说,这使得SFU可以在其带宽改变时切换到不同的层,同时保持连续的视频流无中断。

这是否意味着发布者需要发送更多数据?是的。但是,较低分辨率的层消耗的带宽远少于高质量版本。例如:

  • 原始/高分辨率:1280 x 720 @ 2.5Mbps
  • 中分辨率:640 x 360 @ 400Kbps
  • 低分辨率:320 x 180 @ 125Kbps

使用Simulcast,发布者只需要大约17%的额外带宽就可以发布所有三层。

通过更高级的反馈机制,带宽使用量还可以大大降低。如果某人的视频在接收端严格只呈现为缩略图,为什么还要发送720p层呢?每个订阅者都可以通知SFU它实际需要的层。

看一下Zoom的网络统计数据,你会发现他们管理这一点做得非常好。当参与者不是主讲人,并以缩略图的形式显示给其他人时,Zoom客户端只发送320x180层。

空间与时间可伸缩性

我们介绍了空间层,那么时间可伸缩性又是什么呢?本质上,时间可伸缩性是通过动态降低流的帧速率来降低流比特率的能力。

非可伸缩视频流中的帧,每个帧引用前一帧 

Untitled

这个想法很简单,但复杂性在于视频的编码方式 - 流包含主要是delta帧,这些帧依赖于之前的帧。如果解码器需要对被跳过的帧应用delta,它无法渲染后续帧。 为了使时间可伸缩性能够工作,编码器需要提前规划帧依赖性。

具有两个时间层的可伸缩流中的帧 

Untitled

VP8支持时间可伸缩性,当使用Simulcast时,浏览器会自动启用它。在上面的图中,我们看到一个具有两个时间层的启用了时间可伸缩性的流。我们使用TID来表示时间层:基本层具有TID 0,增强层具有TID 1。基本层上的帧仅引用其他基本层帧。 对于带宽有限的订阅者,我们可以跳过发送所有TID 1的帧,仅转发TID 0的帧。 这有效地将带宽需求降低了约50%,同时保持原始流的质量。 时间可伸缩性更适合内容如屏幕共享,其中清晰度大于FPS。


SFU中发生了什么?

为了支持Simulcast,SFU需要执行一些额外的步骤来完成这种神奇的操作。让我们继续看下面的内容。

重写序列号和时间戳

每个RTP数据包都包含一个序列号,指示流中其顺序,以及时间戳指示何时应播放帧。 WebRTC客户端依靠序列号来检测丢包情况,以及是否应该重新请求(NACK)该数据包。 当客户端收到序列号存在间隙的情况时,它会假设数据包在传输中丢失了。

对于Simulcast的轨道,每个层都作为一个独立的轨道发送,并带有自己的序列号集。 当SFU在层之间切换时,序列号中会有间隙。 对于时间层也是如此,当跳过某些帧时,传出序列号中会有跳跃。

为了补偿这一点,SFU需要跟踪每个订阅者的序列号,并重写每个数据包的序列号。 即使在重传丢失数据包时也需要维护此映射。(译者:也就是说对于订阅客户端来说接收到的rtp seq序号必须是连续的,而不会随着切换不通质量的流seq发生不连续的情况。)

带宽估计/拥塞检测

那么,我们如何知道要为每个订阅者发送哪个空间和时间层呢?理想情况下,它应该是其连接可以处理的最高质量流。 但如何确定呢?

事实证明WebRTC通过RTCP数据包内置了一些简洁的反馈机制:​​​​​

  • 接收者报告(RR):接收者定期发送报告以指示丢包率和抖动等信息
  • 传输范围拥塞控制(TWCC):允许发送方检测接收端的拥塞
  • 接收端估计最大比特率(REMB):接收端定期发送报告,指示估计的可用带宽。 (注意:如今TWCC优于REMB)

SFU接收反馈,并使用拥塞检测算法来估计可用带宽量。我们必须将码率维持在信道的可用带宽内,以避免拥塞。当拥塞发生时,我们将看到数据包丢失量增加,这将导致视频和音频冻结和失真。

跟踪订阅者层(Keep track of subscriber layers)

对于每个订阅者和轨道组合(track combination),SFU需要跟踪:

  • 订阅者希望哪个空间层?例如,如果客户端只需要320x180的流,那么我们就不应该发送任何更码率更高的流。
  • 订阅者的带宽允许哪个空间和时间层?

然后根据以下规则为每个订阅者做出决定:min(desired, allowed)(译者:也就是说sfu应该先得到观众期望的码率,然后再和允许发送的最大码率的值进行比较,取二者的最小值。)


处理发布者约束(Handling publisher constraints

到目前为止,我们已经介绍了如何处理订阅者的带宽限制。如果发布者存在上行带宽限制怎么办?如果发布者具有有限的上行带宽,则无法成功发布所有三个空间层。

浏览器处理这种情况的行为相当不错。 当带宽有限且启用了Simulcast时,WebRTC将禁用高分辨率层,直到它可以在不明显丢包的情况下发送流。您可以通过WebRTC统计信息观察到这一点,其中qualityLimitationReason设置为bandwidth。 类似地,如果发布者的处理器无法足够快地编码三个独立的层,则qualityLimitationReason设置为cpu。

chrome://webrtc-internals

但是,当浏览器决定停止发送层时不会提前通知SFU。 任何订阅终止层的订阅者都将停止接收新数据包,这会导致观众看到静止的画面。(译者:换句话说,如果浏览器在没有任何征兆的情况下停止上传某一个清晰度的流,那么正在订阅这个清晰度的观众则会看到画面卡死。)

这种情况这是不可取的, 将订阅者置于可用的较低分辨率层上会提供更好的体验。您可能已经猜到,这又是一个SFU需要解决的问题。 SFU需要监视每个层上传入数据包的中断情况, 如果检测到某个层上的数据中断,则会将该层标记为不可用,并将订阅者移动到替代层。您可以在StreamTracker中看到LiveKit如何处理此问题。

浏览器行为

我们需要特别注意当浏览器遇到带宽瓶颈的情况。当浏览器将某个清晰度的层禁用后,浏览器将在几秒后尝试重新启用它,只是为了在发现带宽没有改善时再次禁用它。 此波动在webrtc-internals图表中非常明显。 您可以使用MacOS中的网络链接条件仿真器模拟低带宽链接。

Untitled

framesEncoded/s和framesSent/s都呈现波动

通过测试我们已经发现一旦浏览器开始波动,即使带宽恢复后,它也会保持在这种状态。 这一点很出乎意外,我们推测可能是由于SFU或Google的WebRTC实现中的带宽估计逻辑错误造成的。 需要进一步的研究来查明根本原因。

客户端层管理

为了确保网络条件改善后视频质量可以恢复,我们在客户端引入了对simulcast编码的自定义管理。 这利用了WebRTC暴露的一些接口:

  • 通过RTCRtpSendParameters.encodings获取特定轨道的编码列表
  • 通过设置RTCRtpEncodingParameters.active控制层是否活动。 将其设置为false时,浏览器将停止发送该层。

有了这些接口,客户端可以监控当前带宽。 在检测到限制时,客户端可以完全暂停较高码率的视频层(layer),并在更长的回退后尝试重新启用和发送(译者:推测是根据服务器端的反馈决定是否恢复被禁的视频层。我认为这种恢复操作是很谨慎的,应该是从算法角度看可以恢复之后再等待很长时间后才可以恢复,也就是调用RTCRtpEncodingParameters.active=true)。 我们发现这种方法可以解决波动行为,允许一致的质量恢复。

如果您仍在阅读,希望您发现这篇文章很有用。 虽然Simulcast增加了WebRTC实现的复杂性,但它对于提供高质量的多用户会议来说是必要的。

如果您正在寻找支持Simulcast的WebRTC会议平台,请试试LiveKit!

感谢Raja、Orlando和Russ审阅了本文的草稿。

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

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

相关文章

uniapp使用自定义导航栏和手机自带的状态栏重叠

【问题界面】&#xff1a; 【正常界面】&#xff1a; 【解决方法】&#xff1a; 在页面顶部添加代码<!-- #ifndef H5 --> <statusBar></statusBar> <!-- #endif --> 2.引入占位条并注册 import statusBar from "/uni_modules/uni-nav-bar/c…

基于云平台的智慧养殖远程监控系统

项目背景 冬春季节每天的温度和昼夜温差变化很大&#xff0c;为保证养殖动物有一个温暖舒适的生存环境&#xff0c;使动物的生产性能得到较好的发挥&#xff0c;须注意做好温度、湿度、通风等方面的控制。 智慧养殖智能监控系统可以实现对如温度、湿度、气体浓度、光照度等参数…

2023五大自动化测试的 Python 框架

自2018年被评选为编程语言以来&#xff0c;Python在各大排行榜上一直都是名列前茅。目前&#xff0c;它在Tiobe指数中排名第三个&#xff0c;仅次于Java和C。随着该编程语言的广泛使用&#xff0c;基于Python的自动化测试框架也应运而生&#xff0c;且不断发展与丰富。 因此&a…

golang+layui提升界面美化度--[推荐]

一、背景 golanglayui提升界面美化度--[推荐]&#xff1b; golang后端写的页面很难看&#xff0c;如何好看点呢&#xff0c;那就是layui https://layui.dev/ 也是一个简单上手容易使用的框架&#xff0c;类似jquery&#xff0c;对于后端开发来说满足使用需求 二、使用注意点…

input 框如何移动光标,设置光标位置?

获取 input 光标位置 const inputDom document.getElementById("input") const selectionStart inputDom.selectionStart设置 input 光标 inputDom.focus() // focus() 异步&#xff0c;所以加了 setTimeout setTimeout(() > {const nextSelection selection…

Linux进程概念(续)

引入 我们先来看一段代码 #include<stdio.h> #include <unistd.h>int g_val200;//全局变量 int main() {int resfork();if(res>0)//father{printf("我是父进程。我的全局变量 g_val%d,他的地址是 %p\n",g_val,&g_val);}else if(res0)//子进程{g…

day38-Mobile Tab Navigation(手机tab栏导航切换)

50 天学习 50 个项目 - HTMLCSS and JavaScript day38-Mobile Tab Navigation&#xff08;手机tab栏导航切换&#xff09; 效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"…

Cesium态势标绘专题-多边形(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

mybatis_使用

第一步&#xff1a; 编写接口 第二步&#xff1a; 编写对应的mapper中的sql语句 第三步&#xff1a; 测试 CRUD <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http…

喜报 | 擎创科技入选2023中国金融科技竞争力百强榜

2023中关村金融科技论坛——第七届金融科技与金融安全峰会已圆满落幕。本次峰会主要围绕银行科技、保险科技、新一代信息技术、互联网3.0展开专题论坛&#xff0c;共有千余位金融机构和科技公司相互分享研究成果、探索前沿知识、交流实践经验。 会议上正式公布了“2023中国金融…

C++ 引用型别未定义

什么是引用型别未定义呢&#xff1f;&#xff08;首先是基本数据类型的&#xff09; 在使用函数的时候&#xff0c;我们在给函数传参的时候可能会传入一个左值也可能是右值。当我们使用如下函数funtion的时候&#xff0c;会在控制台打印什么样的结果呢&#xff1f;&#xff08;…

fastadmin框架重定向

由于&#xff0c;我们一打开fastadmin框架就进入到前端页面很麻烦&#xff0c;下面这种方法可以解决这个问题。 首先我们找到这个路径 找到重定向&#xff0c; application》index》controller》index 原本文件是这个样子&#xff1a; <?phpnamespace app\index\controll…

Redisson实现简单消息队列:优雅解决缓存清理冲突

在项目中&#xff0c;缓存是提高应用性能和响应速度的关键手段之一。然而&#xff0c;当多个模块在短时间内发布工单并且需要清理同一个接口的缓存时&#xff0c;容易引发缓存清理冲突&#xff0c;导致缓存失效的问题。为了解决这一难题&#xff0c;我们采用Redisson的消息队列…

SuperCLUE中文大模型排行榜(2023年7月)

中文通用大模型综合性测评基准&#xff08;SuperCLUE&#xff09;&#xff0c;是针对中文可用的通用大模型的一个测评基准。 它主要要回答的问题是&#xff1a;在当前通用大模型大力发展的情况下&#xff0c;中文大模型的效果情况。包括但不限于&#xff1a;这些模型哪些…

思科设备静态路由配置

一、静态路由基本知识 路由器的主要功能就是用来转发IP 数据包以使数据包到达正确的目的主机。可以想象数据包到达路由器就像一辆汽车开到十字路口&#xff0c;路由表就类似路标&#xff0c;列出可能到达的目的地&#xff0c;以及应该选择哪条路到达目的地。 路由器必须要有相应…

Cesium态势标绘专题-圆形(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

Set集合类详解(附加思维导图)

目录 一、Set集合思维导图 二、set集合类常用方法 2.1、HashSet集合常用方法 2.2、TreeSet集合的使用 三、HashSet、LinkedHashSet、TreeSet的使用场景 四、list和set集合的区别 一、Set集合思维导图 二、set集合类常用方法 2.1、HashSet集合常用方法 ①&#xff1a;add…

Docker安装Elasticsearch相关软件安装

Docker安装Elasticsearch相关软件安装 本文将介绍通过 Docker 的方式安装 Elasticsearch 相关的软件。 1、Docker安装Elasticsearch 1.1 搜索镜像 $ docker search elasticsearch $ docker search elasticsearch:7.12.11.2 拉取镜像 $ docker pull elasticsearch:7.12.11.…

# jellyfin安装设置使用散记

jellyfin安装设置使用散记 文章目录 jellyfin安装设置使用散记0 软件简介1 安装2 视频转码问题2.1 局域网转码情况测试&#xff08;不同网段&#xff09;2.2 局域网jellyfin app默认转码问题解决2.3 外网转码情况测试 3 一些坑4 插件5 最后 0 软件简介 Jellyfin 是一个自由的软…

ConcurrentHashMap 相比于 HashMap 的优势

ConcurrentHashMap 使用每个链表头节点作为锁对象, 把一把大锁转换成多把小锁, 大大缩小了锁冲突的概率 HashTable 是给整个 Hash 表加锁, 因此只要有线程抢到了锁其他线程就得阻塞等待. ConcurrentHashMap 是对每个链表加锁, 因此只要不是对同一个链表进行修改就不会阻塞, 大…