本文来自Jitsi Videobridge SFU的后端开发人员之一Brian Baldino,他过去在思科和Highfive工作过,拥有丰富的视频会议产品研发经验。他分享了在Jitsi实现自动减少转发视频层,从而降低客户端CPU和带宽使用。LiveVideoStack对原文进行了摘译。
文 / Brian Baldino
译 / 元宝
审校 / Ant
原文 https://webrtchacks.com/suspending-simulcast-streams/
对高流量的流控制(来源:usbr.gov)
大多数人可能都熟悉典型的SFU风格的用户界面,该界面最初是在Google Hangouts的消费者市场中推广的,并由Jitsi Meet和其他服务部门使用。绝大多数屏幕空间的正面和中心是当前活跃的演讲者的视频。所有其他参与者都可以在他们自己的缩略图中看到,通常在右侧或底部。我们想让活跃的演讲者的视频在中间看起来很棒,因此分辨率很高。底部/右侧的缩略图会很小,因此高分辨率会浪费带宽。为了优化这些不同的模式,我们需要每个发送者视频的多个分辨率。值得庆幸的是,这已经是一个用联播解决了的问题!
通过联播,所有发送者编码3种不同的分辨率并将其发送到SFU。SFU决定将哪些流转发到每个接收器。如果参与者是活跃的发言者,我们会尝试并将他们发送给其他人的最高质量的流转发到他们的主界面上。如果要在右侧的缩略图中看到参与者,那么我们就会转发他们的最低质量的流。
联播权衡
联播是优化下载带宽的绝佳机制。然而,与生活中的大多数事情一样,联播涉及权衡——编码3个流比编码单个流需要占用更多CPU。您可以在下面的chrome:// webrtc-internals统计信息中看到这一点,其中使用联播的CPU使用率提高了几个百分点:
没有联播的CPU使用率
使用联播的CPU使用率
它还涉及发送更多比特数:
在没有使用联播时的发送比特率(~2,5M比特/秒)
使用联播时的发送比特率〜(3M比特/秒)
这些图表是由chrome:// webrtc-internals自动缩放的,因此请注意y轴刻度可能不同。请查看实际的y轴值。
流暂停
那么这是否意味着联播对用户来说效率较低呢?相反,由于我们可以单独控制联播的流,因此联播使我们有机会通过关闭不使用的层来节省CPU和比特数。如果你不是活跃的发言者,则根本不需要3层中的2层!
让我们看看当我们关闭前2层时的使用率:
CPU使用率
没有使用联播的CPU使用率基线
具有3个联播流的CPU使用率
禁用前两个层的联播的CPU使用率
每秒比特数
没有使用联播的发送比特率基线
使用3个联播流的比特率
禁用前两个层的联播的比特率
对于客户端和SFU上的负载来说,这是一个巨大的胜利!
实施暂停
现在让我们看看我们是否可以将其集成到实际代码中。这里有两个问题需要解决:
1.在SFU上——弄清楚何时没有使用流并让客户知道
2.在客户端——在不使用流时关闭流,并在需要时再次启动它们
SFU
第一个问题很容易解决——当客户成为活跃的发言人时,客户端会明确地请求参与者提供高质量的流,这样我们就可以告诉发送者何时使用高质量的流以及何时不通过数据消息通道。
客户端
第一次尝试
我们也想到了第二个问题。我们知道Chrome会在可用带宽下降时暂停联播流的传输,那么如果我们只限制可用带宽会发生什么呢?我们可以通过在远程SDP中设置带宽限制来实现此目的:
使用SDP限制最大发送带宽
在 b = AS 的那一行将可用带宽限制到200kbps。让我们试一试,看看会是什么样子:
SDP限制带宽后的CPU使用率
SDP限制带宽后的发送比特率
太棒了!这正是我们所希望的:它与我们之前的测试结果相匹配!现在让我们移除上限以模拟某个人成为活跃的发言者并且我们想要他们的高质量流:
移除上限的CPU使用率
移除上限的发送比特率
移除上限的发送帧的高度
话说回来,还有一个问题......整个过程需要30秒才能恢复到高质量。这意味着当某人成为活跃发言人时,他们在主舞台上的低视频质量将至少持续30秒。这不行,那么为什么这么慢呢?
如果你曾经使用Chrome进行过网络损伤测试,那么你知道它会应用大量逻辑来防止监控。由于担心丢失数据包,因此提高发送比特率是非常谨慎的。我们基本上通过我们的SDP参数完成的工作是让Chrome认为网络的数据包容量非常低(200 kbps),因此当我们删除它时,Chrome会小心地提高比特率,同时计算实际发送的数量。当网络出现问题时,这很有意义,但对于我们的用例,这是一个阻碍因素。
Google Meet测试
我们注意到当Google Meet正在使用时,我们首先开始讨论联播流暂停。我们来看看Google Meet电话会议的图表:
Google Meet上的CPU使用率上升
Google Meet上的发送比特率上升
Google Meet上的发送帧的高度
哇!它们下降并且非常快速地增加。他们是如何做到的呢?我们看了他们的SDP,他们正在使用b = AS上限。我们知道这不会让我们快速上升(正如我们在第一次尝试中看到的那样),所以他们肯定还做了其他事情。
我们看了一下chrome:// webrtc-internals并注意到了这一点:
使用webrtc-internals调查Google Meet
这个addStream可能看起来没有什么作用,但它不是在调用的开始,它在中间的位置,当我们希望流恢复时,那么这里发生了什么呢?正在添加另一个视频流,但没有一个被删除,这是如何工作的呢?它与比特率快速上升有关吗?
所以我们仔细看了一下,发现了一些细节。这是参与者首先将其媒体流添加到 peerConnection的位置:
以下是当我们想要提高比特率时的addStream:
所以我们在这里可以看到轨道ID是相同的但是流ID是不同的。这让我们想起了Chrome如何为新创建的流提供一个免费的时间段,其比特率可以很快提升; 这样,当你加入通话时,你可以快速开始发送高清视频。我们怀疑,新流的自由上升期是这里所利用的,当参与者成为活跃的演讲者时,让流看起来是新的。
尝试2
根据对Meet的调查,我们开始使用独立的WebRTC演示应用程序尝试重现其中的行为。通过这样做,我们能够在我们的测试环境中重现相同的行为:
复制媒体流
将复制的媒体流添加到对等连接
Munge SDP从新流中删除新的ssrcs / stream信息并将其替换为原始信息。
但我们还没有在实际的Jitsi调用中尝试它,测试环境是点对点的,并没有使用联播,所以我们不确定它能移植到Jitsi并工作。曾经我们尝试或,我们发现我们没有得到快速上升。它很慢,就像以前一样只有带宽上限。我们开始对此进行调试,并认为它可能与我们在SFU上的速率控制中的某些因素有关,这会阻止比特率快速上升。
在我们进一步发展之前,出现了一种新的可能性。
尝试3
WebRTC团队最近推出了关于RTCRtpSender的PSA。支持修改登陆Chrome 69的编码参数。这有一个API,可让我们控制各个联播编码,包括它们是否已启用!所以,当我们发现我们不能进入主界面时,客户端可以这样做:
应该禁用前2层,让我们看看它的表现:
CPU通过RtpSender参数丢弃没有使用的层
通过RtpSender参数丢弃没有使用层的发送比特率
我们没有像以前那样下降,但它仍然是一个很大的进步!但是,让我们看看当我们重启它们时如何提升:
CPU使用率上升
发送比特率上升
发送帧高度上升
哇!比特率立即上升了!这将完全适用于有源扬声器切换。我们不会在Chrome 69之前获得此功能,但它是一个好的解决方案,并为我们提供了我们想要的东西:当流不使用时快速降低比特率,并在我们再次需要时快速恢复。
今天就试试看Jitsi Meet,将#config.enableLayerSuspension = true添加到你的URL(只要你使用Chrome v69 +)或查看Jitsi Github中代码。