ARCallPlus 簡介
ARCallPlus 是 anyRTC 開源的音視訊通話項目,同時支援iOS、Android、Web等平台。本文主要介紹音視訊通話 ARUICalling 子產品 iOS 本地庫的封裝。
源碼下載下傳
三行代碼、二十分鐘應用内建構,實作音視訊通話。本項目已上架App Store,歡迎下載下傳體驗。
- GitHub 開源位址
- App Store 下載下傳位址
開發環境
- 開發工具:Xcode13 真機運作
- 開發語言:Objective-C、Swift
項目結構
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iMzATN3YWO3ATN0Q2NzkzMyYzXyMzM0ATMyIzLcNDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL0M3Lc9CX6MHc0RHaiojIsJye.png)
核心 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];
}
}
}