IOS技术分享| ARCallPlus 开源项目
Posted anyRTC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOS技术分享| ARCallPlus 开源项目相关的知识,希望对你有一定的参考价值。
ARCallPlus 简介
ARCallPlus 是开源的音视频通话项目,同时支持ios、android、Web等平台。本文主要介绍音视频通话 ARUICalling 模块 iOS 本地库的封装。
源码下载
三行代码、二十分钟应用内构建,实现音视频通话。本项目已上架App Store,欢迎下载体验。
开发环境
-
开发工具:Xcode13 真机运行
-
开发语言:Objective-C、Swift
项目结构
核心 API:
- ARUILogin(登录 API)
- ARUICalling(通话 API)
- ARUICallingListerner(通话回调)
内部核心 API:
- ARTCCalling(音视频)
- ARTCCallingDelegate(音视频回调)
- ARTCCalling+Signal(实时消息)
核心 API 和回调
@interface ARUICalling : NSObject
+ (instancetype)shareInstance;
/// 通话接口
/// @param users 用户信息
/// @param type 呼叫类型:视频/语音
- (void)call:(NSArray<ARCallUser *> *)users type:(ARUICallingType)type;
/// 通话回调
/// @param listener 回调
- (void)setCallingListener:(id<ARUICallingListerner>)listener;
/// 设置铃声,建议在30s以内,只支持本地音频文件
/// @param filePath 音频文件路径
- (void)setCallingBell:(NSString *)filePath;
/// 开启静音模式(默认关)
- (void)enableMuteMode:(BOOL)enable;
/// 开启自定义路由(默认关)
/// @param enable 打开后,在onStart回调中,会收到对应的ViewController对象,可以自行决定视图展示方式
- (void)enableCustomViewRoute:(BOOL)enable;
@end
@protocol ARUICallingListerner <NSObject>
// 收到呼叫时,先通过此方法询问是否可以唤起被叫UI.
// 返回为true,直接唤起UI。返回为false,内部返回忙线
// 不实现默认直接可以唤起UI
- (BOOL)shouldShowOnCallView NS_SWIFT_NAME(shouldShowOnCallView());
/// 呼叫开始回调。主叫、被叫均会触发;
/// 被叫触发时,会将控制器通过监听回调出来,由接入方决定显示方案。
/// @param userIDs 本次通话用户id(自己除外)
/// @param type 通话类型:视频\\音频
/// @param role 通话角色:主叫\\被叫
/// @param viewController 提供Calling功能页面给调用方,可以让用户在此基础上自定义
- (void)callStart:(NSArray<NSString *> *)userIDs type:(ARUICallingType)type role:(ARUICallingRole)role viewController:(UIViewController * _Nullable)viewController NS_SWIFT_NAME(callStart(userIDs:type:role:viewController:));
/// 通话结束回调
/// @param userIDs 本次通话用户id(自己除外)
/// @param type 通话类型:视频\\音频
/// @param role 通话角色:主叫\\被叫
/// @param totalTime 通话时长
- (void)callEnd:(NSArray<NSString *> *)userIDs type:(ARUICallingType)type role:(ARUICallingRole)role totalTime:(float)totalTime NS_SWIFT_NAME(callEnd(userIDs:type:role:totalTime:));
/// 通话事件回调
/// @param event 回调事件类型
/// @param type 通话类型:视频\\音频
/// @param role 通话角色:主叫\\被叫
/// @param message 事件
- (void)onCallEvent:(ARUICallingEvent)event type:(ARUICallingType)type role:(ARUICallingRole)role message:(NSString *)message NS_SWIFT_NAME(onCallEvent(event:type:role:message:));
/// 推送事件回调
/// @param userIDs 不在线的用户id
/// @param type 通话类型:视频\\音频
- (void)onPushToOfflineUser:(NSArray<NSString *> *)userIDs type:(ARUICallingType)type;
@end
示例代码
效果展示(点对点音频通话)
代码实现
- (ARtcEngineKit *)rtcEngine
if (!_rtcEngine)
/// 实例化音视频引擎对象
_rtcEngine = [ARtcEngineKit sharedEngineWithAppId:[ARUILogin getSdkAppID] delegate:self];
/// 直播模式
[_rtcEngine setChannelProfile: ARChannelProfileLiveBroadcasting];
[_rtcEngine setClientRole: ARClientRoleBroadcaster];
/// 编码配置
ARVideoEncoderConfiguration *configuration = [[ARVideoEncoderConfiguration alloc] init];
configuration.dimensions = CGSizeMake(960, 540);
configuration.frameRate = 15;
configuration.bitrate = 500;
[_rtcEngine setVideoEncoderConfiguration:configuration];
/// 启用说话者音量提示
[_rtcEngine enableAudioVolumeIndication:2000 smooth:3 report_vad: YES];
/// 开启美颜
[_rtcEngine setBeautyEffectOptions:YES options:[[ARBeautyOptions alloc]init]];
return _rtcEngine;
//MARK: - Publish Method
- (void)call:(NSArray *)userIDs type:(CallType)type
/// 发起通话
if (!self.isOnCalling)
self.curLastModel.inviter = [ARUILogin getUserID];
self.curLastModel.action = CallAction_Call;
self.curLastModel.calltype = type;
self.curRoomID = [NSString stringWithFormat:@"%d", [ARTCCallingUtils generateRoomID]];
self.isMembers = userIDs.count >= 2 ? YES : NO;
self.calleeUserIDs = [@[] mutableCopy];
self.curType = type;
self.isOnCalling = YES;
self.isBeingCalled = NO;
[self joinRoom];
[self createMemberChannel];
// 如果不在当前邀请列表,则新增
NSMutableArray *newInviteList = [NSMutableArray array];
for (NSString *userID in userIDs)
if (![self.curInvitingList containsObject:userID])
[newInviteList addObject:userID];
[self.curInvitingList addObjectsFromArray:newInviteList];
[self.calleeUserIDs addObjectsFromArray:newInviteList];
if (!(self.curInvitingList && self.curInvitingList.count > 0)) return;
self.currentCallingUserID = newInviteList.firstObject;
for (NSString *userID in self.curInvitingList)
[self invite:userID action:CallAction_Call];
- (void)accept:(BOOL)isVideo
/// 接受当前通话
ARLog(@"Calling - accept Call");
if (!isVideo)
[self.rtcEngine disableVideo];
self.curType = CallType_Audio;
if ([self canDelegateRespondMethod:@selector(onSwitchToAudio:message:)])
[self.delegate onSwitchToAudio:YES message:@""];
[self joinRoom];
self.currentCallingUserID = self.curSponsorForMe;
[self invite:self.curSponsorForMe action:CallAction_Accept];
self.isCallSucess = YES;
[self dealWithException:10];
- (void)reject
/// 拒绝当前通话
ARLog(@"Calling - reject Call");
[self invite:self.curSponsorForMe action:CallAction_Reject];
self.isOnCalling = NO;
[self.rtcEngine disableVideo];
- (void)hangup
/// 主动挂断通话
__block BOOL hasCallUser = NO;
[self.curRoomList enumerateObjectsUsingBlock:^(NSString *user, NSUInteger idx, BOOL * _Nonnull stop)
if ((user && user.length > 0) && ![self.curInvitingList containsObject:user])
// 还有正在通话的用户
hasCallUser = YES;
[self invite:user action:CallAction_End];
*stop = YES;
];
/// 主叫需要取消未接通的通话
if (hasCallUser == NO)
ARLog(@"Calling - GroupHangup Send CallAction_Cancel");
[self.curInvitingList enumerateObjectsUsingBlock:^(NSString *invitedId, NSUInteger idx, BOOL * _Nonnull stop)
[self invite:invitedId action:CallAction_Cancel];
];
[self leaveRoom];
self.isOnCalling = NO;
- (void)switchToAudio
/// 切换到语音通话(通话中)
self.curType = CallType_Audio;
[self.rtcEngine disableVideo];
[self invite:self.currentCallingUserID action:CallAction_SwitchToAudio];
if ([self canDelegateRespondMethod:@selector(onSwitchToAudio:message:)])
[self.delegate onSwitchToAudio:YES message:@""];
- (void)startRemoteView:(NSString *)userID view:(UIView *)view
/// 开启远程用户视频渲染
ARLog(@"Calling - startRemoteView userID = %@", userID);
if (userID.length != 0)
ARtcVideoCanvas *canvas = [[ARtcVideoCanvas alloc] init];
canvas.uid = userID;
canvas.view = view;
[self.rtcEngine setupRemoteVideo:canvas];
- (void)stopRemoteView:(NSString *)userID
/// 关闭远程用户视频渲染
ARLog(@"Calling - stopRemoteView userID = %@", userID);
ARtcVideoCanvas *canvas = [[ARtcVideoCanvas alloc] init];
canvas.uid = userID;
canvas.view = nil;
[self.rtcEngine setupRemoteVideo:canvas];
- (void)openCamera:(BOOL)frontCamera view:(UIView *)view
/// 打开摄像头
ARLog(@"Calling - openCamera");
if (self.curType == CallType_Video)
[self.rtcEngine enableVideo];
ARtcVideoCanvas *canvas = [[ARtcVideoCanvas alloc] init];
canvas.uid = [ARUILogin getUserID];
canvas.view = view;
[self.rtcEngine setupLocalVideo:canvas];
[self.rtcEngine startPreview];
self.isFrontCamera = frontCamera;
效果展示(点对点视频通话)
代码实现
//MARK: - ARtcEngineDelegate
- (void)rtcEngine:(ARtcEngineKit *)engine didOccurError:(ARErrorCode)errorCode
/// 发生错误回调
ARLog(@"Calling - didOccurError = %ld", (long)errorCode);
- (void)rtcEngine:(ARtcEngineKit *)engine firstRemoteVideoDecodedOfUid:(NSString *)uid size:(CGSize)size elapsed:(NSInteger)elapsed
ARLog(@"Calling - firstRemoteVideoDecodedOfUid = %@", uid);
- (void)rtcEngine:(ARtcEngineKit *)engine didJoinedOfUid:(NSString *)uid elapsed:(NSInteger)elapsed
/// 远端用户/主播加入回调
ARLog(@"Calling - didJoinedOfUid = %@", uid);
// C2C curInvitingList 不要移除 userID,如果是自己邀请的对方,这里移除后,最后发结束信令的时候找不到人
[self dealWithException:0];
[self removeTimer:uid];
if ([self.curInvitingList containsObject:uid])
[self.curInvitingList removeObject:uid];
if (![self.curRoomList containsObject:uid])
[self.curRoomList addObject:uid];
// C2C 通话要计算通话时长
if ([self canDelegateRespondMethod:@selector(onUserEnter:)])
[self.delegate onUserEnter:uid];
- (void)rtcEngine:(ARtcEngineKit *)engine didOfflineOfUid:(NSString *)uid reason:(ARUserOfflineReason)reason
/// 远端用户(通信场景)/主播(直播场景)离开当前频道回调
ARLog(@"Calling - didOfflineOfUid = %@", uid);
// C2C curInvitingList 不要移除 userID,如果是自己邀请的对方,这里移除后,最后发结束信令的时候找不到人
if (self.isMembers || (!self.isMembers && reason == ARUserOfflineReasonQuit))
if ([self.curInvitingList containsObject:uid])
[self.curInvitingList removeObject:uid];
if ([self.curRoomList containsObject:uid])
[self.curRoomList removeObject:uid];
if ([self canDelegateRespondMethod:@selector(onUserLeave:)])
[self.delegate onUserLeave:uid];
[self preExitRoom];
else if (reason == ARUserOfflineReasonDropped)
[self dealWithException:10];
- (void)rtcEngine:(ARtcEngineKit *)engine remoteVideoStateChangedOfUid:(NSString *)uid state:(ARVideoRemoteState)state reason:(ARVideoRemoteStateReason)reason elapsed:(NSInteger)elapsed
/// 远端视频状态发生改变回调
if (reason == ARVideoRemoteStateReasonRemoteMuted || reason == ARVideoRemoteStateReasonRemoteUnmuted)
if ([self canDelegateRespondMethod:@selector(onUserVideoAvailable:available:)])
[self.delegate onUserVideoAvailable:uid available:(reason == ARVideoRemoteStateReasonRemoteMuted) ? NO : YES];
- (void)rtcEngine:(ARtcEngineKit *)engine remoteAudioStateChangedOfUid:(NSString *)uid state:(ARAudioRemoteState)state reason:(ARAudioRemoteStateReason)reason elapsed:(NSInteger)elapsed
/// 远端音频状态发生改变回调
if (reason == ARAudioRemoteReasonRemoteMuted || reason == ARAudioRemoteReasonRemoteUnmuted)
if ([self canDelegateRespondMethod:@selector(onUserAudioAvailable:available:)])
[self.delegate onUserAudioAvailable:uid available:(reason == ARAudioRemoteReasonRemoteMuted) ? NO : YES];
- (void)rtcEngine:(ARtcEngineKit *)engine reportAudioVolumeIndicationOfSpeakers:(NSArray<ARtcAudioVolumeInfo *> *)speakers totalVolume:(NSInteger)totalVolume
/// 提示频道内谁正在说话、说话者音量及本地用户是否在说话的回调
if ([self canDelegateRespondMethod:@selector(onUserVoiceVolume:volume:)])
for (ARtcAudioVolumeInfo *info in speakers)
if ([info.uid isEqualToString:@"0"])
[self.delegate onUserVoiceVolume:[ARUILogin getUserID] volume:(UInt32)info.volume];
else
[self.delegate onUserVoiceVolume:info.uid volume:(UInt32)info.volume];
- (void)rtcEngine:(ARtcEngineKit *)engine connectionChangedToState:(ARConnectionStateType)state reason:(ARConnectionChangedReason)reason
//ARLog(@"Calling - rtc connectionStateChanged state = %ld reason = %ld", (long)state, (long)reason);
- (void)rtcEngine:(ARtcEngineKit *)engine didVideoSubscribeStateChange:(NSString *)channel withUid:(NSString *)uid oldState:(ARStreamSubscribeState)oldState newState:(ARStreamSubscribeState)newState elapseSinceLastState:(NSInteger)elapseSinceLastState
ARLog(@"Calling - didVideoSubscribeStateChange = %@ %@ %lu %lu", channel, uid, (unsigned long)oldState, (unsigned long)newState);
效果展示(多人视频通话)
代码实现
- (void)addSignalListener
ARUILogin.kit.aRtmDelegate = self;
self.callEngine.callDelegate = self;
/// 用户进入后台推送问题
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(enterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(becomeActive:) name:UIApplicationWillEnterForegroundNotification object:nil];
- (void)removeSignalListener
self.callEngine.callDelegate = nil;
self.callEngine = nil;
[NSNotificationCenter.defaultCenter removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
[NSNotificationCenter.defaultCenter removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
- (void)invite:(NSString *)receiver action:(CallAction)action
if (action == CallAction_Call)
/// 发起呼叫邀请
NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:[ARUILogin getUserID], nil];
[arr addObjectsFromArray:self.calleeUserIDs];
NSMutableArray *infoArr = [NSMutableArray array];
for (NSInteger i = 0; i < arr.count; i++)
ARCallUser *user = [ARUILogin getCallUserInfo:arr[i]];
[infoArr addObject:[NSObject ar_dictionaryWithObject: user]];
NSDictionary *dic = @@"Mode": @(self.curType == CallType_Video ? 0 : 1),
@"Conference": [NSNumber numberWithBool:self.isMembers],
@"ChanId": self.curRoomID,
@"UserData": arr,
@"UserInfo": infoArr
;
ARtmLocalInvitation *localInvitation = [[ARtmLocalInvitation alloc] initWithCalleeId:receiver];
localInvitation.content = [ARTCCallingUtils dictionary2JsonStr:dic];
[self.callEngine sendLocalInvitation:localInvitation completion:^(ARtmInvitationApiCallErrorCode errorCode)
ARLog(@"sendLocalInvitation code = %ld", (long)errorCode);
];
[self.callingDic setObject:localInvitation forKey:receiver];
else if (action == CallAction_Cancel)
/// 取消呼叫邀请
id invitation = [self.callingDic objectForKey:receiver];
if (invitation)
ARtmLocalInvitation *localInvitation = (ARtmLocalInvitation *)invitation;
[self.callEngine cancelLocalInvitation:localInvitation completion:^(ARtmInvitationApiCallErrorCode errorCode)
ARLog(@"cancelLocalInvitation code = %ld", (long)errorCode);
];
else if (action == CallAction_Accept)
/// 接受呼叫邀请
id invitation = [self.calledDic objectForKey:receiver];
if (invitation)
ARtmRemoteInvitation *remoteInvitation = (ARtmRemoteInvitation *)invitation;
NSDictionary *dic = @@"Mode": @(self.curType == CallType_Video ? 0: 1), @"Conference": [NSNumber numberWithBool:self.isMembers];
remoteInvitation.response = [ARTCCallingUtils dictionary2JsonStr:dic];
[self.callEngine acceptRemoteInvitation:remoteInvitation completion:^(ARtmInvitationApiCallErrorCode errorCode)
ARLog(@"acceptRemoteInvitation code = %ld", (long)errorCode);
];
else if (action == CallAction_Reject)
/// 拒绝呼叫邀请
id invitation = [self.calledDic objectForKey:receiver];
if (invitation)
ARtmRemoteInvitation *remoteInvitation = (ARtmRemoteInvitation *)invitation;
[self.callEngine refuseRemoteInvitation:remoteInvitation completion:^(ARtmInvitationApiCallErrorCode errorCode)
ARLog(@"refuseRemoteInvitation code = %ld", (long)errorCode);
];
else if (action == CallAction_SwitchToAudio)
/// 切换成语音通话
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:@"SwitchAudio", @"Cmd",nil];
ARtmMessage *message = [[ARtmMessage alloc] initWithText:[ARTCCallingUtils dictionary2JsonStr:dic]];
[self sendPeerMessage:message user:receiver];
else if (action == CallAction_End)
/// 通话中断
if (!self.isMembers)
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:@"EndCall", @"Cmd",nil];
ARtmMessage *message = [[ARtmMessage alloc] initWithText:[ARTCCallingUtils dictionary2JsonStr:dic]];
[self sendPeerMessage:message user:receiver];
- (void)preExitRoom
/// 当前房间中存在成员,不能自动退房
if (self.curRoomList.count > 0) return;
/// 存在正在呼叫的通话
if (self.curInvitingList.count >= 1)
return;
[self exitRoom];
- (void)exitRoom
ARLog(@"Calling - exitRoom");
if ([self canDelegateRespondMethod:@selector(onCallEnd)])
[self.delegate onCallEnd];
for (NSString *uid in self.timerDic.allKeys)
[self removeTimer:uid];
[self dealWithException:0];
[self leaveRoom];
self.isOnCalling = NO;
if(UIApplication.sharedApplication.applicationState == UIApplicationStateBackground)
[self logout];
// MARK: - privite
- (ARtmCallKit *)callEngine
return [ARUILogin.kit getRtmCallKit];
- (void)sendPeerMessage:(ARtmMessage *)message user:(NSString *)uid
ARLog(@"Calling - sendPeerMessage = %@", message.text);
ARtmSendMessageOptions *options = [[ARtmSendMessageOptions alloc] init];
[[ARUILogin kit] sendMessage:message toPeer:uid sendMessageOptions:options completion:^(ARtmSendPeerMessageErrorCode errorCode)
ARLog(@"Calling - SendPeerMessage code = %ld", (long)errorCode);
];
- (void)logout
if (!self.isOnCalling && ARUILogin.kit != nil)
[ARUILogin.kit logoutWithCompletion:nil];
self.interrupt = YES;
- (void)enterBackground:(NSNotification *)notification
[self logout];
- (void)becomeActive:(NSNotification *)notification
if (!self.isOnCalling && self.interrupt)
[ARUILogin.kit loginByToken:nil user:ARUILogin.getUserID completion:nil];
self.interrupt = NO;
//MARK: - ARtmDelegate
- (void)rtmKit:(ARtmKit *)kit connectionStateChanged:(ARtmConnectionState)state reason:(ARtmConnectionChangeReason)reason
ARLog(@"Calling - rtm connectionStateChanged state = %ld reason = %ld", (long)state, (long)reason);
if (reason == ARtmConnectionChangeReasonRemoteLogin)
[self.delegate onError:401 msg:@"RemoteLogin"];
[self exitRoom];
return;
if (!self.isMembers)
if (state == ARtmConnectionStateDisconnected || state == ARtmConnectionStateReconnecting)
self.isReconnection = YES;
[self dealWithException: 30];
else if (state == ARtmConnectionStateConnected)
[self dealWithException:0];
if (self.isReconnection && self.isOnCalling && self.currentCallingUserID)
/// 兼容异常
[self dealWithException:10];
self.isReconnection = NO;
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:@"CallState", @"Cmd",nil];
ARtmMessage *message = [[ARtmMessage alloc] initWithText:[ARTCCallingUtils dictionary2JsonStr:dic]];
[self sendPeerMessage:message user:self.currentCallingUserID];
- (void)rtmKit:(ARtmKit *)kit messageReceived:(ARtmMessage *)message fromPeer:(NSString *)peerId
/// 收到点对点消息回调
ARLog(@"Calling - messageReceived text = %@ ", message.text);
if (message.text.length != 0)
NSDictionary *dic = [ARTCCallingUtils jsonSring2Dictionary:message.text];
NSString *value = [dic objectForKey:@"Cmd"];
if ([value isEqualToString:@"SwitchAudio"])
/// 切换成语音通话
if ([self canDelegateRespondMethod:@selector(onSwitchToAudio:message:)])
[self.delegate onSwitchToAudio:YES message:@""];
self.curType = CallType_Audio;
else if ([value isEqualToString:@"EndCall"])
/// 结束通话
if (!self.isMembers)
if ([self canDelegateRespondMethod:@selector(onUserLeave:)])
[self.delegate onUserLeave:peerId];
[self exitRoom];
else if ([value isEqualToString:@"CallState"])
/// 确认通话状态
NSDictionary *dic;
if (self.isCallSucess)
dic = @@"Cmd": @"CallStateResult", @"state": @(2), @"Mode": (self.curType == CallType_Video ? @(0) : @(1));
else
dic= @@"Cmd": @"CallStateResult", @"state": @(1);
ARtmMessage *message = [[ARtmMessage alloc] initWithText:[ARTCCallingUtils dictionary2JsonStr:dic]];
[self sendPeerMessage:message user:peerId];
else if ([value isEqualToString:@"CallStateResult"])
/// 对方通话状态回复结果
[self dealWithException:0];
int state = [[dic objectForKey:@"state"] intValue];
if (state == 0)
/// 已挂断
[self exitRoom];
else if (state == 1)
/// 呼叫等待
else
/// 已同意
int mode = [[dic objectForKey:@"Mode"] intValue];
if (self.curType == CallType_Video && mode == 1)
self.curType = CallType_Audio;
[self switchToAudio];
// MARK: - ARtmCallDelegate
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit localInvitationReceivedByPeer:(ARtmLocalInvitation * _Nonnull)localInvitation
/// 被叫已收到呼叫邀请
ARLog(@"Calling - localInvitationReceivedByPeer");
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit localInvitationAccepted:(ARtmLocalInvitation * _Nonnull)localInvitation withResponse:(NSString * _Nullable) response
/// 被叫已接受呼叫邀请
ARLog(@"Calling - localInvitationAccepted response = %@", response);
[self.callingDic removeObjectForKey:localInvitation.calleeId];
if (response != nil)
NSDictionary * dic = [ARTCCallingUtils jsonSring2Dictionary:response];
if (self.curType == CallType_Video && [[dic objectForKey:@"Mode"] intValue] == 1)
if ([self canDelegateRespondMethod:@selector(onSwitchToAudio:message:)])
[self.delegate onSwitchToAudio:YES message:@""];
self.curType = CallType_Audio;
self.isCallSucess = YES;
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit localInvitationRefused:(ARtmLocalInvitation * _Nonnull)localInvitation withResponse:(NSString * _Nullable) response
/// 被叫已拒绝呼叫邀请
ARLog(@"Calling - localInvitationRefused");
[self.callingDic removeObjectForKey:localInvitation.calleeId];
BOOL isBusy = NO;
if (localInvitation.response.length != 0)
NSDictionary * dic = [ARTCCallingUtils jsonSring2Dictionary:localInvitation.response];
if ([dic.allValues containsObject:@"Calling"])
isBusy = YES;
if (self.delegate)
NSString *uid = localInvitation.calleeId;
if ([self.curInvitingList containsObject:uid])
[self.curInvitingList removeObject:uid];
if (isBusy)
if ([self canDelegateRespondMethod:@selector(onLineBusy:)])
[self.delegate onLineBusy:localInvitation.calleeId];
else
if ([self canDelegateRespondMethod:@selector(onReject:)])
[self.delegate onReject:uid];
[self preExitRoom];
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit localInvitationCanceled:(ARtmLocalInvitation * _Nonnull)localInvitation
/// 呼叫邀请已被取消
ARLog(@"Calling - localInvitationCanceled");
[self.callingDic removeObjectForKey:localInvitation.calleeId];
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit localInvitationFailure:(ARtmLocalInvitation * _Nonnull)localInvitation errorCode:(ARtmLocalInvitationErrorCode)errorCode
/// 呼叫邀请发送失败
ARLog(@"Calling - localInvitationFailure");
NSString *calleeId = localInvitation.calleeId;
[self.callingDic removeObjectForKey:calleeId];
if ([self canDelegateRespondMethod:@selector(onNoResp:)])
[self.delegate onNoResp:calleeId];
if ([self.curInvitingList containsObject:calleeId])
[self.curInvitingList removeObject:calleeId];
[self preExitRoom];
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit remoteInvitationReceived:(ARtmRemoteInvitation * _Nonnull)remoteInvitation
/// 收到一个呼叫邀请
ARLog(@"Calling - remoteInvitationReceived");
[self.calledDic setObject:remoteInvitation forKey:remoteInvitation.callerId];
if (!self.isOnCalling)
self.isOnCalling = YES;
self.curSponsorForMe = remoteInvitation.callerId;
self.currentCallingUserID = remoteInvitation.callerId;
NSDictionary *dic = [ARTCCallingUtils jsonSring2Dictionary:remoteInvitation.content];
self.isMembers = [[dic objectForKey:@"Conference"] boolValue];
self.curRoomID = [dic objectForKey:@"ChanId"];
CallType type = ([[dic objectForKey:@"Mode"] intValue] == 0) ? CallType_Video : CallType_Audio;
self.curType = type;
if ([dic.allKeys containsObject:@"UserInfo"])
NSArray *infoArr = [dic objectForKey:@"UserInfo"];
for (NSInteger i = 0; i < infoArr.count; i++)
ARCallUser *user = [ARCallUser ar_objectWithDictionary: infoArr[i]];
[ARUILogin setCallUserInfo:user];
if (self.isMembers)
/// 多人通话
NSArray *arr = [dic objectForKey:@"UserData"];
[self.delegate onInvited:remoteInvitation.callerId userIds:arr isFromGroup:NO callType:type];
[self createMemberChannel];
/// 30s
for (NSInteger i = 0; i < arr.count; i++)
NSString *uid = arr[i];
/// 被叫对其他受邀者倒计时 -- 异常处理
if (![uid isEqualToString:[ARUILogin getUserID]] && ![uid isEqualToString:self.curSponsorForMe])
__block NSInteger totalTime = 0;
NSTimeInterval interval = 1.0;
__weak typeof(self) weakSelf = self;
NSString *timerName = [ARTCGCDTimer timerTask:^
totalTime += (NSInteger)interval;
if (totalTime == 30)
if ([weakSelf canDelegateRespondMethod:@selector(onNoResp:)])
[weakSelf.delegate onNoResp:uid];
[weakSelf removeTimer: uid];
ARLog(@"%@ ==> %ld \\n", uid, (long)totalTime);
start:0 interval:interval repeats:YES async:NO];
[self.timerDic setObject:timerName forKey:uid];
else
/// 单人通话
[self.delegate onInvited:remoteInvitation.callerId userIds:@[[ARUILogin getUserID]] isFromGroup:NO callType:type];
else
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:@"Calling", @"Cmd",nil];
remoteInvitation.response = [ARTCCallingUtils dictionary2JsonStr:dic];
[self invite:remoteInvitation.callerId action:CallAction_Reject];
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit remoteInvitationRefused:(ARtmRemoteInvitation * _Nonnull)remoteInvitation
/// 拒绝呼叫邀请成功
ARLog(@"Calling - remoteInvitationRefused");
[self.calledDic removeObjectForKey:remoteInvitation.callerId];
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit remoteInvitationAccepted:(ARtmRemoteInvitation * _Nonnull)remoteInvitation
/// 接受呼叫邀请成功
ARLog(@"Calling - remoteInvitationAccepted");
[self.calledDic removeObjectForKey:remoteInvitation.callerId];
- (void)rtmCallKit:(ARtmCallKit * _Nonnull)callKit remoteInvitationCanceled:(ARtmRemoteInvitation * _Nonnull)remoteInvitation
/// 主叫已取消呼叫邀请
ARLog(@"Calling - remoteInvitationCanceled");
[self.calledDic removeObjectForKey:remoteInvitation.callerId];
if ([self.curSponsorForMe isEqualToString:remoteInvitation.callerId])
[self exitRoom];
if ([self canDelegateRespondMethod:@selector(onCallingCancel:)])
[self.delegate onCallingCancel:remoteInvitation.callerId];
结束语
最后,ARCallPlus开源项目中还存在一些bug和待完善的功能点。有不足之处欢迎大家指出issues。最后再贴一下 Github开源下载地址。
以上是关于IOS技术分享| ARCallPlus 开源项目的主要内容,如果未能解决你的问题,请参考以下文章