一、背景
在移动互联网流量时代,很多业务场景都有音视频通信的需求,比如IM场景,除了文字交流还需要音视频通话进行实时交互。为了帮助58、赶集、安居客等业务线更好的为用户提供服务,节约沟通成本,提升效率,TEG基于WebRTC提供了一套完整的实时音视频通话解决方案——WRTC。
另外还有一种场景,比如在进行语音通话时,APP中的两个用户可能不是同时在线,导致一端无法向另一端发起实时通话。为了解决这个问题,WRTC还具备语音转IP电话的能力。业务方可以通过后端配置选择是否使用。本文主要以语音通话为切入点,详细介绍语音及语音转IP电话在WRTC中的设计与实践。
二、WebRTC简介
作为音视频开源核心项目之一,WebRTC整个框架的设计非常庞大,很多大型公司都在基于WebRTC进行音视频能力开发,包括阿里云、网易云、七牛云等。TEG也在2016年就开始提供基于WebRTC的实时音视频通话能力。同时WebRTC的架构设计灵活,功能强大,覆盖了大部分移动端多媒体技术。
比如APM(声音处理模块),被很多公司借鉴用于音频的AEC、NS、AGC。JitterBuffer用于视频抗抖动。Android端Camera和Camera2结合使用,用于视频采集,可扩展的软编码框架以及软硬编结合方式处理视频流等。可以说WebRTC的每个模块都值得我们深入研究,学习其中的设计思想和音视频处理技术。
目前网上关于WebRTC资料有很多,限于篇幅,本文也只做个简单介绍,具体细节,感兴趣的同学可以深入研究模块代码,就像Linux鼻祖Linus曾说的至理名言"Read the f*** source code"。
- 音频,音频采集、处理模块
- 视频,视频采集、编解码模块
- ICE打洞中继服务器,STUN/TURN
- 媒体流传输,RTP/RTCP
WRTC功能模块主要分为视频通话、语音通话、语音转IP电话。本文主要介绍语音和语音转IP电话部分。
三、音视频通话架构
音视频通话包括音频通话和视频通话。同时,为了丰富音视频电话在当前网络环境下的应用场景和通话能力,WRTC还需要提供IP电话的解决方案。业界比较流行的IP电话方案是用FreeSwitch作为电话网关后台,该方案支撑的架构如下图:
在上图这种架构下,客户端除了实现WRTC需要的信令协议通信之外,还需要额外增加同FreeSwitch服务之间的SIP协议通信。从SDK的角度来考虑,这对客户端SDK的包体积、接入复杂度、容错率以及版本灵活性等都带来了额外的挑战。所以我们最终决定将这部分实现放在服务端,架构如下图:
架构优化后,在没有增加客户端和服务端交互复杂度的前提下,由服务端对音频流进行转接,通过SIP协议对接到电话网关,实现与对端手机的通信。另外,服务端还可以进行语音录制,为后期业务方的语音监控、通话记录分析等需求增加了便利。
通话流程
首先介绍下WRTC的音视频通话流程,如下图所示,主叫通过Room/Signaling服务和被叫进行信息交互,IM服务器对于音视频通话来说并不是必须的,把它放在WRTC的流程当中是为了让被叫顺利接收主叫的通话请求。在一个完整的通话流程中,主叫首先尝试和被叫建立音视频通话的连接,假如连接超时或者主叫主动发起IP电话时,WRTC服务端会通过运营商拨打被叫电话,从而完成IP电话的流程。
客户端:
1、房间管理。房间是一个抽象概念,目的是在主被叫之间建立一个随时可查询可追溯的信息通道,比如说当主叫发起音视频通话请求时,被叫需要一个标识来确定需要和哪一方进行通话,这个标识就是用房间信息来储存的。音视频通话需要双方先加入房间,然后再使用PeerConnection建立连接进行通话。下面举几个房间管理的例子:
/**
@brief 请求RoomInfo(后台需要进行身份验证,并分配roomId等)
@param completeHandler 回调block
@since v1.0.0
*/
+ (void)requestRoomInfo:(CompleteHandler)completeHandler;
/**
@brief 加入房间
@param roomid 房间的id
@param params 参数字典
@param completeHandler 回调返回
@since v1.1.1
*/
+ (void)joinToRoom:(NSString *)roomId
Parameters:(NSDictionary *)params
Complete:(CompleteHandler)completeHandler;
/**
@brief 通知此时处于忙状态
@param roomId 第三方呼叫发来的roomId
@since v1.0.0
*/
+ (void)notifyBusy:(NSString *)roomId;
2、信令管理。WRTC采用的是Websocket作为信令服务器,进行媒体协商,发送SDP会话描述协议和Candidate候选信息等。这里主要涉及SDP会话描述协议(offer/answer)和Candidate信息交换。为了提升编码性能,WRTC视频编解码采用H264,音频编解码综合考虑性能和带宽,采用的是OPUS。Candidate交换的是打洞候选IP地址和端口号,用于p2p连接和中继。
下面是一对一音频通话,主叫方发送的SDP(offer)。
offer
...
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=sendrecv
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:126 telephone-event/8000
...
主叫发送的sdp信息通过信令服务器发送到被叫,被叫收到该offer后,会根据该offer回一个answer信息。至此,主叫和被叫完成了媒体信息的协议协商。
3、状态管理。通过信令服务器进行双方通话状态的管理,状态包括busy、refuse、cancel等。状态管理一方面可以防止双通话,另一方面也能根据通话状态进行用户行为统计分析,然后进行迭代优化,为各个业务线提供更好、更稳定的音视频通话服务。
4、音频模块、视频模块、ICE打洞服务、媒体流传输等在上文做了简单介绍,其实这里的每一个模块,都值得我们去深入研究学习,比如我们也在尝试借鉴其中的音频处理模块用于直播服务。篇幅所限,本文不做过多描述。
服务端:
后台服务管理整个WRTC音视频通话的连接建立、信息交换、房间信息等。
- 房间服务,对加入房间的主叫Caller和被叫Callee的行为进行管理,比如加入房间、退出房间等等(上文已经在客户端部分做过介绍,此处不再重复)。
- 信令服务,控制双端用户的媒体协商、Candidate交换等。
- ICE打洞服务,WRTC音视频通话方式分为p2p和中继两种方式。ICE包括STUN和TURN服务,用于打洞,STUN可以进行NAT类型检测,并获取NAT背后的外网IP地址和端口号。其中NAT类型主要分为Full Cone NAT、RestrictedCone NAT、Port Restricted Cone NAT、Symmetric NAT四种。对于前三种都可以建立p2p直连,对于Symmetric NAT(对称性NAT),因为每次连接端口都是变化的,所以通过STUN获取的端口号是无效的。此时WRTC会改走中继模式,来保证双端的正常通话。
考虑到一旦建立p2p连接,后续服务端不能控制干预媒体流,后端服务后续也计划进行系统升级,统一采用中继的方式进行音视频通话,来保证音视频通话的可靠性和可控性。
运营商(网关代理):
后端服务经过判断需要向被叫拨打IP电话时,协议转换服务会将接到的offer转换为SIP的带有协商信息的invite协议,发送给运营商,然后运营商会回复给100trying,表明正在处理SIP信令。
当被叫手机振铃时,运营商回复180或183协议给协议转换服务,并带有第一次协商的结果,协议转换服务将其转化为answer回复给客户端,完成第一次媒体通信即客户端听到彩铃。
当被叫手机接听时,运营商会发送200给协议转换服务,并带有第二次协商的结果,协议转换服务将其转化为answer回复给客户端,完成第二次媒体通信即被叫和主叫开始通话。
如下图所示:
IP电话二次拨号:
对于IP电话,WRTC也具备对分机二次拨号的能力。比如被叫方是座机,可以通过二次拨号和指定用户进行语音通话。
电话拨号,就必须要提到双音多频信号DTMF。DTMF是电话系统中电话机与交换机之间的一种用户信令,用于电话拨号。通过研究WebRTC底层代码,其实它是支持DTMF拨号能力的,只是没有对外部暴露。
我们需要修改PeerConnection,增加insertDtmf功能。拨打分机号时需要传入本地AudioTrack ID,默认值为ARDAMSa0。分机号码ext_number取值范围0~15,对应event为0-9,*, #, A-D。拨号音duration SDK设置的为1000ms。
下面是insertDtmf部分代码:
bool PeerConnection::insertDtmf(const std::string& audio_track_id,const int ext_number,const int duration){
//判断是否支持发送DTMF信号
bool canInsertDtmf = session_->CanInsertDtmf(audio_track_id);
if (canInsertDtmf) {
//WebRTCSession对象发送DTMF
isInsert = session_->InsertDtmf(audio_track_id,ext_number,duration);
}
return isInsert;
}
下面具体说说音视频开发WebRTC方面需要学习哪些内容,以及推荐的资源:
关于选取资源,我的建议是:资源不在多,而贵在精。不要总是屯一大堆资源而却一直放在那里吃灰
四、总结
本文主要介绍了WRTC的实现方案、流程以及IP电话的原理。其中涉及到的细节还有很多,这里就不再赘述。目前WRTC已经能提供了稳定的音视频通话输出能力,后续我们将继续在WRTC方案的基础之上,结合TEG的短视频SDK,从采集端、通话端进行持续优化,采集端丰富音视频处理细节,通话端增加多人通话,最终形成一对一以及多对多的音频、视频、IP电话混合对话的能力。