RED:利用冗余提高音频质量

2020-08-30 17:53:48

早在2020年4月,一个公民实验室就报道了Zoom相当弱的加密,并声称Zoom使用Silk编解码器进行音频。遗憾的是,这篇文章没有包含验证这一点的原始数据,让我进一步研究一下。谢天谢地,Googles Project Zero的娜塔莉·西尔瓦诺维奇(Natalie Silvanovich)使用Frida追踪工具帮我解决了问题,并提供了一些生丝框架的简短转储。对此的分析启发了我来看看WebRTC是如何处理音频的。就感知而言,音频质量对于感知到的通话质量更为关键,因为我们往往会注意到哪怕是很小的故障。短短10秒的音频分析就足以让我开始一次相当大的冒险,调查WebRTC提供的音频质量的可能改进。

我曾在2017年(在DataChannel发布之前)研究过Zoom原生客户端,发现与任何基于WebRTC的解决方案相比,音频包有时都相当大:

上图显示了具有特定UDP负载长度的数据包数量。与典型的WebRTC调用相比,长度为150到300字节的分组是不寻常的。这些数据包比我们通常从OPUS收到的数据包要长得多。我们当时怀疑是某种前向纠错(FEC)或冗余,但如果不能访问未加密的帧,就很难得出更多的结论或行动项目。

新垃圾场中未加密的丝绸框架显示出非常相似的分布。将帧转换成文件,然后播放短消息(感谢贾科莫·瓦卡(Giacomo Vacca)发表了一篇非常有用的博客文章,描述了所需的步骤)之后,我回到Wireshark查看了这些数据包。下面是一个由三个数据包组成的示例,我发现这三个数据包特别有趣:

数据包7: E9e4ab17ad8b9b5176b1659995972ac9b63737f8aa4d83ffc3073d3037b452fe6e1ee 5e6e68e6bcd73adbd59d3d31ea5fdda955cbb7f 数据包8: E790ba4908639115e02b457676ea75bfe50727bb1c44144d37f74756f90e1ab926ef 930a3ffc36c6a8e773a780202af790acfbd6a4dff79698ea2d96365271c3dff86ce6396 203453951f00065ec7d26a03420496f 数据包9: E93997d503c0601e918d1445e5e985d2f57736614e7f1201711760e4772b020212dc 854000ac6a80fb9a5538741ddd2b5159070ebbf79d5d83363be59f10ef E790ba4908639115e02b457676ea75bfe50727bb1c44144d37f74756f90e1ab926ef 930a3ffc36c6a8e773a780202af790acfbd6a4dff79698ea2d96365271c3dff86ce6396 203453951f00065ec7d26a03420496f E9e4ab17ad8b9b5176b1659995972ac9b63737f8aa4d83ffc3073d3037b452fe6e1ee 5e6e68e6bcd73adbd59d3d31ea5fdda955cbaef。

数据包9完整地包含前面的两个数据包。数据包8包含前一个数据包。这样的冗余是一个特点,因为深入研究Silk解码器(可以从Skype团队提交的互联网草稿或从GitHub存储库获得)显示,这是Silks LBRR-低比特率冗余格式:Zoom使用SKP_SILK_LBRR_VER1,但最多有两个冗余数据包。如果每个UDP数据包不仅包含当前音频帧,还包含前两个音频帧,即使您丢失了三个数据包中的两个,这也是有弹性的。那么,也许放大音频质量的关键是Skype祖母的秘方呢?

我们如何通过WebRTC实现同样的目标呢?看一看Opus FEC显然是下一步。

来自Silk的LBRR(低比特率冗余)也包含在OPUS中(请记住,Opus是一种混合编解码器,它使用Silk作为比特率频谱的低端)。然而,Opus Silk与Skype当时开源的原始Silk有很大的不同,用于前向纠错模式的LBRR部分也是如此。

在OPU中,前向纠错不是简单地附加在原始音频帧之后,而是在它之前,并在比特流中编码。我们尝试使用Insertable Streams API添加我们自己的前向纠错,但这需要完整的代码转换才能在实际数据包之前将信息插入到比特流中。

虽然没有成功,但这项努力确实允许收集关于上面所示的LBRR影响的一些统计数据。在严重丢包的情况下,LBRR使用高达10kbps(或数据速率的三分之二)的比特率。您可以在此处找到[存储库](https://github.com/fippo/opus-fec/).。WebRTC不会公开这些统计数据 Getstats()调用API,因此这些结果非常有趣。

除了必须重新编码的问题外,在WebRTC中,Opus FEC被证明是以几种无用的方式配置的:

它是基于数据包丢失启用的,我们希望始终拥有冗余信息,因此它在出现任何问题之前就存在了。斯拉克夫妇早在2016年就写过这方面的文章。这意味着我们不能在默认情况下启用它,也不能保护自己免受随机的零星丢失。

前向纠错的数量上限为25%。超过这一点就没有用处了。

从目标最大比特率中减去FEC比特率是非常没有帮助的-FEC会主动降低核心流的比特率。较低的比特率流通常会导致质量降低。如果没有任何丢包需要FEC纠正,那么这只会使质量变差,而不是改善。为什么要这样设置呢?一般理论认为丢包的原因之一是拥塞。如果您有拥塞,您不想发送更多的数据,因为那只会使问题变得更糟。然而,正如Emil Ivov在2017年的这场伟大的KrankyGeek演讲中所描述的那样,拥塞并不总是数据包丢失的原因。此外,此方法还忽略任何伴随的视频流。当你同时发送数百千比特的视频和相对较小的50kbps的Opus流时,Opus音频的基于拥塞的FEC策略没有多大意义。也许我们会在未来看到libopus的一些变化。在此之前,关闭它可能很有趣,目前WebRTC默认启用它。

如果我们想要真正的冗余,RTP有一个解决方案,称为冗余音频数据的RTP有效负载,或RED。它相当古老,RFC2198写于1997年。它允许以相对较低的开销将具有不同时间戳的多个RTP有效负载放入同一RTP数据包。

使用RED将一个或两个冗余音频帧放入每个分组中将比OPUS FEC提供更强的对分组丢失的恢复能力。然而,这是有代价的,将音频比特率从30kbps增加一倍或三倍至60kbps或90kbps(头部额外增加10kbps)。然而,与每秒超过1兆比特的视频数据相比,它似乎并没有那么糟糕。

WebRTC库实际上包含了第二个RED编码器和解码器-现在这是多余的!尽管我做了一些努力来删除一些不用的红色音频代码,但我还是设法相对较少地使用了那个编码器。WebRTC问题跟踪器包含完整的故事。

现在,这在现场试用中可用,在使用以下标志运行Chrome时启用:

默认情况下不启用它,因为在某些环境中,消耗额外带宽不是一个好主意。要使用RED,请更改编解码器的顺序,使其位于OPUS编解码器之前。要做到这一点,一种方法是使用 RTCRtpTransceiver。SetCodecPreferences接口,如本次变更所示。SDP显然是另一种选择。SDP格式可能还提供了一种配置最大冗余级别的方法,但是RFC 2198的提供-应答语义并不完全清楚,所以我选择暂时省略它。

在音频示例中运行它将演示该行为。这是一个具有一个数据包冗余的早期版本:默认情况下,负载比特率(红线)几乎是没有冗余时的两倍,接近60kbps。DTX(非连续传输)是一种带宽节约机制,仅在检测到语音时才发送数据包。正如预期的那样,当使用DTX时,比特率影响有所减轻,正如我们在通话接近尾声时所看到的那样。

检查数据包长度会发现预期的结果:与如下所示的负载长度正态分布相比,数据包的平均长度(上图)是正态分布的两倍:

这与Zoom的做法仍然有点不同,我们看到了一些冗余,但很多数据包没有冗余。让我们重复前面的Zoom Packet Length图表,以便我们可以并排查看它们:

OPUS FEC仅在数据包上有语音活动时才发送冗余数据。同样的道理也应该应用于RED实现。要做到这一点,需要修复OPUS编码器,以公开丝绸层中确定的正确VAD信息。通过这种调整,如果有语音,比特率只会达到60kbit(与60kps以上的速率相比):

而且“光谱”看起来更像是Zoom所做的:支持修复的改变还没有落地。

“距离”是指冗余包的数量。在研究这一点时,我们最终发现,虽然距离为1的红色效果很好,但距离为2的红色效果明显更好。我们的实验室评估模拟了60%的随机数据包丢失。在这种环境下,OPUS+RED产生了很好的音频,而没有RED的OPUS则严重退化。WebRTC Getstats()API提供了一种非常有用的方法来衡量这一点,方法是比较隐藏样本的百分比,该百分比是通过将隐藏的样本除以totalSamplesReceived获得的。

在音频示例页面上,通过将以下JavaScript片段粘贴到控制台,可以很容易地获得这些统计信息:

然后,我运行了几个丢包测试,使用的是不是很知名但非常有用的 WebRTCFakeNetworkReceiveLossPercent Chrome字段试用标志:

丢包率为20%,默认启用FEC时,音频质量没有太大差异,但度量有所不同:

如果没有RED或FEC,则度量几乎与请求的分组丢失相同,从而验证度量。FEC有一些小的影响,但不大。

在60%的损耗下,没有红色的音频质量变得相当糟糕,使得声音有点金属味,文字也很难理解:

有一些红色距离为1的声音工件,但距离为2(这是现在使用的冗余量)的音频几乎是完美的。人类的大脑似乎可以忍受一定程度的间歇性沉默。

(间接相关:Google Duo显然使用了一种机器学习算法,用比沉默更好的方式来填补空白。)

我们希望在OPUS中引入RED可以改善音频质量,但在某些情况下可能会使情况变得更糟。Emil Ivov自愿首先使用POLQA-MOS方法运行几个听力测试。Opus过去已经做到了这一点,所以我们有一个基线可供比较。如果初始测试显示有希望的结果,我们将使用上面使用的隐藏百分比度量在主要的Jitsi met部署上进行大规模实验。

请注意,对于媒体服务器和SFU,启用RED稍微复杂一些,因为服务器可能需要管理对选定客户端的RED重传,例如,如果不是会议中的所有客户端都支持RED。此外,有些客户端可能位于带宽受限的链路上,而您不希望使用RED。如果端点不支持RED,则SFU可以删除冗余编码并在没有包装的情况下发送OPU。同样,当从发送OPU的端点向支持RED的端点重新发送分组时,它可以实现RED本身并使用RED。

非常感谢JITSI/8×8 Inc赞助这次冒险,感谢Google的工作人员对所需的更改进行审查或提供反馈。

想跟上我们的最新帖子吗?如果您尚未订阅我们的邮件列表,请单击此处订阅。我们只通过电子邮件发送帖子更新。你也可以在Twitter上关注我们的博客更新,邮箱是:@webrtcHacks。