VoIP(Voice over Internet Protocol)也被称为 IP 电话,通过 IP 协议传输语音通信和多媒体会话,是一种性价比高且体系架构开放的技术方案,可以被应用在相亲、面试、咨询等多种场景,提供优异的通信体验。而 VoIP Push 是 VoIP 应用实现流程上非常重要的一环。关注【融云全球互联网通信云】了解更多
在音视频业务出海过程中,iOS 端 PushKit 与 CallKit 框架配合,提供与手机系统一样的来电体验,直接在锁屏界面显示,并且通话过程中不会被其他应用打断。(由于受苹果审核政策的影响,CallKit 在内地不可用。)
6 月 23 日的融云 RTC · 进阶实战高手课上,融云音视频研发工程师从 VoIP 概念、VoIP Push 在不同平台的实现过程等方面介绍了在 VoIP Push 海外项目中的应用,并重点分享了其在 iOS 端的实践。后台回复【VoIP】获取完整课件
什么是 VoIP?
传统的语音通信使用的是基于电路交换的 PSTN,特点是连续路径采用物理连接,电路接通后,出现在数据终端用户面前的电路就好像专线一样,交换机控制电路不会去查看传输的数据包中的任何内容,为用户提供了一条完全透明的通信通路。
(传统通信采用电路交换技术,图源:Nextiva)
电路交换技术为用户的每一个呼叫建立一个连接技术,而连接一旦建立,就一直被一对用户固定占用,无论他们是否通信,都不能被其他用户共享。当通信较少时,信息传输的实际效率降低,且由于其通信时电路被用户独占,通信成本较高。
VoIP(Voice over Internet Protocol,也称为 IP 电话)是一种通过 Internet 网络协议传送语音通信和多媒体会话的技术。
VoIP 技术采用了分组交换技术作为通信平台。
(VoIP 采用分组交换技术,图源:Nextiva)
分组交换技术采用了报文交换的“存储-转发”方式,但不像报文交换机那样以报文作为单位进行交换,而是把报文截成较短的、统一格式的分组来进行交换和传输。每一个分组进入交换机后,交换机依据分组中的地址信息选择该分组的传输路径,并将其沿着选定的路径传输给下一个交换机或用户终端。
同电路交换机技术相比较,分组交换由于不存在专用链路,其通信通路的利用率很高。可以说,IP 骨干网中的每条线路都在为所有用户提供传输服务。
运营商在架设 PSTN 的终端时有很大的硬件设施上的成本投入,如通信电缆、数据交换设备、各节点设备、施工成本、人工成本、维护成本等。而 VoIP 全是基于 Internet 上的传输,充分利用了用户的宽带资源,性价比更高。加上专门的终端接入设备及相应的平台技术对语音的强大处理功能,VoIP 的通话质量能达到传统的 IP 电话的通话质量。
综上所述,VoIP 与传统电话相比优势在于:
① 能够更加有效地使用网络资源
② 性价比高
③ IP 电话网继承了计算机网络的智能特性,可以灵活地实现各种增值业务的开发
④ 开放的体系结构
VoIP Push 在不同平台的实现
VoIP Push 是 VoIP 应用实现流程上非常重要的一环。而它的实现在不同平台各有特点。
PC 端
PC 端目前与移动的差异是在于 PC 端的 App 一直处于实时在线状态。可以通过融云 IMLib 下发来电进行响应和展示。
(PC 端呼叫流程)
与 PC 端不同,移动端用户可以“杀死”App,就需要一种消息触达的手段。
Android 端
这里就需要用到 FCM 服务了。
FCM (Firebase Cloud Messaging) 是一种基于 Google Play Services 构建的跨平台服务,用于处理服务器应用程序和移动客户端应用之间的消息的发送、路由和排队。
如下图,FCM 充当消息发件人和客户端之间的中介。客户端应用启用 FCM,可在设备上运行。
(FCM 服务流程)
具体到融云 CallLib 呼叫解决方案中,流程如下图所示:
(融云 CallLib 呼叫流程)
FCM 服务收到一个来电的 Push 消息,然后 App 会被系统拉活。App 拉起后会去链接 IM 服务,IM 服务链接成功后,客户端便会收到一条来电的信令。
融云 CallLib 会对来电信令进行处理,然后调用系统的 Telecom 框架创建一个来电,App 需要注册一个 ConnectionService 用来接收系统创建来电的回调。
然后,ConnectionService 会真正创建一个 Connection 对象来处理一个新的来电,在 Connection 中我们可以拉起业务模块提供的呼入页面进行展示。
iOS 端
iOS 端的独特之处在于,苹果引入了支持 VoIP 推送的 PushKit,且必须配合苹果 CallKit 框架使用,否则 iOS 系统将在收到 VoIP 推送后暂时“杀掉”App 进程。
CallKit 提供了统一的语音通话 UI 及与该 UI 交互的 API,让 App 通话可以像 iOS 原生电话来电一样展现全屏来电及接听界面;VoIP App 与系统 Call 有着相同的通话优先级别,而且在通讯录中的拨号记录、Siri 唤起、勿扰模式等都可以得到很好的支持。
下面的对比图可以更清晰地看出其体验优势,左侧是通过普通推送收到的来电提醒,用户想要接听电话需要点击通知并解锁屏幕进入到应用内。
右侧是通过 CallKit 实现来电响应,用户可以在锁屏或非锁屏状态下直接接听或拒绝来电,获得与系统电话一致的体验。
VoIP Push 和普通推送都是基于 APNs 的。
具体流程上,用户服务器决定要向用户发送哪些通知,以及何时发送。
当需要发送通知时,用户服务器生成一个请求,其中包含通知数据和用户设备的唯一标识符。然后将请求转发给 APNs,后者负责将通知传送到用户的设备。收到通知后,用户设备上的操作系统会处理任何用户交互并将通知传递给应用程序。
(VoIP Push 流程)
在以前,VoIP 应用必须与服务器保持持久的网络连接才能接收来电和其他数据。
这意味着需要编写复杂的代码,在应用程序和服务器之间来回发送定期消息,以保持连接处于活动状态,即使在应用程序未使用时也是如此。这种技术会导致设备频繁被唤醒,浪费资源。这也意味着如果用户退出 VoIP 应用,将无法再接收来自服务器的呼叫。
而使用 PushKit 框架——允许应用程序从远程服务器接收推送。每当收到推送时,应用程序就会被唤醒。
PushKit 具有以下特性:
① 仅当 VoIP 推送发生时设备才会被唤醒,节省能源。
② 与标准推送通知不同,VoIP 推送直接进入到 App 内部进行处理。
③ VoIP 推送被认为是高优先级通知,并且会立即发送。
④ VoIP 推送可以包含比标准推送更多的数据内容,用于处理业务逻辑。
⑤ App 在收到 VoIP 推送时未运行,则会自动拉活 App。需要注意的是,我们要合理使用 PushKit,否则 PushKit 会被系统判定为非法激活,导致 PushKit 无法使用。
⑥ 即使 App 在后台运行,App 也有一定时间来处理推送。
与其搭配使用的 CallKit 具有以下特性:
① 允许用户在 VoIP App 中通过链接直接在苹果电话中发起呼叫。
② 系统功能集成,例如勿打模式、静音或振动模式等。
③ 呼叫阻止功能,首先将发送者的应用内号码与接收者的 iPhone 用户阻止列表匹配。允许应用程序插入一个自定义的被阻止号码列表。
④ 识别来电者,从保存的联系人列表中显示他的姓名。此功能在社交网络中特别有用,昵称与通讯录中的名称可以是不同的。
PushKit+CallKit 的实现
首先要在 Apple 开发者中心创建 VoIP 证书,其次要在 XCode 中配置 VoIP 功能生效。
代码中,我们首先要注册并实现 PushKit 相关代码,包括通过 PushRegistry 初始化,注册 VoIP Push Token 并上传到服务端和响应来电等。
CallKit 的架构图
如上 CallKit 的架构图所示,VoIP 应用程序和其他系统服务,比如蓝牙、Siri、电话等都包含 CallKit,可以通过与系统通信互相响应激活。
在实现 CallKit 的应用程序中,需要引入两个 CallKit 核心类。
CXProvider 类和 CXCallController 类。
CXProvider:应用程序将用于让系统知道任何传入/传出呼叫。
CXCallController:应用程序将用于让系统了解任何本地用户操作。
CXProviderConfiguration 提供了一些我们可以自定义的内容,包括自定义名称、视频功能支持、应用图标以及铃声。
接听电话流程
① 当用户接听电话时,系统会向 CXProvider 发送一个 CXAnswerCallAction 实例。
② 应用通过实现 CXProviderDelegate 方法来响应用户的接听行为。
来电呼入流程
① 为了响应来电,应用通过构造一个 CXCallUpdate 并使用 CXProvider 将其发送到 iOS 系统。
② iOS 系统将此作为一个呼入,同步到其他所有系统服务。
一般的音视频项目,通常都需要对 AVAudioSession 进行配置。在什么时机去配置 AVAudioSession 呢?
如上图,这是当用户接听电话后,系统调用 CXProviderDelegate 提供的一个响应接听方法。在该方法的实现中,配置一个 AVAudioSession 并在完成时调用 Action 对象上的 Fulfill 方法。
结束电话流程
① 首先用户在 UI 上点击了通话结束,Callkit 中获取对通话的引用。
② 由于通话即将结束,所以要停止处理通话的音频。
③ 调用 End 方法改变状态,允许其他系统服务对新状态做出反应。
④ 此时,调用 Fullfill 标记为已完成,系统会将其结束掉。