iOS Call Kit for VOIP

作者: MasonFu | 来源:发表于2016-10-17 15:12 被阅读9230次

最近苹果 iOS 10 新发布了一个新的框架Call Kit,使第三方VOIP类型语音通话类APP有了更好的展现方式和用户体验的提升,想深入学习一下。基于Apple Audio Unit及开源GCDAsyncSocket做了一个类似’VOIP’的语音通话Demo APP,实现了位于同一局域网下的两台苹果手机之间基础的语音通话功能(拔出、接听、拒接、挂断、双向对讲),然后翻看了下苹果官网SpeakerBox Demo及国外的少部分Call Kit相关技术资料,将Call Kit新特性加入到了这个通话Demo中,下面大概讲解一下实现思路及要点,示例图片基本都来自Apple的WWDC16的Call Kit Session里。

Call Kit 能做什么

在苹果官方WWDC16 Enhancing VoIP Apps with CallKit(Session 230)中,苹果的工程师为我们展示了集成Call Kit后的VOIP 通话APP的效果,例如在iPhone锁屏状态下APP来电时,通过Call Kit可以像iOS原生电话来电一样展现全屏的来电及接听界面,VOIP APP与系统Call有着相同的通话优先级别,而且在通讯录中的拔号记录,Siri唤起,勿扰模式等都有着很好的支持。

incoming call.png

下面这张图简略的描述了原生APP、第三方APP在Call Kit框架下的关系,Call Kit在系统中提供了一种独有的服务,在需要的时刻,原生或者第三方APP通过Call Kit提供的API向系统请求诸如来电、拔出等展现服务,由Call Service统一安排调度这些请求以达成统一的交互响应。

callkit architecture.png

如何使用Call Kit

简单来说,Call Kit就是提供了统一的壳(语音通话UI)及与该壳交互的API而已,实际的通话链路监听、搭建和管理还是APP原有的实现思路,下面以通话的来电、拔出等几个场景描述下APP与Call Kit的交互流程

Prepare

  1. 创建CXProvider,指定Call Kit展现UI中的APP名称和图标,通话数量,来电铃声等Configuration
  2. 实现CXProviderDelegate协议,以接收来自Call Service发来的更新状态,例如用户点击了接听或者挂断等动作,决定网络链路的处理动作;还有AudioSession的激活状态,决定Audio播放录制的启动关闭时机
  3. 创建CXCallController,使APP可以发送更新状态给Call Service,例如用户拔出电话或者对方挂断电话等状态
  4. 如果需要锁屏界面以及APP未启动状态下显示来电界面,要搭建PushKit通路,与APNS差不多,只是APP端处理方式有些不同。文章末尾有一些资料,这里不详细展开了。
prepare callkit.png

Incoming Call

  1. APP前台时收到来自网络Server端的连接请求,或者APP后台时收到来自网络Push Server的PushEvent
  2. APP收到连接请求或者Push消息后,创建CXCallUpdate对象,指定此次来电的号码等属性
  3. 将上面创建的CXCallUpdate对象通过CXProvider的reportNewIncomingCallWithUUID方法报告给iOS系统
  4. Call Service收到新的来电请求后根据当前的状态,展现原生来电UI
incoming call architecture 1.png

Answer Call

  1. 用户点击来电界面上的接听按钮
  2. Call Service通过CXProvider的Delegate协议performAnswerCallAction方法告知APP
  3. APP将接听命令通过网络传给对端,开始实际音频数据传输,进行通话
incoming call architecture 2.png

End Call

  1. 用户点击APP UI中的挂断按钮
  2. 创建CXEndCallAction对象,指定此次通话的UUID属性
  3. 创建CXTransaction,将刚刚的Action指定给它
  4. 通过CXCallController,调用requestTransaction将这个挂断事件通知给Call Service
  5. Call Service通过当前通话状态,通过CXProvider将挂断动作通知回给APP
  6. APP在CXProviderDelegate中的performEndCallAction中结束此次通话的网络链路,停止音频录制播放Loop
incoming call architecture 4.png

Outgoing Call

  1. 用户通过APP UI、通话录、Siri发起一个拔出请求
  2. 创建CXHandle指定拨出的电话号码,创建CXStartCallAction并将CXHandle指定给它
  3. 创建CXTransaction,将刚刚的Action指定给它
  4. 通过CXCallController,调用requestTransaction将这个拔出事件通知给Call Service
  5. Call Service收到新的拨出请求后根据当前的状态,通过CXProvider将拨出动作通知回APP,
  6. APP在CXProviderDelegate中的performStartCallAction中开启此次呼叫的网络链路,通过CXProvider的reportOutgoingCallWithUUID设置呼叫与接通时间,根据链路建立的结果来展现原生呼叫UI的状态(呼叫成功或者失败)
outgoing call.png

Demo APP (MyCall)

正如前面说的,我实现的这个Demo主要是为了实践Call Kit这个新Framework的功能,因此只具备最简单的点对点通话功能,没有很完善的网络通信协议、APP模块结构划分、通话低时延、路由切换、容错、PushKit通知等处理

Demo结构图

MyCall architecture.png

APP中总共封装有四个socket实例对象用来网络通信,其中两个负责监听来电的Server Player和Server Recorder,两个负责呼出的Client Player和Client Recorder,实际通话中只有两路socket处于收发状态。这四个链路对象统一由CallManager对象调度管理。AudioController基于Audio Unit封装,提供音频流的播放和采集功能。CallControoler封装了CallKit的CXCallController对象,ProviderDelegate封装了CXProvider对象,这两个对象负责与Call Service交互。UI比较简单,就是几个通话控制的按钮,拨号地址,通话状态显示这几个控件。

一些说明

  1. Call Kit开关,在Define中我加入了Call Kit开关,不使用Call Kit这个Demo的基本功能也可以正常运行,方便参照对比。
  2. 通信协议,其实基本上没有什么协议,APP启动后就开始监听Socket连接,没有身份注册验证等,有连接就接受开始准备收发音频PCM裸数据。只是为了实现一端拨出,对端选择接听或者拒接这一流程,增加了一个Accept标志位。
  3. 通话标识,由于此demo并不是真正的VOIP,不存在Server注册及身份号码标识,我以局域网的IP地址及固定端口号为"Phone Number",进行Socket连接。

Tips

  1. 开发者通过Call Kit与Service进行交互时需要形成闭环,例如你向Call Service提交了一个StartCallAction请求后,在某些条件下(例如对方挂断或者拒接)必须发送EndCallAction请求,来告诉Call Service此次通话已经结束,否则原生通话UI会显示呼叫失败
  2. 在CXProviderDelegate协议中收到Action并且处理完自己的逻辑后,要调用fulfill或者fail告诉Call Service用户引发的这个Action是成功还是失败
  3. Audio播放录制loop的启动一定要放在CXProviderDelegate的didActivateAudioSession中,而不是performStartCall或者performAnswerCall中,大概是因为Call Service本身也要播放铃声的关系
  4. 在Call Kit使用中,主要面对的就是CXProvider与CXCallController这两个类,尽量以两个层面看待问题,一个是UI层,用户通过APP UI或者APP自主发起的电话状态更新,基本都是通过CXCallController;另一个是Model层,即实际数据传输链接的搭建,甚至Audio Loop,都是在CXProvider的Delegate里完成。Call Kit的原生UI该怎样展现,取决于APP内网络链路的处理方式和结果

demo代码地址
download

参考资料
XAMARIN Introduction to CallKit
WWDC16 Session 230 (Enhancing VoIP Apps with CallKit)
Apple Call Kit Speakerbox Demo
PushKit Practice

相关文章

  • iOS Call Kit for VOIP

    最近苹果 iOS 10 新发布了一个新的框架Call Kit,使第三方VOIP类型语音通话类APP有了更好的展现方...

  • iOS - CallKit来电显示功能使用

    前言iOS10.0版本后,苹果公司推出callkit框架来支持VoIP功能.这里我们不去对VoIP所产生的call...

  • iOS CallKit初体验

    概述 该CallKit框架提供VoIP功能,以及呼叫限制和识别的编程访问。 VoIP功能 应用程序可以使用Call...

  • iOS VOIP

    1、IOS允许App的一个Socket在App切换到后台后仍然保持连接. 这样,当有通话请求的时候,App能及时处...

  • 收藏博客

    iOS: iOS VOIP后台处理 python: tornado 翻译 tornado 框架 数据库: 数据库基...

  • 生成终端、服务器证书

    生成APNS或者VOIP证书后 生产环境 ios推送证书更换

  • iOS PushKit的使用

      由于iOS 10禁止了VoIP类应用常驻后台的权限,导致Xcode 8 打包出来的VoIP类应用后台长连接失效...

  • iOS CallKit使用微信更新6.6.0版本语音好像系统打电

    iOS10以上:CallKit的简单应用提供VoIP功能 其实QQ早已经实现了CallKit的VoIP这一个功能,...

  • VOIP

    最近在做IOS 版 的VOIP 功能 NSArray * array = [[UIApplication shar...

  • Text Kit 总结

    1.Text Kit 是什么? 在iOS7中,苹果引入了Text Kit--Text Kit是一个快速而又现代化的...

网友评论

  • smkoc:麻烦问下 你这个ZeroPush 是什么 类 ?
  • 0b3eb2336980:请教一下,现在app里设置了supportedHandleTypes为CXHandleTypePhoneNumber。从联系人详情长按呼叫,可以出来我的app,点击进入app,发起呼叫,一系列流程都是正确的。但这次发起的呼叫自动生成的呼叫记录,点击没反应,不能唤起app。而我试了钉钉和触宝电话,都是可以的。不知道是什么原因呢。
    0b3eb2336980:补充一下,不是生成呼叫记录,是自动增加了一项“社交资料”,名字就是APP名字
  • 3accedc77df3:请问,如何和App的音视频通话 结合到一起啊
  • 闭家锁:请问 怎么实现点击系统自带的通话记录(这个通话记录是我的voip app产生的,显示音频类型的),进入到我的app发起通话啊,我现在点击通话记录可以跳转到我的app,但是进入之后就没有操作了,怎么在我的app中获取到被点击的通话记录的号码,然后发起通话 啊?
    闭家锁:明白了,多谢!
    MasonFu:@闭家锁 这里有你需要的答案。两个地方都可以拿到,而且都应该实现。https://stackoverflow.com/questions/39789495/callkit-find-number-used-to-start-app-from-native-phone-app
    闭家锁:现在的问题就是没法拿到被点击的通信录的号码,现在点击之后通过这个- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray * _Nullable))restorationHandler方法可以拦截到跳转事件,但是这个方法中的userActivity里面没有任何信息,拿不到被点击通话记录的任何信息,拿不到号码没办法呼出去电话
  • 5859f6ff880f:大神,当主叫不用系统UI而是用我APP UI去呼叫时,怎么通知系统让这条记录显示在系统的通话记录中,就像QQ语音那样的
  • coder_Wg:大神,录音和播放的是PCM流吗?
  • c47efd2dc5c8:请问你的demo怎么测试,输入ip也打不了电话,请大神指导
    coder_Wg:大神,请问你这是TCP连接吗?如果要改成UDP连接该怎么改额?小白谢过了
    c47efd2dc5c8:@MasonFu Socket连接上了,是执行request transaction 报错,方便加下qq联系吗,我的402624505
    MasonFu:@疯丫头666 debug跟踪下socket连接状态。大部分是局域网环境问题。
  • Damon4Zhou:大神,集成callkit后,来电界面的短信图标,有办法进入自己的app吗??
  • JornyQi:请问你们是怎么后台长时间驻留的?
    MasonFu:@弓林 苹果有VOIP标示允许后台运行,在工程设置中,也可用pushkit唤醒再注册链接
  • 一轮圆月:先mark
  • 有腹肌的小胖子:请问你的Demo怎么做测试呢
    zeigler:我这输入可以后也打不了电话啊?求大神指导
    有腹肌的小胖子:@MasonFu 测试了 刚开始测试的时候网不通 :disappointed_relieved:
    MasonFu:@有腹肌的小胖子 把这个DEMO装在两个手机上,互相可以打电话

本文标题:iOS Call Kit for VOIP

本文链接:https://www.haomeiwen.com/subject/pfiayttx.html