经过了一整年的开发测试,终于将whatsapp 语音通话完成,期间主要参考webrtc的源码来实现.下面简要说一下大致的步骤
XMPP 协商
发起或者接受语音通话第一步是发起XMPP 协商,这个协商过程非常重要。下面是协商一个包
<call to='xxx@s.whatsapp.net' id='xxxx'>
<offer call-creator='xxx.0:0@s.whatsapp.net' call-id='xxx'
device_class='2015'>
<audio rate='8000' enc='opus' />
<audio rate='16000' enc='opus' />
<net medium='3' />
<capability ver='1'>ac</capability>
<enc v='2' type='pkmsg'>xxxxxxxx</enc>
<encopt keygen='2' />
</offer>
</call>
- 语音编码参数 opus 编码, 采样率 16000/8000
- pkmsg 这个是加密字段,用来协议后续发送语音数据的秘钥
- call-id 同一个通话将使用相同的callid, 类似一个房间号一样,同一个callid的用户将可以听到各自的声音。
Stun UDP 内网穿透
NAT为设备提供内网IP地址,以便在专用本地网络中使用,但是这个地址不能在外部使用。没有外部的ip地址, 双方就无法直接进行通信。为了解决这个问题,就需要Stun 技术,也就是传说中的UDP 打洞。
Turn 数据中转
UDP 打洞并不能一定成功,所以当点对点失败的时候,为了能正常进行语音通话,需要有一个保底策略,那就是在turn服务器中转数据流。
TURN服务器具有公共地址,因此即使端点位于防火墙或代理之后,也可以与其他端点进行通信。TURN服务器虽然只有这么一个简单的任务 —— 中继流, 但与STUN服务器不同,它们本身就消耗了大量带宽。换句话说,TURN服务器需要更强大。
RTP 数据包发送
依赖上面的STUN/TURN 服务器的协商结果, 如果STUN 成功,则直接将加密的RTP 数据发送给对方,否则将加密的RTP数据发给TURN 服务器。下面是RTP 数据格式
由于whatsapp的安全性,所有的RTP 数据都是被加密的, 加密使用的秘钥来源第一步XMPP 的秘钥协商。需要发送的音频数据需要使用 XMPP 协商的编码方式,否则可能不能播放。