WebRTC

WebRTC

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

WebRTC梅川酷子 发表了文章 • 0 个评论 • 21 次浏览 • 5 天前 • 来自相关话题

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


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

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


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

1.png

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).
3.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 的处理关系如下。


2.png

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。

3.png

4.3 级联的考虑


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

5.png

下面是这部逻辑的伪代码:
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. 总结


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


RTC vs RTMP,适合的才是最好的!

WebRTC赵炳东 发表了文章 • 0 个评论 • 143 次浏览 • 2021-01-29 16:17 • 来自相关话题

随着在线教育、电商直播、泛娱乐社交等 App 的普及,实时音视频技术的应用需求也越来越多元化。目前,市场中能够支持音视频通信的主流技术有“RTMP+CDN”和“RTC”两大阵营。选型时,开发者如何根据场景选择更适合自己的通信技术?这就要从两者的技术特点、价格、... ...查看全部

随着在线教育、电商直播、泛娱乐社交等 App 的普及,实时音视频技术的应用需求也越来越多元化。目前,市场中能够支持音视频通信的主流技术有“RTMP+CDN”和“RTC”两大阵营。选型时,开发者如何根据场景选择更适合自己的通信技术?这就要从两者的技术特点、价格、厂商服务综合考虑。1.png

RTMP+CDN 技术特点与适用场景

RTMP (Real Time Messaging Protocol)基于 TCP 的流媒体传输协议,最大的特点是与 CDN 的强绑定,需要借助 CDN 的负载均衡系统将内容推送到接近用户的边缘节点,使用户就近取得所需内容,提高用户访问的响应速度和成功率,解决因分布、带宽、服务器性能带来的访问延迟问题。更多适用于站点加速、点播、短视频等场景。

对于初次通过 CDN 服务来实现音视频通信的开发者来说,技术指标应主要关注延时、卡顿率、下载速度、打开速度、回源率、宽带冗余提升率等几个维度。

有研究表明,在 0.1s 以下的延迟,用户几乎是无感知的;1s 左右的延迟,用户会明显注意到延时的发生,但在该时间内思维依然是连贯的;超过 10s 的延时,用户会失去等待的耐心。在所有关键技术指标中,控制延时是 CDN 最需要提升的。

以直播场景为例,延时主要看 2 个核心指标:首播时间和再缓存时间。首播时间即从打开到看到视频画面的时间,会受域名解析、连接、第一包时间的影响,首播时间控制在 1 秒内算是不错的效果。其次是再缓冲时间,是用户观看视频时的卡顿时间。由于实际服务中视频长度不一,一般会做播放的体验统计,主要监测的是卡顿率。行业内而言,直播首播时间 300ms,卡顿率在 15% 以下算是优质的通信服务。

目前的 CDN,通常有 3-5 秒的延迟,在浏览图片、短视频等内容时用户感知不明显,对于不需要实时强互动的直播,比如体育赛事网络直播、演唱会网络直播、新闻现场直播,延迟是可以接受的,并不会影响用户体验。

2.png

而在线视频会议、在线教育、电商直播、远程医疗会诊这些对互动有非常高要求的场景,RTMP+CDN 的模式与这些场景对于低延时、无卡顿的要求有一定差距。这时,选择 RTC 技术才能更好地满足开发者的需求。

RTC 技术特点与适用场景

说到 RTC(Real Time Communication)实时音视频通信,它最大的特点就是低延时和无卡顿。从功能流程上说,它包含了采集、编码、前后处理、传输、解码、缓冲、渲染等诸多环节,RTC 不是靠“优化”各环节去实现的实时互动,而是依靠推流端实时的传输机制。

3.png

很多实时音视频服务专业厂商使用的就是 WebRTC 标准,这是一种基于浏览器的实时通信的开源解决方案,使用 UDP 私有协议来进行媒体推流,而不需要创建离散的媒体段;并且它是面向无连接的,没有 TCP 连接断开时的挥手确认连接关闭的机制,基于这两点,WebRTC 能够做到毫秒级的低延迟,远远低于基于 RTMP 协议的 CDN 分发的延迟。而且,它直接通过浏览器就可以完成推流和播放,对于开发者接入来说实在太方便。

因此,WebRTC 标准针对有高互动性要求的直播场景尤为适宜。以直播连麦为例,主播端把通信直播流发到观众端,同时也可以把观众端拉上麦,实现主播和观众的互动。使用 WebRTC,内容实时传输,主播和观众可以进行音视频连麦互动,实时沟通,延时一般低至 400ms 以内。

4.png

基于 WebRTC 标准的融云实时音视频服务,拥有超低延迟的优势,同时也支持将 RTC 音视频流合流(MCU)转码为 RTMP,并推流到第三方 CDN 上,保留了标准协议普遍被 CDN 网络支持的好处。目前,融云音视频通话,可做到全球端到端延时小于 400ms,最低延时 66ms;低延时互动直播的直播推流可以做到主播观众间延迟在 300ms 左右,保障端到端之间延迟无感知的实时互动。

CDN vs RTC 选型还需看价格服务综合比

一套实时音视频通信能力的搭建,除了要根据场景选择适合的技术外,还要看价格、服务的综合性价比。通常来说,使用 RTC 技术的成本比 RTMP+CDN 高。因为,从实践来看,UDP 传输比 TCP 传输对资源消耗要多,而且重传、封包、FEC 冗余计算等都会额外增加计算量,在多进程模式下可能还会遇到内存资源的过多消耗,这些都导致开发及使用成本的增加。

开发者选型中,性价比需综合技术特点、适用场景、价格和服务四个方面的全面考量。服务在产品上线前后的开发阶段和运营阶段,都要发挥重要作用。目前,开发者服务做得比较好的厂商比如融云,会与开发者共建开发文档,技术手册短视频化,提供场景化的 Demo,以及在官网搭建开发者专区,帮助开发者更便捷、更快速的理解 SDK。

融云全新升级的实时音视频服务,提出“以一套 SDK 解决所有通信场景”,使用融云 RTC 的开发者,同时可以用融云 IM 作为信令通道,而不用自己重新搭建或选择第三方信令通道,这样可以大大提升开发效率,减少 SDK 文档学习时间。

总体而言,RTC 低延迟直播是未来发展的趋势,而 RTMP 在当前依然拥有价格上的优势,而两者作为音视频领域的实用技术,无论是适用场景、还是贴近开发的服务都越来越多样化,开发者未来选型之路也将更顺畅。



【融云分析】WebRTC如何通过STUN、ICE协议实现P2P连接

WebRTC赵炳东 发表了文章 • 0 个评论 • 149 次浏览 • 2021-01-21 11:04 • 来自相关话题

WebRTC中两个或多个主机进行P2P连接是通过STUN、TURN、ICE等技术实现的。主机往往都是在NAT之后,且不同的NAT导致外部主机向内网主机发送数据的可见性不同。 内网主机通过STUN协议可以获得NAT分配的外部地址。ICE是主机之间发现P2P传输路... ...查看全部

WebRTC中两个或多个主机进行P2P连接是通过STUN、TURN、ICE等技术实现的。主机往往都是在NAT之后,且不同的NAT导致外部主机向内网主机发送数据的可见性不同。 内网主机通过STUN协议可以获得NAT分配的外部地址。ICE是主机之间发现P2P传输路径机制,ICE中使用了STUN协议进行连通检测、传输路径的指定和保活。 本文将对STUN和ICE协议进行分析和解读,希望能为开发者们带来一些启发和帮助

1. NAT类型

网络地址转换, 简称NAT,节省了IPv4地址空间的使用并且隔离了内网和外网。NAT对待UDP的实现方式有4种,分别如下:

1.1完全圆锥型

一个内网地址(iAddr:iPort)被映射到一个外网地址 (eAddr:ePort)。这个内网(iAddr:iPort)地址发送的数据包都会通过这个外网地址(eAddr:ePort)。外部主机可以通过这个外网地址(eAddr:ePort)向这个内网地址(iAddr:iPort)发送数据包

1.png

1.2地址受限锥型

一个内网地址(iAddr:iPort)被映射到一个外网地址 (eAddr:ePort)。这个内网(iAddr:iPort)地址发送的数据包都会通过这个外网地址(eAddr:ePort)。外部主机 (hAddr:any) 只有接收过从内网(iAddr:iPort)发送来的数据包后,才能通过外部地址 (eAddr:ePort)发送数据包给内网地址(iAddr:iPort)。其中外部主机的端口可以是任意的。

2.png

1.3端口受限锥型

一个内网地址(iAddr:iPort)被映射到一个外网地址 (eAddr:ePort)。这个内网(iAddr:iPort)地址发送的数据包都会通过这个外网地址(eAddr:ePort)。外部主机 (hAddr:hPort) 只有接收过从内网(iAddr:iPort)发送来的数据包后,才能通过外部地址 (eAddr:ePort)发送数据包给内网地址(iAddr:iPort)。其中外部主机的端口hPort是受限的。

3.png

1.4对称型

一个内网地址(iAddr:iPort)向外网地址 (sAddr1:sPort1)发送的多次请求时,NAT会分配同一个外网地址(eAddr1:ePort1)。若向不同的外网地址如(sAddr2:sPort2)发送数据包时,NAT分配另外一个外网地址(eAddr2:ePort2)。外网地址 (sAddr1:sPort1)只有接收过从内网(iAddr:iPort)发送来的数据后,才能通过已经在NAT上开辟的(eAddr1:ePort1)发送数据包给内网.

4.png

2. STUN简介

STUN是Session Traversal Utilities for NAT简写,RFC5389规定了具体内容。STUN协议是用来获取内网地址对应在NAT上的外网地址,NAT穿越。 STUN是C/S模式的协议,由客户端发送STUN请求;STUN服务响应,告知由NAT分配给主机的IP地址和端口号。

2.1 STUN消息结构

STUN消息头为20字节,后面紧跟0或多个属性。STUN头部包含一STUN消息类型、magic cookie、事务ID和消息长度。 

5.png

(图)STUN消息结构

每个STUN消息的最高位前2位必须为0。当多个协议复用同一个端口的时候,这个可以用于与其他协议区分STUN数据包。 消息类型确定消息的类别(如请求、成功回应、失败回应、指示indication)。虽然这里有四种消息类型,但可以分为2类事务:请求/响应事务、指示事务。

magic cookie为固定值0x2112A442。

Transaction ID标识同一个事务的请求和响应。当客户端发送多个STUN请求,通过Transaction ID识别对应的STUN响应。

2.2 STUN消息类型

6.png

(图)STUN消息类型

C0和C1位置的bit指明了消息的分类。其余的12位置标示不同的请求类型,比如绑定请求。

2.3 STUN属性

STUN头之后是0或多个属性。每个属性都采用TLV编码,type为16位的类型、lenght为16位的长度、value为属性值。每个STUN属性必须是4字节对齐。

7.png

(图)STUN属性

STUN属性格式

STUN服务器请求和响应都包含消息属性。一些属性不是强制性的,其中一些只能出现在绑定请求中,而其他一些只能出现在绑定响应中。  属性空间被划分为2个范围。强制理解属性STUN代理必须处理,否则STUN代理将无法正常处理该属性的消息;STUN代理不能理解可选理解属性的话,这些属性可以被忽略。

强制理解属性 (0x0000-0x7FFF):

8.png

 可选理解属性 (0x8000-0xFFFF)

9.png

具体说明如下: MAPPED-ADDRESS属性标识了NAT映射后的地址。

XOR-MAPPED-ADDRESS属性与MAPPED-ADDRESS属性一致,映射后的地址要做异或处理。

USERNAME属性用于消息完整性。用户名和密码包含在消息完整性中。

MESSAGE-INTEGRITY属性是STUN消息的HMAC-SHA1值,长度20字节。MESSAGE-INTEGRITY属性可以出现在任何类型的STUN消息中。用作HMAC输入的文本是STUN消息,包括头部,直到且包括MESSAGE-INTEGRITY属性前面的属性。FINGERPRINT属性出现MESSAGE-INTEGRITY后。所以FINGERPRINT属性外,STUN代理忽略其他出现在MESSAGE-INTEGRITY属性后的任何属性。

FINGERPRINT属性可以出现在所有的STUN消息中,该属性用于区分STUN数据包与其他协议的包。属性的值为采用CRC32方式计算STUN消息直到但不包括FINGERPRINT属性的的结果,并与32位的值0x5354554e异或。

ERROR-CODE属性被用于错误响应消息中。它包含一个在300至699范围内的错误响应号。

REALM属性出现在请求中,表示认证时要用长期资格。出现在响应中,表示服务器希望客户端使用长期资格进行认证。

NONCE属性是出现在请求和响应消息中的一段字符串。

UNKNOWN-ATTRIBUTES属性出现在错误代码为420的的错误响应,表示服务器端无法理解的属性。

SOFTWARE属性用于代理发送消息时所使用的软件的描述。

ALTERNATE-SERVER属性表示STUN客户可以尝试的不同的STUN服务器地址。属性格式与MAPPED-ADDRESS相同。

2.4 STUN示例

下面是Wireshark抓取的一对STUN绑定请求和响应。STUN绑定请求,源地址192.168.2.36:47798,目标地址180.76.137.157:30001。

10.png

STUN绑定响应,源地址180.76.137.157:30001,目标地址192.168.2.36:47798

11.png

其中ICE-CONTROLLING、PRIORITY属性是下面提到的ICE中扩充的属性。

3. ICE简介

ICE两端并不知道所处的网络的位置和NAT类型,通过ICE能够动态的发现最优的传输路径。如下图L和R是ICE代理,下面简称L和R。L和R有各自的传输地址,包括主机的网卡地址、NAT上的外网地址、 TURN服务地址。ICE就是要从这些地址中,找到L和R的候选地址对,实现两端高效连通。

12.png

(图)ICE部署图举例

ICE两端可以通过信令服务器交换SDP信息。ICE使用STUN,TURN等协议来建立会话。

3.1收集候选地址

ICE端收集本地地址。通过STUN服务收集NAT外网地址;通过TURN收集中继地址。

所以有四种候选地址:

13.png

如下图: 主机候选X:x 服务器反射候选X1':x1' 中继候选Y:y 这里称主机候选地址是服务器候选地址的BASE。

14.png

(图)ICE端口

3.2连通检测

L收集了所有的候选地址后,按优先级从高到低排序,通过信令服务器发送SDP offer给R。R收到offer后,收集获选地址,并将自己候选地址放入SDP answer发送给L。此时两端都有了对端的和本端的候选地址。然后配对,生成candidate pair。为了确保candidate pair的有效性,两端都要做连通检测。根据candidate pair,从本地candidate发送STUN请求到远端candidate;接收端返回STUN响应给发送端。如下图。

15.png

(图)ICE基本连通检测

两端都按照各自checklist分别进行检查。

当R收到L的检测时,R发送向L的检测被称为Triggered检测。

3.3 Candidates pair排序

将连通性检查成功的candidate pair按优先级排序加入check list。两端定期遍历这个check list, 发送STUN请求给对端称为Ordinary检测。优先级的计算根据以下原则: 每端给自己的candidate一个优先级数值。本端优先级和远端优先级结合,得到candidate pair的优先级优先级。

公式 priority = (2^24)*(type preference) + (2^8)*(local preference) + (2^0)*(256 - component ID)

16.png

再根据candidate的优先级计算candidate pair的优先级。

priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)

G:controlling candidate 优先级 D:controlled candidate 优先级

3.4提名Candidates

ICE中有两种角色, controlling角色可以选取最终的candidate pair;controlled角色会等待controlling角色选取的candidate pair。 ICE指定一个ICE代理为controlling角色,其他ICE代理为controlled角色。ICE优先检测优先级高的candidate pair。

Controlling角色有两中提名方案:REGULAR提名:当得到至少一对有效的pair的时候,Controlling角色就会选择其中的一个pair作为候选,此次连通检测发送一个带flag的请求,告诉对端这个就是被选中的pair。

17.png

(图)REGULAR提名

AGGRESSIVE提名:Controlling角色会在每个STUN请求之中添加flag标志,最先成功的那个被选为媒体传输通道。

18.png

(图)AGGRESSIVE提名

3.5 ICE示例

下面是例子中,L和R都是full模式ICE代理,采用aggressive提名,传输媒体为RTP。full模式为双方都要进行连通性检查,都要的走一遍流程;lite模式为,full模式ICE一方进行连通性检查,lite一方只需回应response消息。

19.png

(图)ICE举例

便于理解,采用"主机类型-网络类型-序号"的格式表示传输的地址。地址有两个分量,分别是IP和PORT。L,R,STUN,NAT代表不同的主机类型;PUB代表外网,PRV代表内网; L处在内网中,内网地址是10.0.1.1,R处在外网,外网地址是192.0.2.1。L和R都配置了STUN服务,地址是192.0.2.2,端口是3478。L在NAT后面,NAT外网地址是192.0.2.3。序号表示不同的媒体类型,这里只有RTP所以序号为1。 "S="表示STUN消息的发送地址、"D=" 表示STUN消息的接收地址。 "MA=" 表示STUN绑定响应的中mapped address。"USE-CAND" 表示带有"USE-CANDIDATE" STUN消息。

L收集本地候选地址,并发起STUN绑定请求给STUN服务器,L得到 NAT-PUB-1作为服务器反射候选地址。 L计算候选的优先级,主机候选地址type preference为126;服务器反射候选地址type preference为100。local preference为65535。component ID为1 套用公式priority = (2^24)*(type preference) + (2^8)*(local preference) + (2^0)*(256 - component ID) 得主机候选地址的优先级为2130706431,服务器反射候选地址的优先级为1694498815。 L设置主机候选地址的foundation为1,服务器反射候选地址foundation为2。  L将服务器反射候选地址作为default候选地址。对应的offer sdp为

20.png

替换地址后

21.png

因为L和R都采用的是full-mode,这种情况下ICE协议规定发送offer为controlling端,L为controlling端。 L和R生成candidate pair,两端都有2个candidate pair。L会裁减掉包含服务映射候选地址,保留candidate pair为本端$L_PRIV_1、远端$R_PUB_1

22.png

消息9表示R做连通检测,因为R是controlled角色,所以无需设置USE-CANDIDATE。L处于NAT后面,且没有向R发送过请求,所以此次连通检测会失败。

当L收到answer sdp后,开始连通检测(消息10-13)。L采用的是aggressive提名,所以每个请求都会有USE-CANDIDATE。L使用candidate pair为$L_PRIV_1/$R_PUB_1发起的连通检测成功后,L创建一个新的candidate pair,本端为NAT-PUB-1(消息13中得到) 、远端为R-PUB-1(消息10中得到),加入valid list中。这个连通检测中设置了USE-CANDIDA属性,该candidate pair为选中的候选。L的RTP流在valid list中有选中的candidate pair,所以L进入完成状态。

R收到L的STUN绑定请求(消息11)后,R发起消息11对应的Triggered检测,其candidate pair的本端为R-PUB-1、远端为NAT-PUB-1。检测成功后,R创建本端为R-PUB-1、远端为NAT-PUB-1的candidate pair,加入valid list。因为消息11中包含了USE-CANDIDATE,所以这个candidate pair就被选中为这个RTP流的传输通道。R进入完成状态。

4. 总结

本文介绍了NAT、STUN、ICE等基本概念。STUN部分介绍了STUN的消息结构、消息类型和消息属性ICE协议中STUN消息要遵循STUN协议。 ICE部分介绍了ICE代理之间是如何根据各自的网络地址建立连接的步骤有收集候选地址、连通检测、Candidates pair生成与排序、提名Candidates。 详细内容还需查看ICE协议rfc5245以及webrtc的p2p部分的具体实现。


万人群聊的消息分发控速方案

IM即时通讯徐凤年 发表了文章 • 0 个评论 • 114 次浏览 • 2021-01-21 11:04 • 来自相关话题

当前阶段,群聊已经成为主流IM软件的基本功能,不管是亲属群,朋友群亦或是工作群,都是非常常见的场景。随着移动互联网的发展,即时通讯服务被广泛应用到各个行业,客户业务快速发展,传统百人甚至千人上限的群聊已经无法满足很多业务发展需求,所以超大群的业务应运而生。&n... ...查看全部

当前阶段,群聊已经成为主流IM软件的基本功能,不管是亲属群,朋友群亦或是工作群,都是非常常见的场景。随着移动互联网的发展,即时通讯服务被广泛应用到各个行业,客户业务快速发展,传统百人甚至千人上限的群聊已经无法满足很多业务发展需求,所以超大群的业务应运而生。

 

1超大群面临的挑战

我们以一个万人群的模型进行举例:

1、如果群中有人发了消息,那么这条消息需要按照1:9999的比例进行分发投递,如果我们按照常规消息的处理流程,那么消息处理服务压力巨大。

2、消息量大的情况下,服务端向客户端直推消息的处理速度将会成为系统瓶颈,而一旦用户的消息下发队列造成了挤压,会影响到正常的消息分发,也会导致服务缓存使用量激增。

3、在微服务架构中,服务以及存储(DB,缓存)之间的QPS和网络流量也会急剧增高。

4、以群为单位的消息缓存,内存和存储开销较大(消息体的存储被放大了万倍)。

基于这些挑战,我们的服务势必要做一定的优化来应对。

 

2群消息分发模型

整体的群聊服务架构如下图所示:

1.png

   用户在群里发了一条群消息后,消息先到群组服务,然后通过群组服务缓存的群关系,锁定这条消息最终需要分发的目标用户,然后根据一定的策略分发到消息服务上,消息服务再根据用户的在线状态和消息状态来判断这条消息是直推、通知拉取还是转Push,最终投递给目标用户。

 

3超大群消息分发解决方案

3.1分发控速:

第一,首先我们会根据服务器的核数来建立多个群消息分发队列,这些队列我们设置了不同的休眠时间以及不同的消费线程数,这里可以理解为快、中、慢等队列。如下图所示:

2.png

第二,我们根据群成员数量的大小来将所有群映射到相应的队列中,规则是小群映射到快队列中,大群映射到相应的慢队列中。

第三,小群由于人数少,对服务的影响很小,所以服务利用快队列快速的将群消息分发出去,而大群群消息则利用慢队列的相对高延时来起到控速的作用。

3.2 合并分发:

一条群消息发送到IM服务器后,需要从群组服务投递给消息服务,如果每一个群成员都投递一次,并且投递的群消息内容是一致的话,那肯定会造成相应的资源浪费和服务压力。

服务落点计算中我们使用的是一致性哈希,群成员落点相对固定,所以落点一致的群成员我们可以合并成一次请求进行投递,这样就大幅提高了投递效率同时减少了服务的压力。

3.3 超大规模群的处理方案

在实际群聊业务中,还有一种业务场景是超大规模群,这种群的群人数达到了数十万甚至上百万,这种群如果按照上述的分发方案,势必也会造成消息节点的巨大压力。比如我们有一个十万人的群,消息节点五台,消息服务处理消息的上限是一秒钟4000条,那每台消息节点大约会分到2万条群消息,这超出了消息节点的处理能力。

所以为了避免上述问题,我们的超大群(群成员上线超过3000,可以根据服务器数量和服务器配置相应做调整)会用特殊的队列来处理群消息的分发,这个特殊的队列一秒钟往后端消息服务投递的消息数是消息服务处理上限的一半(留相应的能力处理其他消息),如果单台消息服务处理的QPS上限是4000,那群组服务一秒往单台消息服务最多投递2000条。

 

结束语

我们后续也会针对群消息进行引用分发,对于大群里发的消息体比较大的消息,我们给群成员只分发和缓存消息的索引,比如MessageID,等群成员真正拉取群消息时再从将消息组装好给客户端分发下去。这样做会节省分发的流量以及存储的空间。

随着互联网的发展,群组业务的模型和压力也在不停地扩展,后续可能还会遇到更多的挑战,届时我们服务器也会通过更优的处理方式来应对。

 

感兴趣的开发者可以扫码下载融云的 IM 即时通讯 Demo 产品:SealTalk,体验融云的群聊、聊天室等通信能力。

3.png

集成融云 Web 音视频通话踩坑之旅

WebRTC苏道 发表了文章 • 0 个评论 • 293 次浏览 • 2020-12-29 16:44 • 来自相关话题

前言最近有个项目需要使用的融云的 CallLib SDK 实现类似微信的视频通话,所以在项目还未正式启动的时候,我已经偷偷的开始进行集成了,以免到时候不熟一顿加班那真的欲哭无泪了,好消息就是我已经使用过融云家的 IMLib SDK 做过即时通讯的功能,所以整个... ...查看全部

前言

最近有个项目需要使用的融云的 CallLib SDK 实现类似微信的视频通话,所以在项目还未正式启动的时候,我已经偷偷的开始进行集成了,以免到时候不熟一顿加班那真的欲哭无泪了,好消息就是我已经使用过融云家的 IMLib SDK 做过即时通讯的功能,所以整个注册流程和开发者后台的使用已经比较熟了,当然,即时不熟也没关系,跟着他们的文档一步一步来,也能很快的就上手了。
融云官网:https://www.rongcloud.cn/

下面是集成的时候碰到的一些需要注意的问题:

1、Web 站点必须为 localhost 或 https
2、必须成功连接 IM 后, 才可进行 CallLib 通话
3、新版谷歌浏览器(86版本)会报错,我集成的 RTC 版本是 3.2.3
4、音视频通话接通不了

综上问题,我会逐一解答,关于具体的集成可以直接参考:https://docs.rongcloud.cn/v4/views/rtc/call/noui/quick/web.html

还有具体的 demo 也可以参考一下 https://github.com/rongcloud-snippets/web-call-quickstart

Web 站点必须为 localhost 或 https:
这个是融云的使用音视频通话的前置条件,本来在本地调试的时候好好的,可是发布到线上的时候就不能用了,最后提交工单询问融云的技术人员上线是否还需要配置什么,最后排查一圈发现生产环境使用的站点是 http(欲哭无泪。。。),童鞋们引以为戒啊!!

必须成功连接 IM 后, 才可进行 CallLib 通话:
直接看代码:

// appKey 可在融云开发者后台获取
const im = RongIMLib.init({ appkey: '<your-appkey>' })
// 添加事件监听器
im.watch({
  // 连接状态监听
  status(evt) {
    console.log('连接状态码:', evt.status);
  },
  // 消息监听
  message(evt) {
    console.log('收到新消息:', evt.message);
  }
})
// CallLib 初始化
var config = {
    timeout: 20000,
    RongIMLib: RongIMLib,
    RongRTC: RongRTC
};
rongCallLib = RongCallLib.init(config);
//token 可从开发者后台获取 或 Server API
const token = ''
im.connect({ token }).then(user => {
  console.log('链接成功, 链接用户 id 为: ', user.id);
}).catch(error => {
  console.log('链接失败: ', error.code, error.msg);
});

新版谷歌浏览器会报错:
由于浏览器更新,导致 SDK 需要升级,升级到最新版本的 RTC SDK 下载地址:https://cdn.ronghub.com/RongRTC-3.2.6.min.js
需要注意,如果使用的 SDK 2.X 也需要升级到 2.5.10 以上

音视频通话接通不了:
这种情况我总结分析了一下几种情况,如下:

代码传参错误或者书写错误
这个好像没什么说的,只能怪自己不仔细吧!跟着文档来呗

结束通话没有调用挂断方法 hungup
用户主动发起挂断,在 rongCallLib.commandWatch 监听中收到 HungupMessage 消息,可以调用 rongCallLib.hungup 来挂断通话,不然下次呼叫时会出现报错,提示对方正忙

如何识别挂断原因
HungupMessage 消息中 reason 字段及 SummaryMessage 消息中 status 字段都为挂断原因,详情地址参考:https://docs.rongcloud.cn/rtc/calllib/web/code/

因为对音视频的集成也刚开始,还在学习当中。后续随着继续深入,也会同步音视频相关的集成问题,方便复习记录也希望能帮到需要的童鞋!!!


融云 CallLib 集成遇到的问题

IM即时通讯苏道 发表了文章 • 0 个评论 • 278 次浏览 • 2020-12-29 16:41 • 来自相关话题

近期选用融云音视频产品实现类似微信的通话功能, 经过几天的调试, 终于完成了基本功能, 以下总结集成中遇到的问题查看文档首先先查看融云的文档, 介绍不是很详细, 如果不参考 Demo, 集成起来还是比较困难但有一个亮点, 文档内就直接能体验融云 CallLib... ...查看全部

近期选用融云音视频产品实现类似微信的通话功能, 经过几天的调试, 终于完成了基本功能, 以下总结集成中遇到的问题

查看文档

首先先查看融云的文档, 介绍不是很详细, 如果不参考 Demo, 集成起来还是比较困难

但有一个亮点, 文档内就直接能体验融云 CallLib 的效果

融云 CallLib 文档: https://docs.rongcloud.cn/v4/views/rtc/call/noui/intro.html

Demo 参考

Demo 找到两个. 代码都很简单, 没有太多业务代码, 参考起来比较友好

文档中的 Demo: https://github.com/rongcloud-snippets/web-call-quickstart

教程中的 Demo: https://github.com/rongcloud/websdk-demo/tree/master/calllib-v3/

教程类 Demo 包含一个完整的启动教程, 可参考: https://tutorials.rongcloud.cn/tutorial/web-calllib-demo#0

遇到的问题

1、未找到错误码的完整解释

在文档中搜索多遍, 都没有找到错误码的列表. 提工单询问后, 得知只有旧文档中有解释. 新文档还正在添加中

旧文档: https://docs.rongcloud.cn/rtc/calllib/web/code/

2、单对单通话, 一方挂断, 另一方必须也调用挂断方法

设计有些不合理. 应该是考虑兼容多人音视频, 希望单人、多人调用方式保持一致

3、Web 多端登录时, 需要额外处理错误码 8

如果同一个用户在 Web1、Web2 同时登录, 如果用户收到音视频呼叫, Web1 接通后, Web2 会自动挂断, 并抛出一个挂断码 8. 此处逻辑需要额外处理, 给客户一个提示


WebRTC 实现实时音视频技术研究

WebRTC苏道 发表了文章 • 0 个评论 • 300 次浏览 • 2020-12-29 16:39 • 来自相关话题

由于疫情原因很多公司都选择线上办公,让我对实时音视频倍感兴趣,所以决定了解下。首先来画个饼,说下我的熟悉了解的过程。了解下 webRTC 是个什么,能做什么。选一家可以做实时音视频的三方集成实验下,同时也想具体了解小他们是如何实现的。调研是了几家:腾讯,声网和... ...查看全部

由于疫情原因很多公司都选择线上办公,让我对实时音视频倍感兴趣,所以决定了解下。

首先来画个饼,说下我的熟悉了解的过程。

  1. 了解下 webRTC 是个什么,能做什么。

  2. 选一家可以做实时音视频的三方集成实验下,同时也想具体了解小他们是如何实现的。

调研是了几家:腾讯,声网和融云,这三家,最后选中了融云。
首先说下为什么选择了融云:首先作为个人开发者,而且是对这块想了解的用户,融云在我注册后给我了很快的回访,我说明我的需要还有人很细致的介绍了下,体验不错。就那你先试试吧,不过有点不给力的是,集成中遇到些问题,不过提工单还是解决了。虽然过程略微有点磕磕绊绊~~~~

首先按照画饼的步骤说下 WebRTC

WebRTC 简介
WebRTC 是一个由 Google 发起的实时通讯解决方案,其中包含视频音频采集,编解码,数据传输,音视频展示等功能,我们可以通过技术快速地构建出一个音视频通讯应用。 虽然其名为 WebRTC,但是实际上它不光支持 Web 之间的音视频通讯,还支持 Android 以及 IOS 端,此外由于该项目是开源的,我们也可以通过编译 C++代码,从而达到全平台的互通。

WebRTC 架构介绍

咳咳~~ 介绍说的有点生硬,这个查 WebRTC 相关资料室看到的。不过个人看架构图还是研究了下,有点收获的大家也可以细看看,有不明白的可以深入了解下,很有意思的

再说下集成融云音视频的一些问题:

  1. 下载了 Demo,跑步起来,报了个 RongRTC-3.2.3.js:8335 Uncaught (in promise) DOMException: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to parse SessionDescription. m=video 9 UDP/TLS/RTP/SAVPF 98 99 96 97 100 101 127 Invalid value:

  2. 获取本地资源失败

说说我是如何解决的吧:

先说第二个问题吧。获取失败是因为我开了两个浏览器,有一个浏览器已经占用了资源,在第二个浏览器访问时就报了资源获取失败。好吧~只能这样了,那就不用两个浏览器呗~~~

再说问题一,由于本来就是抱着学习的态度去了解 RTC 一看报了 RTCPeerConnection 很感兴趣,正好可以好好梳理下。说以了解了下 RTCPeerConnection 和 SDP

RTCPeerConnection: 一个 RTCPeerConnection 对象允许用户在两个浏览器之间直接通讯。

SDP 中都有哪些属性,分别是什么含义:

//版本
v=0
//<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
o=- 3089712662142082488 2 IN IP4 127.0.0.1
//会话名
s=-
//会话的起始时间和结束时间,0代表没有限制
t=0 0
//表示音频传输和data channel传输共用一个传输通道传输的媒体,通过id进行区分不同的流
a=group:BUNDLE audio data
//WebRTC Media Stream
a=msid-semantic: WMS
//m=audio说明本会话包含音频,9代表音频使用端口9来传输,但是在webrtc中现在一般不使用,如果设置为0,代表不传输音频
//使用UDP来传输RTP包,并使用TLS加密, SAVPF代表使用srtcp的反馈机制来控制通信过程
//111 103 104 9 0 8 106 105 13 110 112 113 126表示支持的编码,和后面的a=rtpmap对应
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
//表示你要用来接收或者发送音频使用的IP地址, webrtc使用ice传输,不使用这个地址, 关于ICE是什么后面会讲到
c=IN IP4 0.0.0.0
//用来传输rtcp的地址和端口,webrtc中不使用
a=rtcp:9 IN IP4 0.0.0.0
//ice协商过程中的安全验证信息
a=ice-ufrag:ubhd
a=ice-pwd:l82NnsGm5i7pucQRchNdjA6B
//支持trickle,即sdp里面只描述媒体信息, ice候选项的信息另行通知
a=ice-options:trickle
//dtls协商过程中需要的认证信息
a=fingerprint:sha-256 CA:83:D0:0F:3B:27:4C:8F:F4:DB:34:58:AC:A6:5D:36:01:07:9F:2B:1D:95:29:AD:0C:F8:08:68:34:D8:62:A7
a=setup:active
//前面BUNDLE行中用到的媒体标识
a=mid:audio
//指出要在rtp头部中加入音量信息
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
//当前客户端只接受数据,不发送数据,recvonly,sendonly,inactive,sendrecv
a=recvonly
//rtp,rtcp包使用同一个端口来传输
a=rtcp-mux
//下面都是对m=audio这一行的媒体编码补充说明,指出了编码采用的编号,采样率,声道等
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
//对opus编码可选的补充说明,minptime代表最小打包时长是10ms,useinbandfec=1代表使用opus编码内置fec特性
a=fmtp:111 minptime=10;useinbandfec=1
//下面就是对Data Channel的描述,基本和上面的audio描述类似,使用DTLS加密,使用SCTP传输
m=application 9 DTLS/SCTP 5000
c=IN IP4 0.0.0.0
//可以是CT或AS,CT方式是设置整个会议的带宽,AS是设置单个会话的带宽。缺省带宽是千比特每秒
b=AS:30
a=ice-ufrag:ubhd
a=ice-pwd:l82NnsGm5i7pucQRchNdjA6B
a=ice-options:trickle
a=fingerprint:sha-256 CA:83:D0:0F:3B:27:4C:8F:F4:DB:34:58:AC:A6:5D:36:01:07:9F:2B:1D:95:29:AD:0C:F8:08:68:34:D8:62:A7
a=setup:active
//前面BUNDLE行中用到的媒体标识
a=mid:data
//使用端口5000,一个消息的大小是1024比特
a=sctpmap:5000 webrtc-datachannel 1024

好吧了解完这些还是没解决,于是我提了工单,额~让我升级 SDK 说 chrome 升级优化了协议,SDK 有配套优化更新。fine~

问题解决。很开心这次体验,虽然 Demo 写的很简单还借鉴了融云的 Demo ,不过本身就是为了学习 WebRTC 东西。整个历程还是收获匪浅的。

梳理了下融云整体的 SDK 结构大体分了两大概念。

  1. 房间的概念

  2. 资源流的概念

房间是一个抽象概念,目的是加入房间,然后再使用 PeerConnection 建立连接进行通话。所以房间模型中提供了加入,离开获取房间内信息等接口

资源流主要是指媒体流,用到的是 WebRTC 的 navigator.mediaDevices.getUserMedia 来获取视频流,在通过建立的 PeerConnection 连接,对自己和他人的流进行发布和订阅等操作。具体可以看参考文旦中的 PeerConnection 介绍的连接,本人也是通过此处了解的,不过还不是很透彻还只是了解皮毛。

融云提供的接口还是蛮简洁的,用起来很方便。上几块代码大家可以看下如果感兴趣也可以多了解了解。



截图借鉴的融云官方文档的,没有贴自己的代码,因为我的代码里会有些自己的业务逻辑。

本文纯属个人学习总结,为了了解 WebRTC,也集成了下融云的 SDK !不喜勿喷哦 ~~~ ❤

参考文案:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection

https://baike.baidu.com/item/WebRTC/5522744

融云官网:https://www.rongcloud.cn/

集成文档:https://docs.rongcloud.cn/v2/views/rtc/meeting/intro/ability.html


集成融云小程序 SDK 遇到的问题

IM即时通讯木土走召 发表了文章 • 0 个评论 • 311 次浏览 • 2020-12-24 14:33 • 来自相关话题

集成版本最初集成 3.0.5, 最后更新至 3.0.7.1文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/setting/include/mini.html... ...查看全部

集成版本

最初集成 3.0.5, 最后更新至 3.0.7.1

文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/setting/include/mini.html

问题列表

1、链接返回 31012

文档解释 31012 为安全域名错误, 请至开发者后台查看设置的安全域名

但小程序开发, 不应该涉及安全域名问题. 所以只能到融云开发者后台提工单查找解决问题

咨询结果后, 原因为: 融云 3.0.5 SDK 在小程序环境中, 错误的认为当前环境为 Web, 且我自己的 AppKey 确实在融云开发者后台设置了安全域名. 导致有此限制

解决方法有两种:

  • 升级 SDK 至当前最新的 3.0.7.1. 最新 SDK 已修复此问题

  • 去掉我自己融云开发者后台的安全域名

两者选其一即可

2、在模拟器运行没有问题, 真机运行无法链接

通过微信开发者工具 + 真机调试, 发现报错微信合法域名限制

按照融云文档配置合法域名后, 此问题解决

文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/setting/include/mini.html#domain

3、发送图片消息, 移动端无法显示, 但小程序可正常显示

通过文档, 发现是小程序端发送图片消息时, content 内不包含缩略图字段

融云 IMKit 默认是展示缩略图的, 而本小程序项目内的渲染逻辑为显示 remoteUrl, 最终导致了此问题

按照文档添加 content 字段后, 此问题解决

文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/msgmanage/msgsend/web.html?plat=mini#ImgMsg


通过融云 WebSDK 简单了解 WebSocket

IM即时通讯苏道 发表了文章 • 0 个评论 • 79 次浏览 • 2020-12-24 14:33 • 来自相关话题

近期使用融云开发聊天页面, 通过抓包, 发现融云 SDK 使用 WebSocket 实现与服务端通讯, 由此简单了解下 WebSocket 的使用需要 WebSocket 的原因:一句话总结: HTTP 协议只能由客户端发起, 服务端响应. WebSocket... ...查看全部

近期使用融云开发聊天页面, 通过抓包, 发现融云 SDK 使用 WebSocket 实现与服务端通讯, 由此简单了解下 WebSocket 的使用

需要 WebSocket 的原因:

一句话总结: HTTP 协议只能由客户端发起, 服务端响应. WebSocket 可服务端主动发起, 也可客户端发起. 能实现的场景更多、更复杂

相关资料如下:

融云开发文档: https://docs.rongcloud.cn/v4/

WebSocket Web API: https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

WebSocket Node API: socket.iowsuWebSockets (任选其一)

融云抓包

测试地址: https://rongcloud.github.io/websdk-demo/api-test/

通过抓包可知, 融云链接 websocket 时还带了 appkey、token、SDK 版本信息

接下来实现一个简单地 客户端 + 服务端示例

Nodejs 服务端

此处示例使用 ws 模块

创建链接:

const Port = '2928';
const wss = new WebSocket.Server({ port: Port });
wss.on('connection', function (ws, event) {
  const url = event.url; // url 为客户端链接地址, 可拼接参数. 比如融云拼接了 appkey 等信息: ?appid=n19xxxxxx&token=xxxxxxxx
  /* 监听链接关闭 */
  ws.on('close', () => {
    // 此 ws 链接已断开. do something
  });
});

监听客户端消息:

const Port = '2928';
const wss = new WebSocket.Server({ port: Port });
wss.on('connection', function (ws, event) {
  ws.on('message', function (data) {
    console.log('data', data); // 收到客户端发送的数据 do something
  });
});

向客户端发送请求:

const Port = '2928';
const wss = new WebSocket.Server({ port: Port });
wss.on('connection', function (ws, event) {
  ws.send('some data');
});

Web 客户端

具体 API 可参考: WebSocket

建立链接:

const socket = new WebSocket('ws://localhost:2928');
socket.addEventListener('open', function (event) {
  // 链接成功, do something
});

监听服务端消息:

const socket = new WebSocket('ws://localhost:2928');
socket.addEventListener('message', function (event) {
  console.log('收到服务端发送的数据', event.data);
});

向服务端发送请求:

const socket = new WebSocket('ws://localhost:2928');
socket.addEventListener('open', function (event) {
  socket.send('some data'); // 链接后, 调用 send 发送
});


前端音视频之WebRTC初探

WebRTC大兴 发表了文章 • 0 个评论 • 138 次浏览 • 2020-10-22 17:35 • 来自相关话题

今天,我们来一起学习一下 WebRTC,相信你已经对这个前端音视频网红儿有所耳闻了。WebRTC Web Real-Time Communication 网页即时通信WebRTC 于 2011 年 6 月 1 日开源,并在 Google、Mozilla、Ope... ...查看全部

今天,我们来一起学习一下 WebRTC,相信你已经对这个前端音视频网红儿有所耳闻了。

WebRTC Web Real-Time Communication 网页即时通信

WebRTC 于 2011 年 6 月 1 日开源,并在 Google、Mozilla、Opera 等大佬们的支持下被纳入 W3C 推荐标准,它给浏览器和移动应用提供了即时通信的能力。

WebRTC 优势及应用场景

优势

  • 跨平台(Web、Windows、MacOS、Linux、iOS、Android)
  • 实时传输
  • 音视频引擎
  • 免费、免插件、免安装
  • 主流浏览器支持
  • 强大的打洞能力

应用场景

在线教育、在线医疗、音视频会议、即时通讯工具、直播、共享远程桌面、P2P网络加速、游戏(狼人杀、线上KTV)等。

1.png

(有喜欢玩狼人杀的同学吗?有时间可以一起来一局,给我一轮听发言的时间,给你裸点狼坑,一个坑容错。)

WebRTC 整体架构

拉回来,我们看一看 WebRTC 的整体架构,我用不同的颜色标识出了各层级所代表的含义。

2.png

  • Web 应用
  • Web API
  • WebRTC C++ API
  • Session Management 信令管理
  • Transport 传输层
  • Voice Engine 音频引擎
  • Video Engine 视频处理引擎

我们再来看下核心的模块:

Voice Engine 音频引擎

VoIP 软件开发商 Global IP Solutions 提供的 GIPS 引擎可以说是世界上最好的语音引擎,谷歌大佬一举将其收购并开源,也就是 WebRTC 中的 音频引擎。

  • iSAC:WebRTC 音频引擎的默认编解码器,针对 VoIP 和音频流的宽带和超宽带音频编解码器。
  • iLBC:VoIP 音频流的窄带语音编解码器。
  • NetEQ For Voice:针对音频软件实现的语音信号处理元件。NetEQ 算法是自适应抖动控制算法以及语音包丢失隐藏算法,能够有效的处理网络抖动和语音包丢失时对语音质量产生的影响。
  • Acoustic Echo Canceler:AEC,回声消除器。
  • Noise Reduction:NR,噪声抑制。

Video Engine 视频处理引擎

VPx 系列视频编解码器是 Google 大佬收购 ON2 公司后开源的。

  • VP8:视频图像编解码器,WebRTC 视频引擎默认的编解码器。
  • Video Jitter Buffer:视频抖动缓冲器模块。
  • Image Enhancements:图像质量增强模块。

WebRTC 通信原理

媒体协商

媒体协商也就是让双方可以找到共同支持的媒体能力,比如双方都支持的编解码器,这样才能实现彼此之间的音视频通信。

SDP Session Description Protocal

媒体协商所交换的数据就是 SDP,说是协议,其实 SDP 并不是一个真正的协议,它就是一种描述各端“能力”的数据格式。

3.png

上图所示就是 SDP 的一部分,详细内容请参考:SDP: Session Description Protocol

https://tools.ietf.org/html/rfc4566

或者参考卡神的这篇文章:WebRTC:会话描述协议SDP

https://zhuanlan.zhihu.com/p/75492311

网络协商

ICE Interactive Connectivity Establishment 互动式连接建立

想要建立连接,我们要需要拿到双方 IP 和端口的信息,在当下复杂的网络环境下,ICE 统一了各种 NAT 穿越技术(STUN、TURN),可以让客户端成功地穿透远程用户与网络之间可能存在的各类防火墙。

STUN、TURN

STUN:简单 UDP 穿透 NAT,可以使位于 NAT(或多重 NAT) 后的客户端找出自己的公网 IP 地址,以及查出自己位于哪种类型的 NAT 及 NAT 所绑定的 Internet 端口。

我们知道,NAT 主要有以下四个种类:

  • 完全锥型 NAT
  • IP 限制锥型
  • 端口限制锥型
  • 对称型

前三种都可以使用 STUN 穿透,而面对第四种类型,也是大型公司网络中经常采用的对称型 NAT ,这时的路由器只会接受之前连线过的节点所建立的连线。

那么想要处理这种网络情况,我们就需要使用 TURN (中继穿透 NAT) 技术。

TURN 是 STUN 的一个扩展,其主要添加了中继功能。在 STUN 服务器的基础上,再添加几台 TURN 服务器,如果 STUN 分配公网 IP 失败,则可以通过 TURN 服务器请求公网 IP 地址作为中继地址,将媒体数据通过 TURN 服务器进行中转。

信令服务器 Signal Server

拿到了双方的媒体信息(SDP)和网络信息(Candidate)后,我们还需要一台信令服务器作为中间商来转发交换它们。

信令服务器还可以实现一些 IM 功能,比如房间管理,用户进入、退出等。

小结

本文我们了解了 WebRTC 优势及应用场景、WebRTC 的整体架构及主要模块构成以及 WebRTC 的通信原理。这些基础知识和概念是需要我们牢记的,大家要记牢~

参考

  • 《从 0 打造音视频直播系统》 李超
  • 《WebRTC 音视频开发 React+Flutter+Go 实战》 亢少军
  • https://webrtc.github.io/webrtc-org/architecture/
  • https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API
  • https://www.w3.org/TR/webrtc/


本文转自公众号“前端食堂”,作者霍语佳

LiveVideoStackCon 深圳站:融云解析 WebRTC 低延迟直播技术

WebRTC融云那些事 发表了文章 • 0 个评论 • 252 次浏览 • 2020-06-16 18:36 • 来自相关话题

“基于 WebRTC 的低延迟直播将会是未来直播行业的主流解决方案!”这是融云联合创始人兼CTO 杨攀在 8 月 LiveVideoStackCon 2019 音视频技术大会北京站上对于未来行业趋势的判断。仅仅 4 个月之后,当大会首次落户有“中国硅谷”之称的... ...查看全部

“基于 WebRTC 的低延迟直播将会是未来直播行业的主流解决方案!”这是融云联合创始人兼CTO 杨攀在 8 月 LiveVideoStackCon 2019 音视频技术大会北京站上对于未来行业趋势的判断。仅仅 4 个月之后,当大会首次落户有“中国硅谷”之称的深圳时,融云的另一位技术专家,首席架构师李淼就“基于 WebRTC 的低延迟直播方案”进行了深入的技术分享。

12 月 13-14 日,LiveVideoStackCon 音视频技术大会在深圳举办,大会聚焦音视频、图像、AI 等技术的最新探索与应用实践,覆盖社交、游戏、直播、智能设备等行业领域,面向开发者分享技术创新与最佳实践。本次大会,聚集了数十名海内外技术专家和上千名开发者围绕前沿技术发展进行探讨。


融云首席架构师李淼
随着我国 5G 正式走向商用,直播行业在获得更多发展机遇的同时,也对直播技术提出了新的挑战。传统直播解决方案如果无法解决技术层面导致的延时问题,那么这一弊病将在 5G 的高速网络环境下被无限放大,这也进一步促使了低延迟音视频直播技术方案的演化。对此,李淼结合 WebRTC 的低延迟特性,在现场展示了融云 WebRTC 直播场景的构建全过程及服务架构设计,并向开发者们分享了技术实践细节,希望通过新技术的应用来解决视频直播的延时问题。

为什么要选用 WebRTC 来做直播?李淼表示,相较于传统的直播解决方案,WebRTC 拥有着不可比拟的三大优势。首先是低延时,让直播用户可以享受低延时的观看体验。目前直播行业中绝大多数产品是基于 RTMP、HLS、HDL 方式构建的,即使在不考虑网络链路的情况下,也会产生秒级的延迟,而 WebRTC 则天生具备低延迟的优势,使用 WebRTC 直播可有效将延迟降低至 200ms 以下。

其次是流量消耗小。基于 UDP 传输的 WebRTC 相比基于 TCP 传输的 RTMP 等协议,由于 UDP 协议内容较 TCP 小,且数据包是基于 NACK 进行传输等特点,对于流量的使用也有明显的降低。对于开发者和直播企业而言,流量消耗大幅削减,成本也因此可以得到有效的控制。

而最重要的优势在于 WebRTC 技术方案可以使主播端与观众端保持一致。当主播端使用  WebRTC 进行推流时,主播端与观众端保持一致,可以减少开发的编码量,对于团队人员的占用和后期对于代码的维护,都能保证最低的资源消耗。

在 LiveVideoStackCon 现场,李淼向开发者讲解了如何通过 WebRTC 完成直播场景构建的全过程,并对于 WebRTC 直播的技术细节一一进行了详细解读。李淼表示,使用 WebRTC 直播方案,MCU 服务器的设计至关重要。一方面 MCU 可以按需进行编解码,另一方面需要以房间号进行聚合,记录每台MCU的状态并按最小资源分配新房间,通过这种设计来减少 WebRTC 直播方案的资源消耗。


WebRTC 直播发布订阅流程
当然,对于很多开发者而言,实际的生产环境中仍面临着如何做到秒开视频、降低 MCU 带宽压力以及避免流量风暴等难题,李淼从 GOP 缓存结构和 GOP 控制策略两个层面进行了分析。以解决首帧卡顿延迟为例,直播数据在客户端与 Media Sever 进行交互之后,通常会对 SPS 和 I 帧进行正常下发,但是在随后的 P 帧或 B 帧的下发阶段,融云会采用 1.2 倍速下发的方式进行,直至所有数据包与 MCU 端推包进程同步,这就将直播延迟降至了最低。

此外,李淼还指出,客户端的设计必须考虑就近接入,且支持多链路选择,数据中心间同源音视频只有一路级联;同时还可以利用 IaaS 层的能力,进行中心间级联链路的优化。遵循这些直播网络设计原则都可以有效地降低直播延迟。

在分享的最后,李淼表示在 5G 时代,直播、短视频等内容传播形态将迎来新一轮技术升级,用户体验将成为行业洗牌的关键,此次将 WebRTC 低延迟直播的设计理念和技术要点与开发者和行业人士们一同分享,希望能够给业界带来一些启发和思考。作为互联网通信云行业的技术领导者,融云也将持续优化实时音视频技术和场景化解决方案,助力音视频直播行业在 5G 时代的创新发展。

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

WebRTC梅川酷子 发表了文章 • 0 个评论 • 21 次浏览 • 5 天前 • 来自相关话题

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


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

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


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

1.png

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).
3.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 的处理关系如下。


2.png

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。

3.png

4.3 级联的考虑


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

5.png

下面是这部逻辑的伪代码:
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. 总结


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


RTC vs RTMP,适合的才是最好的!

WebRTC赵炳东 发表了文章 • 0 个评论 • 143 次浏览 • 2021-01-29 16:17 • 来自相关话题

随着在线教育、电商直播、泛娱乐社交等 App 的普及,实时音视频技术的应用需求也越来越多元化。目前,市场中能够支持音视频通信的主流技术有“RTMP+CDN”和“RTC”两大阵营。选型时,开发者如何根据场景选择更适合自己的通信技术?这就要从两者的技术特点、价格、... ...查看全部

随着在线教育、电商直播、泛娱乐社交等 App 的普及,实时音视频技术的应用需求也越来越多元化。目前,市场中能够支持音视频通信的主流技术有“RTMP+CDN”和“RTC”两大阵营。选型时,开发者如何根据场景选择更适合自己的通信技术?这就要从两者的技术特点、价格、厂商服务综合考虑。1.png

RTMP+CDN 技术特点与适用场景

RTMP (Real Time Messaging Protocol)基于 TCP 的流媒体传输协议,最大的特点是与 CDN 的强绑定,需要借助 CDN 的负载均衡系统将内容推送到接近用户的边缘节点,使用户就近取得所需内容,提高用户访问的响应速度和成功率,解决因分布、带宽、服务器性能带来的访问延迟问题。更多适用于站点加速、点播、短视频等场景。

对于初次通过 CDN 服务来实现音视频通信的开发者来说,技术指标应主要关注延时、卡顿率、下载速度、打开速度、回源率、宽带冗余提升率等几个维度。

有研究表明,在 0.1s 以下的延迟,用户几乎是无感知的;1s 左右的延迟,用户会明显注意到延时的发生,但在该时间内思维依然是连贯的;超过 10s 的延时,用户会失去等待的耐心。在所有关键技术指标中,控制延时是 CDN 最需要提升的。

以直播场景为例,延时主要看 2 个核心指标:首播时间和再缓存时间。首播时间即从打开到看到视频画面的时间,会受域名解析、连接、第一包时间的影响,首播时间控制在 1 秒内算是不错的效果。其次是再缓冲时间,是用户观看视频时的卡顿时间。由于实际服务中视频长度不一,一般会做播放的体验统计,主要监测的是卡顿率。行业内而言,直播首播时间 300ms,卡顿率在 15% 以下算是优质的通信服务。

目前的 CDN,通常有 3-5 秒的延迟,在浏览图片、短视频等内容时用户感知不明显,对于不需要实时强互动的直播,比如体育赛事网络直播、演唱会网络直播、新闻现场直播,延迟是可以接受的,并不会影响用户体验。

2.png

而在线视频会议、在线教育、电商直播、远程医疗会诊这些对互动有非常高要求的场景,RTMP+CDN 的模式与这些场景对于低延时、无卡顿的要求有一定差距。这时,选择 RTC 技术才能更好地满足开发者的需求。

RTC 技术特点与适用场景

说到 RTC(Real Time Communication)实时音视频通信,它最大的特点就是低延时和无卡顿。从功能流程上说,它包含了采集、编码、前后处理、传输、解码、缓冲、渲染等诸多环节,RTC 不是靠“优化”各环节去实现的实时互动,而是依靠推流端实时的传输机制。

3.png

很多实时音视频服务专业厂商使用的就是 WebRTC 标准,这是一种基于浏览器的实时通信的开源解决方案,使用 UDP 私有协议来进行媒体推流,而不需要创建离散的媒体段;并且它是面向无连接的,没有 TCP 连接断开时的挥手确认连接关闭的机制,基于这两点,WebRTC 能够做到毫秒级的低延迟,远远低于基于 RTMP 协议的 CDN 分发的延迟。而且,它直接通过浏览器就可以完成推流和播放,对于开发者接入来说实在太方便。

因此,WebRTC 标准针对有高互动性要求的直播场景尤为适宜。以直播连麦为例,主播端把通信直播流发到观众端,同时也可以把观众端拉上麦,实现主播和观众的互动。使用 WebRTC,内容实时传输,主播和观众可以进行音视频连麦互动,实时沟通,延时一般低至 400ms 以内。

4.png

基于 WebRTC 标准的融云实时音视频服务,拥有超低延迟的优势,同时也支持将 RTC 音视频流合流(MCU)转码为 RTMP,并推流到第三方 CDN 上,保留了标准协议普遍被 CDN 网络支持的好处。目前,融云音视频通话,可做到全球端到端延时小于 400ms,最低延时 66ms;低延时互动直播的直播推流可以做到主播观众间延迟在 300ms 左右,保障端到端之间延迟无感知的实时互动。

CDN vs RTC 选型还需看价格服务综合比

一套实时音视频通信能力的搭建,除了要根据场景选择适合的技术外,还要看价格、服务的综合性价比。通常来说,使用 RTC 技术的成本比 RTMP+CDN 高。因为,从实践来看,UDP 传输比 TCP 传输对资源消耗要多,而且重传、封包、FEC 冗余计算等都会额外增加计算量,在多进程模式下可能还会遇到内存资源的过多消耗,这些都导致开发及使用成本的增加。

开发者选型中,性价比需综合技术特点、适用场景、价格和服务四个方面的全面考量。服务在产品上线前后的开发阶段和运营阶段,都要发挥重要作用。目前,开发者服务做得比较好的厂商比如融云,会与开发者共建开发文档,技术手册短视频化,提供场景化的 Demo,以及在官网搭建开发者专区,帮助开发者更便捷、更快速的理解 SDK。

融云全新升级的实时音视频服务,提出“以一套 SDK 解决所有通信场景”,使用融云 RTC 的开发者,同时可以用融云 IM 作为信令通道,而不用自己重新搭建或选择第三方信令通道,这样可以大大提升开发效率,减少 SDK 文档学习时间。

总体而言,RTC 低延迟直播是未来发展的趋势,而 RTMP 在当前依然拥有价格上的优势,而两者作为音视频领域的实用技术,无论是适用场景、还是贴近开发的服务都越来越多样化,开发者未来选型之路也将更顺畅。



【融云分析】WebRTC如何通过STUN、ICE协议实现P2P连接

WebRTC赵炳东 发表了文章 • 0 个评论 • 149 次浏览 • 2021-01-21 11:04 • 来自相关话题

WebRTC中两个或多个主机进行P2P连接是通过STUN、TURN、ICE等技术实现的。主机往往都是在NAT之后,且不同的NAT导致外部主机向内网主机发送数据的可见性不同。 内网主机通过STUN协议可以获得NAT分配的外部地址。ICE是主机之间发现P2P传输路... ...查看全部

WebRTC中两个或多个主机进行P2P连接是通过STUN、TURN、ICE等技术实现的。主机往往都是在NAT之后,且不同的NAT导致外部主机向内网主机发送数据的可见性不同。 内网主机通过STUN协议可以获得NAT分配的外部地址。ICE是主机之间发现P2P传输路径机制,ICE中使用了STUN协议进行连通检测、传输路径的指定和保活。 本文将对STUN和ICE协议进行分析和解读,希望能为开发者们带来一些启发和帮助

1. NAT类型

网络地址转换, 简称NAT,节省了IPv4地址空间的使用并且隔离了内网和外网。NAT对待UDP的实现方式有4种,分别如下:

1.1完全圆锥型

一个内网地址(iAddr:iPort)被映射到一个外网地址 (eAddr:ePort)。这个内网(iAddr:iPort)地址发送的数据包都会通过这个外网地址(eAddr:ePort)。外部主机可以通过这个外网地址(eAddr:ePort)向这个内网地址(iAddr:iPort)发送数据包

1.png

1.2地址受限锥型

一个内网地址(iAddr:iPort)被映射到一个外网地址 (eAddr:ePort)。这个内网(iAddr:iPort)地址发送的数据包都会通过这个外网地址(eAddr:ePort)。外部主机 (hAddr:any) 只有接收过从内网(iAddr:iPort)发送来的数据包后,才能通过外部地址 (eAddr:ePort)发送数据包给内网地址(iAddr:iPort)。其中外部主机的端口可以是任意的。

2.png

1.3端口受限锥型

一个内网地址(iAddr:iPort)被映射到一个外网地址 (eAddr:ePort)。这个内网(iAddr:iPort)地址发送的数据包都会通过这个外网地址(eAddr:ePort)。外部主机 (hAddr:hPort) 只有接收过从内网(iAddr:iPort)发送来的数据包后,才能通过外部地址 (eAddr:ePort)发送数据包给内网地址(iAddr:iPort)。其中外部主机的端口hPort是受限的。

3.png

1.4对称型

一个内网地址(iAddr:iPort)向外网地址 (sAddr1:sPort1)发送的多次请求时,NAT会分配同一个外网地址(eAddr1:ePort1)。若向不同的外网地址如(sAddr2:sPort2)发送数据包时,NAT分配另外一个外网地址(eAddr2:ePort2)。外网地址 (sAddr1:sPort1)只有接收过从内网(iAddr:iPort)发送来的数据后,才能通过已经在NAT上开辟的(eAddr1:ePort1)发送数据包给内网.

4.png

2. STUN简介

STUN是Session Traversal Utilities for NAT简写,RFC5389规定了具体内容。STUN协议是用来获取内网地址对应在NAT上的外网地址,NAT穿越。 STUN是C/S模式的协议,由客户端发送STUN请求;STUN服务响应,告知由NAT分配给主机的IP地址和端口号。

2.1 STUN消息结构

STUN消息头为20字节,后面紧跟0或多个属性。STUN头部包含一STUN消息类型、magic cookie、事务ID和消息长度。 

5.png

(图)STUN消息结构

每个STUN消息的最高位前2位必须为0。当多个协议复用同一个端口的时候,这个可以用于与其他协议区分STUN数据包。 消息类型确定消息的类别(如请求、成功回应、失败回应、指示indication)。虽然这里有四种消息类型,但可以分为2类事务:请求/响应事务、指示事务。

magic cookie为固定值0x2112A442。

Transaction ID标识同一个事务的请求和响应。当客户端发送多个STUN请求,通过Transaction ID识别对应的STUN响应。

2.2 STUN消息类型

6.png

(图)STUN消息类型

C0和C1位置的bit指明了消息的分类。其余的12位置标示不同的请求类型,比如绑定请求。

2.3 STUN属性

STUN头之后是0或多个属性。每个属性都采用TLV编码,type为16位的类型、lenght为16位的长度、value为属性值。每个STUN属性必须是4字节对齐。

7.png

(图)STUN属性

STUN属性格式

STUN服务器请求和响应都包含消息属性。一些属性不是强制性的,其中一些只能出现在绑定请求中,而其他一些只能出现在绑定响应中。  属性空间被划分为2个范围。强制理解属性STUN代理必须处理,否则STUN代理将无法正常处理该属性的消息;STUN代理不能理解可选理解属性的话,这些属性可以被忽略。

强制理解属性 (0x0000-0x7FFF):

8.png

 可选理解属性 (0x8000-0xFFFF)

9.png

具体说明如下: MAPPED-ADDRESS属性标识了NAT映射后的地址。

XOR-MAPPED-ADDRESS属性与MAPPED-ADDRESS属性一致,映射后的地址要做异或处理。

USERNAME属性用于消息完整性。用户名和密码包含在消息完整性中。

MESSAGE-INTEGRITY属性是STUN消息的HMAC-SHA1值,长度20字节。MESSAGE-INTEGRITY属性可以出现在任何类型的STUN消息中。用作HMAC输入的文本是STUN消息,包括头部,直到且包括MESSAGE-INTEGRITY属性前面的属性。FINGERPRINT属性出现MESSAGE-INTEGRITY后。所以FINGERPRINT属性外,STUN代理忽略其他出现在MESSAGE-INTEGRITY属性后的任何属性。

FINGERPRINT属性可以出现在所有的STUN消息中,该属性用于区分STUN数据包与其他协议的包。属性的值为采用CRC32方式计算STUN消息直到但不包括FINGERPRINT属性的的结果,并与32位的值0x5354554e异或。

ERROR-CODE属性被用于错误响应消息中。它包含一个在300至699范围内的错误响应号。

REALM属性出现在请求中,表示认证时要用长期资格。出现在响应中,表示服务器希望客户端使用长期资格进行认证。

NONCE属性是出现在请求和响应消息中的一段字符串。

UNKNOWN-ATTRIBUTES属性出现在错误代码为420的的错误响应,表示服务器端无法理解的属性。

SOFTWARE属性用于代理发送消息时所使用的软件的描述。

ALTERNATE-SERVER属性表示STUN客户可以尝试的不同的STUN服务器地址。属性格式与MAPPED-ADDRESS相同。

2.4 STUN示例

下面是Wireshark抓取的一对STUN绑定请求和响应。STUN绑定请求,源地址192.168.2.36:47798,目标地址180.76.137.157:30001。

10.png

STUN绑定响应,源地址180.76.137.157:30001,目标地址192.168.2.36:47798

11.png

其中ICE-CONTROLLING、PRIORITY属性是下面提到的ICE中扩充的属性。

3. ICE简介

ICE两端并不知道所处的网络的位置和NAT类型,通过ICE能够动态的发现最优的传输路径。如下图L和R是ICE代理,下面简称L和R。L和R有各自的传输地址,包括主机的网卡地址、NAT上的外网地址、 TURN服务地址。ICE就是要从这些地址中,找到L和R的候选地址对,实现两端高效连通。

12.png

(图)ICE部署图举例

ICE两端可以通过信令服务器交换SDP信息。ICE使用STUN,TURN等协议来建立会话。

3.1收集候选地址

ICE端收集本地地址。通过STUN服务收集NAT外网地址;通过TURN收集中继地址。

所以有四种候选地址:

13.png

如下图: 主机候选X:x 服务器反射候选X1':x1' 中继候选Y:y 这里称主机候选地址是服务器候选地址的BASE。

14.png

(图)ICE端口

3.2连通检测

L收集了所有的候选地址后,按优先级从高到低排序,通过信令服务器发送SDP offer给R。R收到offer后,收集获选地址,并将自己候选地址放入SDP answer发送给L。此时两端都有了对端的和本端的候选地址。然后配对,生成candidate pair。为了确保candidate pair的有效性,两端都要做连通检测。根据candidate pair,从本地candidate发送STUN请求到远端candidate;接收端返回STUN响应给发送端。如下图。

15.png

(图)ICE基本连通检测

两端都按照各自checklist分别进行检查。

当R收到L的检测时,R发送向L的检测被称为Triggered检测。

3.3 Candidates pair排序

将连通性检查成功的candidate pair按优先级排序加入check list。两端定期遍历这个check list, 发送STUN请求给对端称为Ordinary检测。优先级的计算根据以下原则: 每端给自己的candidate一个优先级数值。本端优先级和远端优先级结合,得到candidate pair的优先级优先级。

公式 priority = (2^24)*(type preference) + (2^8)*(local preference) + (2^0)*(256 - component ID)

16.png

再根据candidate的优先级计算candidate pair的优先级。

priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)

G:controlling candidate 优先级 D:controlled candidate 优先级

3.4提名Candidates

ICE中有两种角色, controlling角色可以选取最终的candidate pair;controlled角色会等待controlling角色选取的candidate pair。 ICE指定一个ICE代理为controlling角色,其他ICE代理为controlled角色。ICE优先检测优先级高的candidate pair。

Controlling角色有两中提名方案:REGULAR提名:当得到至少一对有效的pair的时候,Controlling角色就会选择其中的一个pair作为候选,此次连通检测发送一个带flag的请求,告诉对端这个就是被选中的pair。

17.png

(图)REGULAR提名

AGGRESSIVE提名:Controlling角色会在每个STUN请求之中添加flag标志,最先成功的那个被选为媒体传输通道。

18.png

(图)AGGRESSIVE提名

3.5 ICE示例

下面是例子中,L和R都是full模式ICE代理,采用aggressive提名,传输媒体为RTP。full模式为双方都要进行连通性检查,都要的走一遍流程;lite模式为,full模式ICE一方进行连通性检查,lite一方只需回应response消息。

19.png

(图)ICE举例

便于理解,采用"主机类型-网络类型-序号"的格式表示传输的地址。地址有两个分量,分别是IP和PORT。L,R,STUN,NAT代表不同的主机类型;PUB代表外网,PRV代表内网; L处在内网中,内网地址是10.0.1.1,R处在外网,外网地址是192.0.2.1。L和R都配置了STUN服务,地址是192.0.2.2,端口是3478。L在NAT后面,NAT外网地址是192.0.2.3。序号表示不同的媒体类型,这里只有RTP所以序号为1。 "S="表示STUN消息的发送地址、"D=" 表示STUN消息的接收地址。 "MA=" 表示STUN绑定响应的中mapped address。"USE-CAND" 表示带有"USE-CANDIDATE" STUN消息。

L收集本地候选地址,并发起STUN绑定请求给STUN服务器,L得到 NAT-PUB-1作为服务器反射候选地址。 L计算候选的优先级,主机候选地址type preference为126;服务器反射候选地址type preference为100。local preference为65535。component ID为1 套用公式priority = (2^24)*(type preference) + (2^8)*(local preference) + (2^0)*(256 - component ID) 得主机候选地址的优先级为2130706431,服务器反射候选地址的优先级为1694498815。 L设置主机候选地址的foundation为1,服务器反射候选地址foundation为2。  L将服务器反射候选地址作为default候选地址。对应的offer sdp为

20.png

替换地址后

21.png

因为L和R都采用的是full-mode,这种情况下ICE协议规定发送offer为controlling端,L为controlling端。 L和R生成candidate pair,两端都有2个candidate pair。L会裁减掉包含服务映射候选地址,保留candidate pair为本端$L_PRIV_1、远端$R_PUB_1

22.png

消息9表示R做连通检测,因为R是controlled角色,所以无需设置USE-CANDIDATE。L处于NAT后面,且没有向R发送过请求,所以此次连通检测会失败。

当L收到answer sdp后,开始连通检测(消息10-13)。L采用的是aggressive提名,所以每个请求都会有USE-CANDIDATE。L使用candidate pair为$L_PRIV_1/$R_PUB_1发起的连通检测成功后,L创建一个新的candidate pair,本端为NAT-PUB-1(消息13中得到) 、远端为R-PUB-1(消息10中得到),加入valid list中。这个连通检测中设置了USE-CANDIDA属性,该candidate pair为选中的候选。L的RTP流在valid list中有选中的candidate pair,所以L进入完成状态。

R收到L的STUN绑定请求(消息11)后,R发起消息11对应的Triggered检测,其candidate pair的本端为R-PUB-1、远端为NAT-PUB-1。检测成功后,R创建本端为R-PUB-1、远端为NAT-PUB-1的candidate pair,加入valid list。因为消息11中包含了USE-CANDIDATE,所以这个candidate pair就被选中为这个RTP流的传输通道。R进入完成状态。

4. 总结

本文介绍了NAT、STUN、ICE等基本概念。STUN部分介绍了STUN的消息结构、消息类型和消息属性ICE协议中STUN消息要遵循STUN协议。 ICE部分介绍了ICE代理之间是如何根据各自的网络地址建立连接的步骤有收集候选地址、连通检测、Candidates pair生成与排序、提名Candidates。 详细内容还需查看ICE协议rfc5245以及webrtc的p2p部分的具体实现。


万人群聊的消息分发控速方案

IM即时通讯徐凤年 发表了文章 • 0 个评论 • 114 次浏览 • 2021-01-21 11:04 • 来自相关话题

当前阶段,群聊已经成为主流IM软件的基本功能,不管是亲属群,朋友群亦或是工作群,都是非常常见的场景。随着移动互联网的发展,即时通讯服务被广泛应用到各个行业,客户业务快速发展,传统百人甚至千人上限的群聊已经无法满足很多业务发展需求,所以超大群的业务应运而生。&n... ...查看全部

当前阶段,群聊已经成为主流IM软件的基本功能,不管是亲属群,朋友群亦或是工作群,都是非常常见的场景。随着移动互联网的发展,即时通讯服务被广泛应用到各个行业,客户业务快速发展,传统百人甚至千人上限的群聊已经无法满足很多业务发展需求,所以超大群的业务应运而生。

 

1超大群面临的挑战

我们以一个万人群的模型进行举例:

1、如果群中有人发了消息,那么这条消息需要按照1:9999的比例进行分发投递,如果我们按照常规消息的处理流程,那么消息处理服务压力巨大。

2、消息量大的情况下,服务端向客户端直推消息的处理速度将会成为系统瓶颈,而一旦用户的消息下发队列造成了挤压,会影响到正常的消息分发,也会导致服务缓存使用量激增。

3、在微服务架构中,服务以及存储(DB,缓存)之间的QPS和网络流量也会急剧增高。

4、以群为单位的消息缓存,内存和存储开销较大(消息体的存储被放大了万倍)。

基于这些挑战,我们的服务势必要做一定的优化来应对。

 

2群消息分发模型

整体的群聊服务架构如下图所示:

1.png

   用户在群里发了一条群消息后,消息先到群组服务,然后通过群组服务缓存的群关系,锁定这条消息最终需要分发的目标用户,然后根据一定的策略分发到消息服务上,消息服务再根据用户的在线状态和消息状态来判断这条消息是直推、通知拉取还是转Push,最终投递给目标用户。

 

3超大群消息分发解决方案

3.1分发控速:

第一,首先我们会根据服务器的核数来建立多个群消息分发队列,这些队列我们设置了不同的休眠时间以及不同的消费线程数,这里可以理解为快、中、慢等队列。如下图所示:

2.png

第二,我们根据群成员数量的大小来将所有群映射到相应的队列中,规则是小群映射到快队列中,大群映射到相应的慢队列中。

第三,小群由于人数少,对服务的影响很小,所以服务利用快队列快速的将群消息分发出去,而大群群消息则利用慢队列的相对高延时来起到控速的作用。

3.2 合并分发:

一条群消息发送到IM服务器后,需要从群组服务投递给消息服务,如果每一个群成员都投递一次,并且投递的群消息内容是一致的话,那肯定会造成相应的资源浪费和服务压力。

服务落点计算中我们使用的是一致性哈希,群成员落点相对固定,所以落点一致的群成员我们可以合并成一次请求进行投递,这样就大幅提高了投递效率同时减少了服务的压力。

3.3 超大规模群的处理方案

在实际群聊业务中,还有一种业务场景是超大规模群,这种群的群人数达到了数十万甚至上百万,这种群如果按照上述的分发方案,势必也会造成消息节点的巨大压力。比如我们有一个十万人的群,消息节点五台,消息服务处理消息的上限是一秒钟4000条,那每台消息节点大约会分到2万条群消息,这超出了消息节点的处理能力。

所以为了避免上述问题,我们的超大群(群成员上线超过3000,可以根据服务器数量和服务器配置相应做调整)会用特殊的队列来处理群消息的分发,这个特殊的队列一秒钟往后端消息服务投递的消息数是消息服务处理上限的一半(留相应的能力处理其他消息),如果单台消息服务处理的QPS上限是4000,那群组服务一秒往单台消息服务最多投递2000条。

 

结束语

我们后续也会针对群消息进行引用分发,对于大群里发的消息体比较大的消息,我们给群成员只分发和缓存消息的索引,比如MessageID,等群成员真正拉取群消息时再从将消息组装好给客户端分发下去。这样做会节省分发的流量以及存储的空间。

随着互联网的发展,群组业务的模型和压力也在不停地扩展,后续可能还会遇到更多的挑战,届时我们服务器也会通过更优的处理方式来应对。

 

感兴趣的开发者可以扫码下载融云的 IM 即时通讯 Demo 产品:SealTalk,体验融云的群聊、聊天室等通信能力。

3.png

集成融云 Web 音视频通话踩坑之旅

WebRTC苏道 发表了文章 • 0 个评论 • 293 次浏览 • 2020-12-29 16:44 • 来自相关话题

前言最近有个项目需要使用的融云的 CallLib SDK 实现类似微信的视频通话,所以在项目还未正式启动的时候,我已经偷偷的开始进行集成了,以免到时候不熟一顿加班那真的欲哭无泪了,好消息就是我已经使用过融云家的 IMLib SDK 做过即时通讯的功能,所以整个... ...查看全部

前言

最近有个项目需要使用的融云的 CallLib SDK 实现类似微信的视频通话,所以在项目还未正式启动的时候,我已经偷偷的开始进行集成了,以免到时候不熟一顿加班那真的欲哭无泪了,好消息就是我已经使用过融云家的 IMLib SDK 做过即时通讯的功能,所以整个注册流程和开发者后台的使用已经比较熟了,当然,即时不熟也没关系,跟着他们的文档一步一步来,也能很快的就上手了。
融云官网:https://www.rongcloud.cn/

下面是集成的时候碰到的一些需要注意的问题:

1、Web 站点必须为 localhost 或 https
2、必须成功连接 IM 后, 才可进行 CallLib 通话
3、新版谷歌浏览器(86版本)会报错,我集成的 RTC 版本是 3.2.3
4、音视频通话接通不了

综上问题,我会逐一解答,关于具体的集成可以直接参考:https://docs.rongcloud.cn/v4/views/rtc/call/noui/quick/web.html

还有具体的 demo 也可以参考一下 https://github.com/rongcloud-snippets/web-call-quickstart

Web 站点必须为 localhost 或 https:
这个是融云的使用音视频通话的前置条件,本来在本地调试的时候好好的,可是发布到线上的时候就不能用了,最后提交工单询问融云的技术人员上线是否还需要配置什么,最后排查一圈发现生产环境使用的站点是 http(欲哭无泪。。。),童鞋们引以为戒啊!!

必须成功连接 IM 后, 才可进行 CallLib 通话:
直接看代码:

// appKey 可在融云开发者后台获取
const im = RongIMLib.init({ appkey: '<your-appkey>' })
// 添加事件监听器
im.watch({
  // 连接状态监听
  status(evt) {
    console.log('连接状态码:', evt.status);
  },
  // 消息监听
  message(evt) {
    console.log('收到新消息:', evt.message);
  }
})
// CallLib 初始化
var config = {
    timeout: 20000,
    RongIMLib: RongIMLib,
    RongRTC: RongRTC
};
rongCallLib = RongCallLib.init(config);
//token 可从开发者后台获取 或 Server API
const token = ''
im.connect({ token }).then(user => {
  console.log('链接成功, 链接用户 id 为: ', user.id);
}).catch(error => {
  console.log('链接失败: ', error.code, error.msg);
});

新版谷歌浏览器会报错:
由于浏览器更新,导致 SDK 需要升级,升级到最新版本的 RTC SDK 下载地址:https://cdn.ronghub.com/RongRTC-3.2.6.min.js
需要注意,如果使用的 SDK 2.X 也需要升级到 2.5.10 以上

音视频通话接通不了:
这种情况我总结分析了一下几种情况,如下:

代码传参错误或者书写错误
这个好像没什么说的,只能怪自己不仔细吧!跟着文档来呗

结束通话没有调用挂断方法 hungup
用户主动发起挂断,在 rongCallLib.commandWatch 监听中收到 HungupMessage 消息,可以调用 rongCallLib.hungup 来挂断通话,不然下次呼叫时会出现报错,提示对方正忙

如何识别挂断原因
HungupMessage 消息中 reason 字段及 SummaryMessage 消息中 status 字段都为挂断原因,详情地址参考:https://docs.rongcloud.cn/rtc/calllib/web/code/

因为对音视频的集成也刚开始,还在学习当中。后续随着继续深入,也会同步音视频相关的集成问题,方便复习记录也希望能帮到需要的童鞋!!!


融云 CallLib 集成遇到的问题

IM即时通讯苏道 发表了文章 • 0 个评论 • 278 次浏览 • 2020-12-29 16:41 • 来自相关话题

近期选用融云音视频产品实现类似微信的通话功能, 经过几天的调试, 终于完成了基本功能, 以下总结集成中遇到的问题查看文档首先先查看融云的文档, 介绍不是很详细, 如果不参考 Demo, 集成起来还是比较困难但有一个亮点, 文档内就直接能体验融云 CallLib... ...查看全部

近期选用融云音视频产品实现类似微信的通话功能, 经过几天的调试, 终于完成了基本功能, 以下总结集成中遇到的问题

查看文档

首先先查看融云的文档, 介绍不是很详细, 如果不参考 Demo, 集成起来还是比较困难

但有一个亮点, 文档内就直接能体验融云 CallLib 的效果

融云 CallLib 文档: https://docs.rongcloud.cn/v4/views/rtc/call/noui/intro.html

Demo 参考

Demo 找到两个. 代码都很简单, 没有太多业务代码, 参考起来比较友好

文档中的 Demo: https://github.com/rongcloud-snippets/web-call-quickstart

教程中的 Demo: https://github.com/rongcloud/websdk-demo/tree/master/calllib-v3/

教程类 Demo 包含一个完整的启动教程, 可参考: https://tutorials.rongcloud.cn/tutorial/web-calllib-demo#0

遇到的问题

1、未找到错误码的完整解释

在文档中搜索多遍, 都没有找到错误码的列表. 提工单询问后, 得知只有旧文档中有解释. 新文档还正在添加中

旧文档: https://docs.rongcloud.cn/rtc/calllib/web/code/

2、单对单通话, 一方挂断, 另一方必须也调用挂断方法

设计有些不合理. 应该是考虑兼容多人音视频, 希望单人、多人调用方式保持一致

3、Web 多端登录时, 需要额外处理错误码 8

如果同一个用户在 Web1、Web2 同时登录, 如果用户收到音视频呼叫, Web1 接通后, Web2 会自动挂断, 并抛出一个挂断码 8. 此处逻辑需要额外处理, 给客户一个提示


WebRTC 实现实时音视频技术研究

WebRTC苏道 发表了文章 • 0 个评论 • 300 次浏览 • 2020-12-29 16:39 • 来自相关话题

由于疫情原因很多公司都选择线上办公,让我对实时音视频倍感兴趣,所以决定了解下。首先来画个饼,说下我的熟悉了解的过程。了解下 webRTC 是个什么,能做什么。选一家可以做实时音视频的三方集成实验下,同时也想具体了解小他们是如何实现的。调研是了几家:腾讯,声网和... ...查看全部

由于疫情原因很多公司都选择线上办公,让我对实时音视频倍感兴趣,所以决定了解下。

首先来画个饼,说下我的熟悉了解的过程。

  1. 了解下 webRTC 是个什么,能做什么。

  2. 选一家可以做实时音视频的三方集成实验下,同时也想具体了解小他们是如何实现的。

调研是了几家:腾讯,声网和融云,这三家,最后选中了融云。
首先说下为什么选择了融云:首先作为个人开发者,而且是对这块想了解的用户,融云在我注册后给我了很快的回访,我说明我的需要还有人很细致的介绍了下,体验不错。就那你先试试吧,不过有点不给力的是,集成中遇到些问题,不过提工单还是解决了。虽然过程略微有点磕磕绊绊~~~~

首先按照画饼的步骤说下 WebRTC

WebRTC 简介
WebRTC 是一个由 Google 发起的实时通讯解决方案,其中包含视频音频采集,编解码,数据传输,音视频展示等功能,我们可以通过技术快速地构建出一个音视频通讯应用。 虽然其名为 WebRTC,但是实际上它不光支持 Web 之间的音视频通讯,还支持 Android 以及 IOS 端,此外由于该项目是开源的,我们也可以通过编译 C++代码,从而达到全平台的互通。

WebRTC 架构介绍

咳咳~~ 介绍说的有点生硬,这个查 WebRTC 相关资料室看到的。不过个人看架构图还是研究了下,有点收获的大家也可以细看看,有不明白的可以深入了解下,很有意思的

再说下集成融云音视频的一些问题:

  1. 下载了 Demo,跑步起来,报了个 RongRTC-3.2.3.js:8335 Uncaught (in promise) DOMException: Failed to execute 'setLocalDescription' on 'RTCPeerConnection': Failed to parse SessionDescription. m=video 9 UDP/TLS/RTP/SAVPF 98 99 96 97 100 101 127 Invalid value:

  2. 获取本地资源失败

说说我是如何解决的吧:

先说第二个问题吧。获取失败是因为我开了两个浏览器,有一个浏览器已经占用了资源,在第二个浏览器访问时就报了资源获取失败。好吧~只能这样了,那就不用两个浏览器呗~~~

再说问题一,由于本来就是抱着学习的态度去了解 RTC 一看报了 RTCPeerConnection 很感兴趣,正好可以好好梳理下。说以了解了下 RTCPeerConnection 和 SDP

RTCPeerConnection: 一个 RTCPeerConnection 对象允许用户在两个浏览器之间直接通讯。

SDP 中都有哪些属性,分别是什么含义:

//版本
v=0
//<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
o=- 3089712662142082488 2 IN IP4 127.0.0.1
//会话名
s=-
//会话的起始时间和结束时间,0代表没有限制
t=0 0
//表示音频传输和data channel传输共用一个传输通道传输的媒体,通过id进行区分不同的流
a=group:BUNDLE audio data
//WebRTC Media Stream
a=msid-semantic: WMS
//m=audio说明本会话包含音频,9代表音频使用端口9来传输,但是在webrtc中现在一般不使用,如果设置为0,代表不传输音频
//使用UDP来传输RTP包,并使用TLS加密, SAVPF代表使用srtcp的反馈机制来控制通信过程
//111 103 104 9 0 8 106 105 13 110 112 113 126表示支持的编码,和后面的a=rtpmap对应
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
//表示你要用来接收或者发送音频使用的IP地址, webrtc使用ice传输,不使用这个地址, 关于ICE是什么后面会讲到
c=IN IP4 0.0.0.0
//用来传输rtcp的地址和端口,webrtc中不使用
a=rtcp:9 IN IP4 0.0.0.0
//ice协商过程中的安全验证信息
a=ice-ufrag:ubhd
a=ice-pwd:l82NnsGm5i7pucQRchNdjA6B
//支持trickle,即sdp里面只描述媒体信息, ice候选项的信息另行通知
a=ice-options:trickle
//dtls协商过程中需要的认证信息
a=fingerprint:sha-256 CA:83:D0:0F:3B:27:4C:8F:F4:DB:34:58:AC:A6:5D:36:01:07:9F:2B:1D:95:29:AD:0C:F8:08:68:34:D8:62:A7
a=setup:active
//前面BUNDLE行中用到的媒体标识
a=mid:audio
//指出要在rtp头部中加入音量信息
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
//当前客户端只接受数据,不发送数据,recvonly,sendonly,inactive,sendrecv
a=recvonly
//rtp,rtcp包使用同一个端口来传输
a=rtcp-mux
//下面都是对m=audio这一行的媒体编码补充说明,指出了编码采用的编号,采样率,声道等
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
//对opus编码可选的补充说明,minptime代表最小打包时长是10ms,useinbandfec=1代表使用opus编码内置fec特性
a=fmtp:111 minptime=10;useinbandfec=1
//下面就是对Data Channel的描述,基本和上面的audio描述类似,使用DTLS加密,使用SCTP传输
m=application 9 DTLS/SCTP 5000
c=IN IP4 0.0.0.0
//可以是CT或AS,CT方式是设置整个会议的带宽,AS是设置单个会话的带宽。缺省带宽是千比特每秒
b=AS:30
a=ice-ufrag:ubhd
a=ice-pwd:l82NnsGm5i7pucQRchNdjA6B
a=ice-options:trickle
a=fingerprint:sha-256 CA:83:D0:0F:3B:27:4C:8F:F4:DB:34:58:AC:A6:5D:36:01:07:9F:2B:1D:95:29:AD:0C:F8:08:68:34:D8:62:A7
a=setup:active
//前面BUNDLE行中用到的媒体标识
a=mid:data
//使用端口5000,一个消息的大小是1024比特
a=sctpmap:5000 webrtc-datachannel 1024

好吧了解完这些还是没解决,于是我提了工单,额~让我升级 SDK 说 chrome 升级优化了协议,SDK 有配套优化更新。fine~

问题解决。很开心这次体验,虽然 Demo 写的很简单还借鉴了融云的 Demo ,不过本身就是为了学习 WebRTC 东西。整个历程还是收获匪浅的。

梳理了下融云整体的 SDK 结构大体分了两大概念。

  1. 房间的概念

  2. 资源流的概念

房间是一个抽象概念,目的是加入房间,然后再使用 PeerConnection 建立连接进行通话。所以房间模型中提供了加入,离开获取房间内信息等接口

资源流主要是指媒体流,用到的是 WebRTC 的 navigator.mediaDevices.getUserMedia 来获取视频流,在通过建立的 PeerConnection 连接,对自己和他人的流进行发布和订阅等操作。具体可以看参考文旦中的 PeerConnection 介绍的连接,本人也是通过此处了解的,不过还不是很透彻还只是了解皮毛。

融云提供的接口还是蛮简洁的,用起来很方便。上几块代码大家可以看下如果感兴趣也可以多了解了解。



截图借鉴的融云官方文档的,没有贴自己的代码,因为我的代码里会有些自己的业务逻辑。

本文纯属个人学习总结,为了了解 WebRTC,也集成了下融云的 SDK !不喜勿喷哦 ~~~ ❤

参考文案:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection

https://baike.baidu.com/item/WebRTC/5522744

融云官网:https://www.rongcloud.cn/

集成文档:https://docs.rongcloud.cn/v2/views/rtc/meeting/intro/ability.html


集成融云小程序 SDK 遇到的问题

IM即时通讯木土走召 发表了文章 • 0 个评论 • 311 次浏览 • 2020-12-24 14:33 • 来自相关话题

集成版本最初集成 3.0.5, 最后更新至 3.0.7.1文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/setting/include/mini.html... ...查看全部

集成版本

最初集成 3.0.5, 最后更新至 3.0.7.1

文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/setting/include/mini.html

问题列表

1、链接返回 31012

文档解释 31012 为安全域名错误, 请至开发者后台查看设置的安全域名

但小程序开发, 不应该涉及安全域名问题. 所以只能到融云开发者后台提工单查找解决问题

咨询结果后, 原因为: 融云 3.0.5 SDK 在小程序环境中, 错误的认为当前环境为 Web, 且我自己的 AppKey 确实在融云开发者后台设置了安全域名. 导致有此限制

解决方法有两种:

  • 升级 SDK 至当前最新的 3.0.7.1. 最新 SDK 已修复此问题

  • 去掉我自己融云开发者后台的安全域名

两者选其一即可

2、在模拟器运行没有问题, 真机运行无法链接

通过微信开发者工具 + 真机调试, 发现报错微信合法域名限制

按照融云文档配置合法域名后, 此问题解决

文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/setting/include/mini.html#domain

3、发送图片消息, 移动端无法显示, 但小程序可正常显示

通过文档, 发现是小程序端发送图片消息时, content 内不包含缩略图字段

融云 IMKit 默认是展示缩略图的, 而本小程序项目内的渲染逻辑为显示 remoteUrl, 最终导致了此问题

按照文档添加 content 字段后, 此问题解决

文档: https://docs.rongcloud.cn/v4/views/im/noui/guide/private/msgmanage/msgsend/web.html?plat=mini#ImgMsg


通过融云 WebSDK 简单了解 WebSocket

IM即时通讯苏道 发表了文章 • 0 个评论 • 79 次浏览 • 2020-12-24 14:33 • 来自相关话题

近期使用融云开发聊天页面, 通过抓包, 发现融云 SDK 使用 WebSocket 实现与服务端通讯, 由此简单了解下 WebSocket 的使用需要 WebSocket 的原因:一句话总结: HTTP 协议只能由客户端发起, 服务端响应. WebSocket... ...查看全部

近期使用融云开发聊天页面, 通过抓包, 发现融云 SDK 使用 WebSocket 实现与服务端通讯, 由此简单了解下 WebSocket 的使用

需要 WebSocket 的原因:

一句话总结: HTTP 协议只能由客户端发起, 服务端响应. WebSocket 可服务端主动发起, 也可客户端发起. 能实现的场景更多、更复杂

相关资料如下:

融云开发文档: https://docs.rongcloud.cn/v4/

WebSocket Web API: https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket

WebSocket Node API: socket.iowsuWebSockets (任选其一)

融云抓包

测试地址: https://rongcloud.github.io/websdk-demo/api-test/

通过抓包可知, 融云链接 websocket 时还带了 appkey、token、SDK 版本信息

接下来实现一个简单地 客户端 + 服务端示例

Nodejs 服务端

此处示例使用 ws 模块

创建链接:

const Port = '2928';
const wss = new WebSocket.Server({ port: Port });
wss.on('connection', function (ws, event) {
  const url = event.url; // url 为客户端链接地址, 可拼接参数. 比如融云拼接了 appkey 等信息: ?appid=n19xxxxxx&token=xxxxxxxx
  /* 监听链接关闭 */
  ws.on('close', () => {
    // 此 ws 链接已断开. do something
  });
});

监听客户端消息:

const Port = '2928';
const wss = new WebSocket.Server({ port: Port });
wss.on('connection', function (ws, event) {
  ws.on('message', function (data) {
    console.log('data', data); // 收到客户端发送的数据 do something
  });
});

向客户端发送请求:

const Port = '2928';
const wss = new WebSocket.Server({ port: Port });
wss.on('connection', function (ws, event) {
  ws.send('some data');
});

Web 客户端

具体 API 可参考: WebSocket

建立链接:

const socket = new WebSocket('ws://localhost:2928');
socket.addEventListener('open', function (event) {
  // 链接成功, do something
});

监听服务端消息:

const socket = new WebSocket('ws://localhost:2928');
socket.addEventListener('message', function (event) {
  console.log('收到服务端发送的数据', event.data);
});

向服务端发送请求:

const socket = new WebSocket('ws://localhost:2928');
socket.addEventListener('open', function (event) {
  socket.send('some data'); // 链接后, 调用 send 发送
});


前端音视频之WebRTC初探

WebRTC大兴 发表了文章 • 0 个评论 • 138 次浏览 • 2020-10-22 17:35 • 来自相关话题

今天,我们来一起学习一下 WebRTC,相信你已经对这个前端音视频网红儿有所耳闻了。WebRTC Web Real-Time Communication 网页即时通信WebRTC 于 2011 年 6 月 1 日开源,并在 Google、Mozilla、Ope... ...查看全部

今天,我们来一起学习一下 WebRTC,相信你已经对这个前端音视频网红儿有所耳闻了。

WebRTC Web Real-Time Communication 网页即时通信

WebRTC 于 2011 年 6 月 1 日开源,并在 Google、Mozilla、Opera 等大佬们的支持下被纳入 W3C 推荐标准,它给浏览器和移动应用提供了即时通信的能力。

WebRTC 优势及应用场景

优势

  • 跨平台(Web、Windows、MacOS、Linux、iOS、Android)
  • 实时传输
  • 音视频引擎
  • 免费、免插件、免安装
  • 主流浏览器支持
  • 强大的打洞能力

应用场景

在线教育、在线医疗、音视频会议、即时通讯工具、直播、共享远程桌面、P2P网络加速、游戏(狼人杀、线上KTV)等。

1.png

(有喜欢玩狼人杀的同学吗?有时间可以一起来一局,给我一轮听发言的时间,给你裸点狼坑,一个坑容错。)

WebRTC 整体架构

拉回来,我们看一看 WebRTC 的整体架构,我用不同的颜色标识出了各层级所代表的含义。

2.png

  • Web 应用
  • Web API
  • WebRTC C++ API
  • Session Management 信令管理
  • Transport 传输层
  • Voice Engine 音频引擎
  • Video Engine 视频处理引擎

我们再来看下核心的模块:

Voice Engine 音频引擎

VoIP 软件开发商 Global IP Solutions 提供的 GIPS 引擎可以说是世界上最好的语音引擎,谷歌大佬一举将其收购并开源,也就是 WebRTC 中的 音频引擎。

  • iSAC:WebRTC 音频引擎的默认编解码器,针对 VoIP 和音频流的宽带和超宽带音频编解码器。
  • iLBC:VoIP 音频流的窄带语音编解码器。
  • NetEQ For Voice:针对音频软件实现的语音信号处理元件。NetEQ 算法是自适应抖动控制算法以及语音包丢失隐藏算法,能够有效的处理网络抖动和语音包丢失时对语音质量产生的影响。
  • Acoustic Echo Canceler:AEC,回声消除器。
  • Noise Reduction:NR,噪声抑制。

Video Engine 视频处理引擎

VPx 系列视频编解码器是 Google 大佬收购 ON2 公司后开源的。

  • VP8:视频图像编解码器,WebRTC 视频引擎默认的编解码器。
  • Video Jitter Buffer:视频抖动缓冲器模块。
  • Image Enhancements:图像质量增强模块。

WebRTC 通信原理

媒体协商

媒体协商也就是让双方可以找到共同支持的媒体能力,比如双方都支持的编解码器,这样才能实现彼此之间的音视频通信。

SDP Session Description Protocal

媒体协商所交换的数据就是 SDP,说是协议,其实 SDP 并不是一个真正的协议,它就是一种描述各端“能力”的数据格式。

3.png

上图所示就是 SDP 的一部分,详细内容请参考:SDP: Session Description Protocol

https://tools.ietf.org/html/rfc4566

或者参考卡神的这篇文章:WebRTC:会话描述协议SDP

https://zhuanlan.zhihu.com/p/75492311

网络协商

ICE Interactive Connectivity Establishment 互动式连接建立

想要建立连接,我们要需要拿到双方 IP 和端口的信息,在当下复杂的网络环境下,ICE 统一了各种 NAT 穿越技术(STUN、TURN),可以让客户端成功地穿透远程用户与网络之间可能存在的各类防火墙。

STUN、TURN

STUN:简单 UDP 穿透 NAT,可以使位于 NAT(或多重 NAT) 后的客户端找出自己的公网 IP 地址,以及查出自己位于哪种类型的 NAT 及 NAT 所绑定的 Internet 端口。

我们知道,NAT 主要有以下四个种类:

  • 完全锥型 NAT
  • IP 限制锥型
  • 端口限制锥型
  • 对称型

前三种都可以使用 STUN 穿透,而面对第四种类型,也是大型公司网络中经常采用的对称型 NAT ,这时的路由器只会接受之前连线过的节点所建立的连线。

那么想要处理这种网络情况,我们就需要使用 TURN (中继穿透 NAT) 技术。

TURN 是 STUN 的一个扩展,其主要添加了中继功能。在 STUN 服务器的基础上,再添加几台 TURN 服务器,如果 STUN 分配公网 IP 失败,则可以通过 TURN 服务器请求公网 IP 地址作为中继地址,将媒体数据通过 TURN 服务器进行中转。

信令服务器 Signal Server

拿到了双方的媒体信息(SDP)和网络信息(Candidate)后,我们还需要一台信令服务器作为中间商来转发交换它们。

信令服务器还可以实现一些 IM 功能,比如房间管理,用户进入、退出等。

小结

本文我们了解了 WebRTC 优势及应用场景、WebRTC 的整体架构及主要模块构成以及 WebRTC 的通信原理。这些基础知识和概念是需要我们牢记的,大家要记牢~

参考

  • 《从 0 打造音视频直播系统》 李超
  • 《WebRTC 音视频开发 React+Flutter+Go 实战》 亢少军
  • https://webrtc.github.io/webrtc-org/architecture/
  • https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API
  • https://www.w3.org/TR/webrtc/


本文转自公众号“前端食堂”,作者霍语佳

WebRTC