超大规模会议技术优化策略 轻松实现 500 人线上流畅沟通

超大规模会议技术优化策略 轻松实现 500 人线上流畅沟通

受疫情影响,许多公司已经形成线上办公习惯,尤其是在线音视频会议,已经成为一种常态。对于一些大型企业和组织机构来说,分支机构遍布全国各地,员工异地参会人数众多,大规模音视频会议成为刚需。而当前音视频会议主流产品中,单个会议最多支持 500 人入会进行互动。

但是 500 人同时线上开会,对于资源消耗比较高。而传统的 WebRTC 架构并不擅长超过 200 人以上的会议场景。在面对超大规模会议室、聊天室、直播等各种复杂场景时,对流进行按需合流,可以降低带宽占用和设备压力;对流进行有选择的订阅分发,有助于扩展各种组合场景。针对 App 具体的应用场景,可以配合订阅分发模式,组合使用 SFU 和 MCU 架构。下来我们将详细分析一下大规模会议的资源优化策略。

1.超大规模会议架构对比

WebRTC 多对多网络架构有 P2P、MCU、SFU 三种。各种网络拓扑的优缺点如下:

融云

SFU 方式灵活,只要降低带宽就可以实现大规模会议的要求。

2.超大规模会议中存在的挑战

在超过 20 人会议场景下,SFU 及 WebRTC 兼容场景仍然无法很好的解决。如果直接选择参会人之间进行音视频互动,音视频数据完全转发对服务器资源的要求是巨大的,如果会议中有大量人员同时接入,服务端上行流量和下行流量陡增,会对服务器造成巨大压力。

这里我们来对比一下 20 人与 200 人同时参加音视频会议时,对服务端造成压力的差距:
20人
各端流量:
20*(1Mbps+32Kbps)=20.64Mbps
服务端上行流量:20*(1Mbps+32Kbps)=20.64Mbps
服务端下行流量:20*(20-1)*(1Mbps+32Kbps)=392.16Mbps
200人
各端流量:200*(1Mbps+32Kbps)=206.4Mbps
服务端上行流量:200*(1Mbps+32Kbps)=206.4Mbps
服务端下行流量:200*(200-1)*(1Mbps+32Kbps)=41.07Gbps

从对比结果中可以看出,服务端下行流量直接上升了一个量级。如果采用视频按需订阅,音频选择出音量最大的几路可以大大降低下行流量。比如每个客户端订阅 4 路视频,服务器只需下发 4 路音量最大的音频,服务端下行流量只需要 200*4*(1Mbps+32Kbps)=800+25.6=825.6Mbps,可以极大缓解服务器压力。 

若要解决上面的问题,建议通过按需订阅与转发、音频流量两个方面来制定策略,在保证效果的前提下,降低服务端的压力。

3.按需订阅与转发以及音频流量优化策略

3.1 按需订阅与转发
按需订阅与转发的方式有:
➀支持单独订阅某个人的某路视频或某路音频。
➁接收端仅订阅正在说话的人的视频,音频全部订阅。
➂融云 SDK 支持发送端视频编码支持大小流。接收端按需订阅大流或小流。大流的清晰度高,码率高;小流的清晰度低,码率低。这样当接收端想观看清晰视频的时候订阅大流;对清晰度要求不高的时候订阅小流。另外,弱网下自动切换大小流,可以保证视频的流畅性。

3.2 音频流量优化策略

针对音频全部订阅有以下几种优化音频流量的方法。

3.2.1 发送端静音时不发送数据

WebRTC 的音频 codec 如果采用 Opus,可以开启 Opus 的 DTX(Discontinuous Transmission)。SDP 对应的设置为 usedtx=1。但测试中发现流量下降不如预期,因为用户的使用环境多少有点背景音。背景音量很容易超出静音阈值。像 Android/iOS 这种定制开发端可以手动调整静音阈值,而 PC 的 Web 端因为是浏览器,则无法调整静音阈值。

3.2.2 调整音频码率通过设置客户端上音频码率,降低客户端上行的音频码率。当音频路数跟多的时候,限定每一路的音频码率后,总的音频码率会减少很多。SDP 设置方式 b=AS:码率。下面是摘自RFC3556的原文:

 The Session Description Protocol includes an optional bandwidth   attribute with the following syntax:      
b=<modifier>:<bandwidth-value>  
where <modifier> is a single alphanumeric word giving the meaning of   the bandwidth figure, and where the default units for <bandwidth-   value> are kilobits per second.  This attribute specifies the   proposed bandwidth to be used by the session or media.
A typical use is with the modifier “AS” (for Application Specific   Maximum) which may be used to specify the total bandwidth for a   single media stream from one site (source).

.2.3 服务器下发音量 Top N 路

客户端收到音频流,在音频解码后,默认一般仅混流播放音量最大的 3(WebRTC 中的 kMaximumAmountOfMixedAudioSources 值)路声音。所以避免不必要的音频包的转发可以减少服务流量的。步骤如下:
➀发送端通过 Audio Level 标识音频能量。
➁音频包进入 SFU 转发队列,先进入计算队列,定期弹出 Top N 的音频包。
➂只有有效音频包,会进入到下行分发队列。 

下面介绍音频如何转发音量最大几路的方法实践。

4. 音频 Top N 选择

4.1 客户端处理

客户端会计算出音量大小,并把值记录在 RTP 包中。所以客户端需要开启 audio-level 的 RTP 扩展, 如下: a=extmap:1urn:ietf:params:rtp-hdrext:ssrc-audio-level 开启这个 RTP 扩展后,WebRTC 客户端机会计算 audio 包的音量大小。这个音量大小计算方法RFC6464有明确定义。WebRTC 中的计算方法为 modules/audio_processing/rms_level.cc 的 ComputeRms 方法:

// Calculates the normalized RMS value from a mean square value. The input
// should be the sum of squared samples divided by the number of samples. The
// value will be normalized to full range before computing the RMS, wich is
// returned as a negated dBfs. That is, 0 is full amplitude while 127 is very
// faint.
int ComputeRms(float mean_square) {
 if (mean_square <= kMinLevel * kMaxSquaredLevel) {    
// Very faint; simply return the minimum value.
   return RmsLevel::kMinLevelDb;
 }  
// Normalize by the max level.  
const float mean_square_norm = mean_square / kMaxSquaredLevel;  
RTC_DCHECK_GT(mean_square_norm, kMinLevel);
 // 20log_10(x^0.5) = 10log_10(x)  
const float rms = 10.f * log10(mean_square_norm);  
RTC_DCHECK_LE(rms, 0.f);
RTC_DCHECK_GT(rms, -RmsLevel::kMinLevelDb);
 // Return the negated value.  return static_cast<int>(-rms + 0.5f); }

客户端告诉服务器音频包的音量大小。服务器收到音频包后不用做解码,就能知道从客户端上来的音频包的音量值,为后面的服务器音频包下发策略奠定了基础。

4.2 服务器处理

下面用 Publisher 表示发布者的音频流,Subscriber 表示订阅者的音频流。RtpAudioPacket 表示一个音频包。RtpAudioPacket 里有个 mute 属性,标记这个音频包时是否静音。

在没有音频根据音量大小转发的逻辑前,Publisher 和 Subscriber 的处理关系如下。

融云

Subscriber1、Subscriber2、Subscriber3 订阅 Publisher1、Publisher2、Publisher3。Publisher 发上来的音频包都会转发给各自的订阅者。

音频根据音量大小转发的逻辑如下:
➀AudioLevelHandler 表示每个 Publisher 的音频处理单元。AudioLevelHandler 里有两个音频包缓冲队列,计算队列 calculate_queue 和发送队列 send_queue。Publisher 的音频包先进入计算队列 calculate_queue 中。有个定时计算任务 AudioLevelCalculator。AudioLevelCalculator 会每隔一个音频打包时间 ptime 进行一次对所有 Publisher 的计算队列里音频包的 audio_level 均值(因为均值表示这个 Publisher 收到的若干个音频包的音量)做排序计算,选出音量值最大的几路。这几路的音频包 RtpAudioPacket 的 mute 被标记为 false,而其他音频包标记为 true。
➁排序后,这些音频包会从计算队列里移入到发送队列 send_queue 中。
➂之后音频包从 send_queue 出队,转发给 Subscriber。Subscriber 中的 MuteHandler 有以下两个作用:
a. 根据 RtpAudioPacket 的 mute 属性,mute 为 true 时,这个音频包直接被吞掉,false 表示转发给订阅者。
b. 因为下发给订阅者的音频包 RTP 序号 SeqNum 不是连续的,需要做连续化处理。

下面图中 Subscriber1、Subscriber2、Subscriber3 订阅 Publisher1、Publisher2、Publisher3。假设 Publisher1 收到的当前音量最大,最终只有它的音频包会转发给 Subscriber1、Subscriber2、Subscriber3。

融云

4.3 级联的考虑

比如下面的图中,Subscriber4 通过级联服务器连接到当前 MediaServer 上。Publisher1、Publisher2、Publisher3 的音频包都会直接转发级联服务器。由级联服务器负责计算 Top N 音频包的计算下发给 Subscriber4。

融云

​下面是这部逻辑的伪代码:

void Publisher::Process(RtpAudioPacket packet, AudioLevelHandler handler) {    handler.calculate_queue.enqueue(packet)
   RtpAudioPacket packetSend = handler.send_queue.dequeue();
   for (对当前Publisher的所有Subscriber subscriber) {
       if (subscriber是级联服务器) {
           转发packet
       } else {
           转发packetSend
       }
   }
}

4.4 音频下发策略优化

现实中人的说话是有停顿的。比如停顿前后人声比较大,如果简单的排序下发音频包,客户端会收到连续的非静音包。经测试,这样的体验并不理想,因此需要加入平滑处理。这里 history 为过去若干次的音频是否进入 Top N。音频包是最大的几路中的,加入 history 队列尾部加入 true,转发表示此次声音大而发。否则,加入 history 队列尾部加入 false。因为本次静音,还需判断过去的静音情况,若 history 中有 true 值,转发可表示过去一小段说过话,所以需要转发。若 history 中全为 false, 不转发则表示本次声音不大,过去一小段声音也不大,所以不转。

4.5 其他相关策略

➀当会议中的人数相对比较的少的时候,音频包为上面所述的正常转发。而当多个 Publisher 的订阅人数超过某个阈值(比如 50),此时 MediaServer 发的音频码率很大,对应客户端也要收相应的音频流量。这时可以走超大会议音频最大几路转发逻辑。而当会议中多个 Publisher 的订阅人数下降到阈值之下,再回归正常的转发逻辑。
➁经过选取最大几路流的下发方式,音频流量已经大大降低了。而在此基础上实际设置的选取路数做少许冗余,可以多发一些有音量的音频包,提高接收方体验。
➂当参会者增加时,相应的 MediaServer 也需要动态调度。通过把参会者音视频流打到多个 MediaServer 上,通过级联的方式解决问题,保证每台 MediaServer 服务器上的 CPU、内存、带宽的正常。

5. 总结

以上是基于超大规模会议技术优化进行的策略方面的探索。其主要思想是视频按需订阅,音频降低不必要的流量。其中涉及客户端音量值的上传、服务器端音量选择、级联、优化体验、减少音频流量等多个方面。研发过程中,超大会议需要多测试,才能暴露其中的问题,从而提高最终的会议体验。

       

标签: , ,