简介

EaseCallKit是一套基于声网音视频服务,使用环信IM作为信令通道的开源音视频UI库,UI库提供了单人语音通话、视频通话,以及多人会议的功能接口。

使用EaseCallKit库可以快速实现常用的音视频场景,通过信令的交互确认,保证用户多端登录时,收到呼叫同时振铃,一端处理后,其他端自动停止

EaseCallKit库在github上的地址:https://github.com/easemob/easecallkitui-ios

EaseCallKit在通话过程中,使用环信ID加入频道,方便音视频视图中显示用户名。如果用户不使用EaseCallKit而直接调用声网API,也可以直接使用数字uid加入频道。

注:本UI库只和移动端3.8.0以上版本Demo互通。3.8.1的UI库使用声网数字uid加入频道,而3.8.0使用字符串加入频道,3.8.1版本不与3.8.0互通

跑通Demo

EaseCallKit集成在环信提供的开源IMDemo中,你可以通过进入环信IM Demo下载页面,选择iOS端Demo下载,或直接进入github开源网站https://github.com/easemob/chat-ios下载。

  • 安装SDK与UI库

Demo源码中不包含SDK与UI库,你可以通过直接引入,或者使用cocoapods安装的方式引入SDK与UI库。

如果当前系统上没有安装cocoapods,需要用户自己安装,安装过程参考 Getting Started with CocoaPods 安装说明

使用cocoapods安装需要进入podfile所在目录,然后在终端执行如下命令

pod install
  • 运行

安装完成SDK与UI库后,在XCode打开工作空间EaseIM.xcworkspace,连接手机,然后就可以运行了。

快速集成

以下展示如何快速集成EaseCallKit进行音视频通话的开发

在集成该库前,你需要满足以下条件:

使用 EaseCallKit UI库进行一次完整的音视频通话的流程如下:

  1. 用户调用 EaseCallKit UI库初始化接口(接口调用)
  2. 主叫方调用发起通话邀请接口,自动进入通话页面(接口调用)
  3. 被叫方自动弹出通话请求页面,在UI界面选择接听,进入通话(UI操作)
  4. 结束通话时,点击UI界面挂断按钮(UI操作)

具体流程如下

导入UI库

EaseCallKit UI库依赖于HyphenateChat、AgoraRtcEngine_iOS、Masonry和SDWebImage库,导入UI库时需要同步导入工程,依赖库可通过cocoapods导入。

EaseCallKit是动态库,在podfile中必须加入use_frameworks!

导入EaseCallKit库有两种方式,分别是手动导入和使用cocoapods导入

使用cocoapods导入EaseCallKit

  • 在 Terminal 里进入项目根目录,并运行 pod init 命令。项目文件夹下会生成一个 Podfile 文本文件。
  • 打开 Podfile 文件,修改文件为如下内容。注意将 AppName 替换为你的 Target 名称
use_frameworks!
target 'AppName' do
    pod 'HyphenateChat'
    pod 'Masonry'
    pod 'AgoraRtcEngine_iOS'
    pod 'SDWebImage'
    pod 'EaseCallKit', '~> version'
end
  • 在 Terminal 内运行 pod update 命令更新本地库版本。
  • 运行 pod install 命令安装 EaseCallKit UI库。成功安装后,Terminal 中会显示 Pod installation complete!,此时项目文件夹下会生成一个 xcworkspace 文件。
  • 打开新生成的 xcworkspace 文件,连接手机,运行

手动导入EaseCallKit

  • 将在跑通Demo阶段下载的EaseCallKit.framework拷贝到项目工程目录下
  • 打开XCode工程设置/Genaral菜单下,将EaseCallKit.framework拖拽到工程下,在Frameworks,libraries,and Embedded Content中设置EaseCallKit.framework为Embed & Sign

添加权限

应用需要音频设备及摄像头权限,在 info.plist 文件中,点击 + 图标,添加如下信息

Key Type Value
Privacy - Microphone Usage Description String 描述信息,如“环信需要使用您的麦克风”
Privacy - Camera Usage Description String 描述信息,如“环信需要使用您的摄像头”

如果希望在后台运行,还需要添加后台运行音视频权限,在info.plist文件中,点击 + 图标,添加Required background modes ,Type为Array,在Array下添加元素App plays audio or streams audio/video using AirPlay

集成上传AppStore

由于 iOS 编译的特殊性,为了方便开发者使用,我们将x86_64,arm64 两个平台都合并到了一起,所以使用动态库上传 appstore 时需要将x86_64 平台删除后,才能正常提交审核。

删除x86_64方式如下:在终端进入EaseCallKit.FrameWork,使用以下命令

lipo EaseCallKit -remove x86_64 -output EaseCallKit

由于删除后会覆盖EaseCallKit,需要先将EaseCallKit备份

初始化

在环信IM SDK初始化完成后,同时初始化EaseCallKit,初始化的同时开启回调监听,设置常用配置项。代码如下:

EaseCallConfig* config = [[EaseCallConfig alloc] init];
EaseCallUser* usr = [[EaseCallUser alloc] init];
usr.nickName = @"自定义昵称";
usr.headImage = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"headImage" ofType:@"png"]];
config.users = @{@"环信ID":usr};
config.agoraAppId=@"声网AppID";
[[EaseCallManager sharedManager] initWithConfig:config delegate:self];

可设置的配置项包括以下内容:

@interface EaseCallConfig : NSObject
// 默认头像
@property (nonatomic)  NSURL*  defaultHeadImage;
// 呼叫超时时间,单位秒
@property (nonatomic) UInt32 callTimeOut;
// 用户信息字典,key为环信ID,value为EaseCallUser
@property (nonatomic) NSMutableDictionary* users;
// 振铃文件
@property (nonatomic) NSURL* ringFileUrl;
// 声网appId
@property (nonatomic) NSString* agoraAppId;
// 声网Token验证开关,默认不开启
@property (nonatomic) BOOL enableRTCTokenValidate
@end

发起通话邀请

初始化完成后,可以开始发起音视频通话

发起单人音视频通话

单人通话分为语音通话与视频通话,发起过程如下:

// remoteUser为邀请对象的环信ID
// type为EaseCallType1v1Audio表示语音通话,EaseCallType1v1Video表示视频通话
// ext为通话扩展信息,字典
[[EaseCallManager sharedManager] startSingleCallWithUId:remoteUser type:aType ext:nil completion:^(NSString * callId, EaseCallError * aError) {
    
}];

发起多人音视频通话

客户需要自己完成邀请对象的选择,可以从群组成员列表或者好友列表中选择,具体实现可参考demo中的ConfInviteUsersViewController。确定邀请对象后,发起过程如下:

// aInviteUsers为邀请对象的环信ID数组
// ext可设置扩展信息,如果从群组发起,可通过ext设置群组ID,其他用户也可以从该群组邀请群成员
[[EaseCallManager sharedManager] startInviteUsers:aInviteUsers ext:@{@"groupId":aConversationId} completion:^(NSString * callId, EaseCallError * aError) {
    
}];

发起通话后的UI界面如下:

收到邀请

主叫方调用邀请接口后,如果被叫方在线,且并未处于通话过程中,将弹起通话页面,被叫用户可以在UI界面选择接听或者拒绝。通话页面如下

被叫振铃的同时,会触发以下回调

- (void)callDidReceive:(EaseCallType)aType inviter:(NSString*_Nonnull)user ext:(NSDictionary*)aExt
{
    
}

多人通话中间发起邀请

多人通话中,用户可以再次向其他用户发起邀请。用户点击多人通话界面右上角的邀请按钮,会触发以下回调

// 多人音视频邀请按钮的回调,vc为当前视图控制器,users为会议中已存在的用户,aExt为通话扩展信息
- (void)multiCallDidInvitingWithCurVC:(UIViewController*_Nonnull)vc excludeUsers:(NSArray<NSString*> *_Nullable)users ext:(NSDictionary *)aExt
{
    // 发起通话时在扩展信息里添加groupId,可以实现只在群组里邀请用户
    NSString* groupId = nil;
    if(aExt) {
        groupId = [aExt objectForKey:@"groupId"];
    }
    
    ConfInviteUsersViewController * confVC = nil;
    if([groupId length] == 0) {
        confVC = [[ConfInviteUsersViewController alloc] initWithType:ConfInviteTypeUser isCreate:NO excludeUsers:users groupOrChatroomId:nil];
    }else{
        confVC = [[ConfInviteUsersViewController alloc] initWithType:ConfInviteTypeGroup isCreate:NO excludeUsers:users groupOrChatroomId:groupId];
    }
    [confVC setDoneCompletion:^(NSArray *aInviteUsers) {
        [[EaseCallManager sharedManager] startInviteUsers:aInviteUsers ext:aExt completion:nil];
    }];
    confVC.modalPresentationStyle = UIModalPresentationPopover;
    [vc presentViewController:confVC animated:NO completion:nil];
}

会议邀请界面的实现,可以参考demo中的ConfInviteUsersViewController实现

加入频道成功回调

3.8.1新增接口,在自己加入会议后,会收到以下回调

- (void)callDidJoinChannel:(NSString*_Nonnull)aChannelName uid:(NSUInteger)aUid
{
    //此时,可以获取当前频道中已有用户的声网id与环信id的映射表,并将映射表设置到UI库,同时也可以更新用户的头像和昵称
    //[self _fetchUserMapsFromServer:aChannelName];
    [[EaseCallManager sharedManager] setUsers:users channelName:channelName];
}

对方加入频道成功回调

3.8.1新增接口,在对方加入会议后,会收到回调

-(void)remoteUserDidJoinChannel:( NSString*_Nonnull)aChannelName uid:(NSInteger)aUid username:(NSString*_Nullable)aUserName
{
    //此时,可以获取当前频道中已有用户的声网id与环信id的映射表,并将映射表设置到UI库,同时也可以更新用户的头像和昵称
    //[self _fetchUserMapsFromServer:aChannelName];
    [[EaseCallManager sharedManager] setUsers:users channelName:channelName];
}

通话结束

单人通话中,对方挂断,本地会自动挂断结束,多人通话中需要主动挂断才能结束通话。通话结束后,会触发以下回调

// aChannelName为通话使用的声网频道名称,用户可以根据频道名称,到声网console的水晶球查询通话质量
// aReason为通话结束原因,aTm为通话时长,单位秒,aCallType为通话类型
- (void)callDidEnd:(NSString*)aChannelName reason:(EaseCallEndReason)aReason time:(int)aTm type:(EaseCallType)aCallType
{
    NSString* msg = @"";
    switch (aReason) {
        case EaseCallEndReasonHandleOnOtherDevice:
            msg = @"已在其他端处理";
            break;
        case EaseCallEndReasonBusy:
            msg = @"对方忙";
            break;
        case EaseCallEndReasonRefuse:
            msg = @"拒绝通话";
            break;
        case EaseCallEndReasonCancel:
            msg = @"您已取消呼叫";
            break;
        case EaseCallEndReasonRemoteCancel:
            msg = @"通话已取消";
            break;
        case EaseCallEndReasonRemoteNoResponse:
            msg = @"对方超时未响应";
            break;
        case EaseCallEndReasonNoResponse:
            msg = @"未接听";
            break;
        case EaseCallEndReasonHangup:
            msg = [NSString stringWithFormat:@"通话已结束,通话时长:%d秒",aTm];
            break;
        default:
            break;
    }
    if([msg length] > 0)
       [self showHint:msg];
}

获取声网token

用户加入音视频会话时,如果需要进行声网token鉴权,需要先开启token验证开关,开启过程如下

EaseCallUser* callUser = [[EaseCallUser alloc] init];
config.enableRTCTokenValidate = YES;// 开启RTC Token验证,默认不开启
[[EaseCallManager sharedManager] initWithConfig:config delegate:self];

获取token的过程由用户自己完成,开启后在通话时,会收到以下回调,用户需要在回调中,实现从用户自己的AppServer中获取token(AppServer的实现参见生成声网Token),然后调用setRTCToken:channelName:接口。

- (void)callDidRequestRTCTokenForAppId:(NSString * _Nonnull)aAppId
                           channelName:(NSString * _Nonnull)aChannelName
                               account:(NSString * _Nonnull)aUserAccount
{
  [[EaseCallManager sharedManager] setRTCToken:@"自己的RTC Token" channelName:aChannelName];
}

3.8.1后使用数字uid加入声网频道,该回调接口有所变更,新接口为

- (void)callDidRequestRTCTokenForAppId:(NSString *)aAppId channelName:(NSString *)aChannelName account:(NSString *)aUserAccount uid:(NSInteger)aAgoraUid
{
    [[EaseCallManager sharedManager] setRTCToken:@"自己的RTC Token" channelName:aChannelName uid:自己的声网uid];
}

通话异常回调

通话过程中如果发生异常,会触发以下回调

// 异常回调
// aError为异常信息,包括了IM异常,RTC异常,业务异常三种情况
- (void)callDidOccurError:(EaseCallError *)aError
{
    
}

EaseCallError异常包括IM异常,RTC异常以及业务处理异常,定义如下

@interface EaseCallError : NSObject
// 异常类型,包括IM异常,RTC异常,业务处理异常
@property (nonatomic) EaseCallErrorType aErrorType;
// 异常代号
@property (nonatomic) NSInteger errCode;
// 异常描述
@property (nonatomic) NSString* errDescription;

配置修改

UI库初始化之后,也可以再次修改配置,修改方法如下:

// 以下为修改铃声过程
EaseCallConfig* config = [[EaseCallManager sharedManager] getEaseCallConfig];
NSString* path = [[NSBundle mainBundle] pathForResource:@"huahai128" ofType:@"mp3"];
config.ringFileUrl = [NSURL fileURLWithPath:path];

头像昵称修改

3.8.1新增了修改头像昵称的接口,用户可以在加入频道后,修改自己和会议中其他人的头像昵称,修改方法如下:

EaseCallUser* user = [EaseCallUser userWithNickName:info.nickName image:[NSURL URLWithString:info.avatarUrl]];
[[[EaseCallManager sharedManager] getEaseCallConfig] setUser:username info:user];

离线推送

为保证被叫用户APP退到后台,或离线时也能收到通话请求,用户需要开启离线推送,离线推送场景方案参见:http://docs-im.easemob.com/im/other/integrationcases/iosimnotifi

离线推送开启过程参见:http://docs-im.easemob.com/im/ios/apns/deploy

开启离线推送后,被叫用户在离线情况下收到呼叫请求时,会在手机通知页面弹出一条通知消息,用户点击该消息,可以唤醒App,并进入振铃页面

EaseCallKitUI库的API主要包括管理模块EaseCallManager和回调模块EaseCallDelegate。

管理模块EaseCallManager的API列表如下

方法 说明

initWithConfig:delegate 初始化方法
startSingleCallWithUId:type:ext:completion 发起单人通话
startInviteUsers:type:ext:completion: 发起多人通话
getEaseCallConfig 获取配置
setRTCToken:channelName: 3.8.0,设置声网Token
setRTCToken:channelName:uid: 3.8.1以上,设置声网Token
setUsers:channelName: 3.8.1以上,设置环信名称与声网uid的映射表

回调模块EaseCallDelegate的API列表如下

事件 说明
callDidEnd:reason:time:type: 通话结束时触发该事件
multiCallDidInvitingWithCurVC:excludeUsers:ext: 多人会议中点击邀请按钮触发该事件
callDidReceive:inviter:ext: 振铃时触发该事件
callDidRequestRTCTokenForAppId:channelName:account: 3.8.0,获取声网token回调
callDidRequestRTCTokenForAppId:channelName:account:uid: 3.8.1以上,获取声网token回调
callDidOccurError: 异常时触发该事件
remoteUserDidJoinChannel:uid: 3.8.1以上,对方加入频道时触发
callDidJoinChannel:uid: 3.8.1以上,自己加入频道时触发