// // NTESNetChatViewController.m // NIM // // Created by chris on 15/5/18. // Copyright (c) 2015年 Netease. All rights reserved. // #import "NTESNetChatViewController.h" #import "UIAlertView+NTESBlock.h" #import "UIView+Toast.h" #import "NTESTimerHolder.h" #import "NTESNetCallChatInfo.h" #import "NTESBundleSetting.h" #import #import #import #import "UIView+NTES.h" //十秒之后如果还是没有收到对方响应的control字段,则自己发起一个假的control,用来激活铃声并自己先进入房间 #define DelaySelfStartControlTime 10 //激活铃声后无人接听的超时时间 #define NoBodyResponseTimeOut 45 //周期性检查剩余磁盘空间 #define DiskCheckTimeInterval 10 //剩余磁盘空间不足的警告阈值 #define MB (1024ll * 1024ll) #define FreeDiskSpaceWarningThreshold (10 * MB) @interface NTESNetChatViewController () @property(nonatomic, strong) NTESTimerHolder *timer; @property(nonatomic, strong) NSMutableArray *chatRoom; @property(nonatomic, assign) BOOL recordWillStopForLackSpace; @property(nonatomic, strong) NTESTimerHolder *diskCheckTimer; @property(nonatomic, assign) BOOL userHangup; @property(nonatomic, strong) NTESTimerHolder *calleeResponseTimer; //被叫等待用户响应接听或者拒绝的时间 @property(nonatomic, assign) BOOL calleeResponsed; @property(nonatomic, assign) int successRecords; //@property (nonatomic, strong) NTESRecordSelectView * recordView; @end @implementation NTESNetChatViewController { UInt64 CallId; } - (BOOL)forbidInteractivePop { return YES; } - (instancetype)initWithCallee:(NSString *)callee { self = [self initWithNibName:nil bundle:nil]; if (self) { self.peerUid = callee; self.callInfo.callee = callee; self.callInfo.caller = [[NIMSDK sharedSDK].loginManager currentAccount]; } return self; } - (instancetype)initWithCaller:(NSString *)caller callId:(uint64_t)callID { self = [self initWithNibName:nil bundle:nil]; if (self) { self.peerUid = caller; self.callInfo.caller = caller; self.callInfo.callee = [[NIMSDK sharedSDK].loginManager currentAccount]; self.callInfo.callID = callID; } return self; } - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { self.edgesForExtendedLayout = UIRectEdgeAll; if (!self.callInfo) { [[NIMSDK sharedSDK].mediaManager switchAudioOutputDevice:NIMAudioOutputDeviceSpeaker]; _callInfo = [[NTESNetCallChatInfo alloc] init]; } _timer = [[NTESTimerHolder alloc] init]; _diskCheckTimer = [[NTESTimerHolder alloc] init]; //防止应用在后台状态,此时呼入,会走init但是不会走viewDidLoad,此时呼叫方挂断,导致被叫监听不到,界面无法消去的问题。 id manager = [NIMAVChatSDK sharedSDK].netCallManager; [manager addDelegate:self]; } return self; } - (void)dealloc { NSLog(@"vc dealloc info : %@", self); [[NIMAVChatSDK sharedSDK].netCallManager removeDelegate:self]; } - (void)viewDidLoad { [super viewDidLoad]; __weak typeof(self) wself = self; [[NIMAVChatSDK sharedSDK].netCallManager addDelegate:self]; [self checkServiceEnable:^(BOOL result) { if (result) { // [self response:YES]; [wself afterCheckService]; } else { //用户禁用服务,干掉界面 if (wself.callInfo.callID) { //说明是被叫方 [[NIMAVChatSDK sharedSDK].netCallManager response:wself.callInfo.callID accept:NO option:nil completion:nil]; } [wself dismiss:nil]; } }]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [UIApplication sharedApplication].idleTimerDisabled = YES; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.player stop]; // [[NIMAVChatSDK sharedSDK].netCallManager hangup:CallId]; [UIApplication sharedApplication].idleTimerDisabled = NO; } - (void)afterCheckService { if (self.callInfo.isStart) { [self.timer startTimer:0.5 delegate:self repeats:YES]; [self onCalling]; } else if (self.callInfo.callID) { [self startByCallee]; } else { [self startByCaller]; } [self checkFreeDiskSpace]; [self.diskCheckTimer startTimer:DiskCheckTimeInterval delegate:self repeats:YES]; } #pragma mark - Subclass Impl - (void)startByCaller { if (self.callInfo.callType == NIMNetCallTypeVideo) { //视频呼叫马上发起 [self doStartByCaller]; } else { //语音呼叫先播放一断提示音,然后再发起 [self playConnnetRing]; __weak typeof(self) wself = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (!wself) { return; } if (wself.userHangup) { NSLog(@"Netcall request was cancelled before, ignore it."); return; } [wself doStartByCaller]; }); } } - (void)doStartByCaller { self.callInfo.isStart = YES; NSArray *callees = [NSArray arrayWithObjects:self.callInfo.callee, nil]; NIMNetCallOption *option = [[NIMNetCallOption alloc] init]; option.extendMessage = @"音视频请求扩展信息"; option.apnsContent = [NSString stringWithFormat:@"%@请求", self.callInfo.callType == NIMNetCallTypeAudio ? @"网络通话" : @"视频聊天"]; // option.apnsInuse = NO; option.apnsSound = @"video_chat_push.mp3"; [self fillUserSetting:option]; option.videoCaptureParam.startWithCameraOn = (self.callInfo.callType == NIMNetCallTypeVideo); __weak typeof(self) wself = self; [[NIMAVChatSDK sharedSDK].netCallManager start:callees type:wself.callInfo.callType option:option completion:^(NSError *error, UInt64 callID) { if (!error && wself) { NSLog(@"呼叫成功"); CallId = callID; wself.callInfo.callID = callID; wself.chatRoom = [[NSMutableArray alloc] init]; //十秒之后如果还是没有收到对方响应的control字段,则自己发起一个假的control,用来激活铃声并自己先进入房间 NSTimeInterval delayTime = DelaySelfStartControlTime; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [wself onControl:callID from:wself.callInfo.callee type:NIMNetCallControlTypeFeedabck]; }); } else { NSLog(@"呼叫失败"); if (error) { NSLog(@"连接失败跳转%@", error); [wself.navigationController.view makeToast:@"连接失败" duration:2 position:CSToastPositionCenter]; } else { //说明在start的过程中把页面关了。。 [[NIMAVChatSDK sharedSDK].netCallManager hangup:callID]; } [wself dismiss:nil]; } }]; } - (void)startByCallee { //告诉对方可以播放铃声了 self.callInfo.isStart = YES; NSMutableArray *room = [[NSMutableArray alloc] init]; [room addObject:self.callInfo.caller]; self.chatRoom = room; [[NIMAVChatSDK sharedSDK].netCallManager control:self.callInfo.callID type:NIMNetCallControlTypeFeedabck]; [self playReceiverRing]; _calleeResponseTimer = [[NTESTimerHolder alloc] init]; [_calleeResponseTimer startTimer:NoBodyResponseTimeOut delegate:self repeats:NO]; } - (void)hangup { _userHangup = YES; [[NIMAVChatSDK sharedSDK].netCallManager hangup:self.callInfo.callID]; if (self.callInfo.localRecording) { __weak typeof(self) wself = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ wself.chatRoom = nil; [wself dismiss:nil]; }); } else { self.chatRoom = nil; [self dismiss:nil]; } } - (void)response:(BOOL)accept { _calleeResponsed = YES; NIMNetCallOption *option = [[NIMNetCallOption alloc] init]; [self fillUserSetting:option]; __weak typeof(self) wself = self; [[NIMAVChatSDK sharedSDK].netCallManager response:self.callInfo.callID accept:accept option:option completion:^(NSError *error, UInt64 callID) { if (!error) { NSLog(@"被接成功"); [wself onCalling]; [wself.player stop]; [wself.chatRoom addObject:wself.callInfo.callee]; NSTimeInterval delay = 10.f; //10秒后判断下房间 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (wself.chatRoom.count == 1) { [wself.navigationController.view makeToast:@"通话失败" duration:2 position:CSToastPositionCenter]; [wself hangup]; } }); } else { NSLog(@"被接失败"); wself.chatRoom = nil; [wself.navigationController.view makeToast:@"连接失败" duration:2 position:CSToastPositionCenter]; [wself dismiss:nil]; } }]; //dismiss需要放在self后面,否在ios7下会有野指针 if (accept) { [self waitForConnectiong]; } else { [self dismiss:nil]; } } //- (void)onReceive:(UInt64)callID from:(NSString *)caller type:(NIMNetCallMediaType)type message:(NSString *)extendMessage{ // //// NTESMainTabController *tabVC = [NTESMainTabController instance]; //// [tabVC.view endEditing:YES]; //// UINavigationController *nav = tabVC.selectedViewController; //// //// if ([self shouldResponseBusy]){ //// [[NIMAVChatSDK sharedSDK].netCallManager control:callID type:NIMNetCallControlTypeBusyLine]; //// } //// else { //// //// if ([self shouldFireNotification:caller]) { //// NSString *text = [self textByCaller:caller //// type:type]; //// [_notifier start:text]; //// } // // // // UIViewController *vc; // switch (type) { // case NIMNetCallTypeVideo:{ // vc = [[NTESNetChatViewController alloc] initWithCaller:caller callId:callID]; //// [self response:NO]; // } // break; // case NIMNetCallTypeAudio:{ //// vc = [[NTESAudioChatViewController alloc] initWithCaller:caller callId:callID]; // } // break; // default: // break; // } // if (!vc) { // return; // } // // // 由于音视频聊天里头有音频和视频聊天界面的切换,直接用present的话页面过渡会不太自然,这里还是用push,然后做出present的效果 //// CATransition *transition = [CATransition animation]; //// transition.duration = 0.25; //// transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; //// transition.type = kCATransitionPush; //// transition.subtype = kCATransitionFromTop; //// [nav.view.layer addAnimation:transition forKey:nil]; //// nav.navigationBarHidden = YES; //// if (nav.presentedViewController) { //// // fix bug MMC-1431 //// [nav.presentedViewController dismissViewControllerAnimated:NO completion:nil]; //// } //// [nav pushViewController:vc animated:NO]; //// } //} - (void)dismiss:(void (^)(void))completion { [self dismissViewControllerAnimated:YES completion:^{ if (completion) { completion(); } }]; } - (void)onCalling { //子类重写 } - (void)waitForConnectiong { //子类重写 } - (void)onCalleeBusy { //子类重写 } - (BOOL)startAudioRecording { UInt64 timestamp = [[NSDate date] timeIntervalSince1970] * 1000; NSString *pathComponent = [NSString stringWithFormat:@"record_audio%@_%llu", [[NIMSDK sharedSDK].loginManager currentAccount], timestamp]; NSURL *filePath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:pathComponent]; return [[NIMAVChatSDK sharedSDK].netCallManager startAudioRecording:filePath error:nil]; } - (BOOL)startLocalRecording { UInt64 timestamp = [[NSDate date] timeIntervalSince1970] * 1000; NSString *pathComponent = [NSString stringWithFormat:@"record_%@_%llu.mp4", [[NIMSDK sharedSDK].loginManager currentAccount], timestamp]; NSURL *filePath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:pathComponent]; BOOL startAccepted; startAccepted = [[NIMAVChatSDK sharedSDK].netCallManager startRecording:filePath videoBitrate:(UInt32) [[NTESBundleSetting sharedConfig] localRecordVideoKbps] * 1000 uid:[[NIMSDK sharedSDK].loginManager currentAccount] ]; return startAccepted; } - (BOOL)startOtherSideRecording { UInt64 timestamp = [[NSDate date] timeIntervalSince1970] * 1000; NSString *pathComponent = [NSString stringWithFormat:@"record_%@_%llu.mp4", self.peerUid, timestamp]; NSURL *filePath = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:pathComponent]; BOOL startAccepted; startAccepted = [[NIMAVChatSDK sharedSDK].netCallManager startRecording:filePath videoBitrate:(UInt32) [[NTESBundleSetting sharedConfig] localRecordVideoKbps] * 1000 uid:self.peerUid ]; return startAccepted; } - (void)stopAudioRecording { [[NIMAVChatSDK sharedSDK].netCallManager stopAudioRecording]; self.callInfo.audioConversation = NO; } - (BOOL)stopLocalRecording { return [[NIMAVChatSDK sharedSDK].netCallManager stopRecordingWithUid:[[NIMSDK sharedSDK].loginManager currentAccount]]; } - (BOOL)stopOtherSideRecording { return [[NIMAVChatSDK sharedSDK].netCallManager stopRecordingWithUid:self.peerUid]; } - (void)udpateLowSpaceWarning:(BOOL)show { //子类重写 } - (void)recordWithAudioConversation:(BOOL)audioConversationOn myMedia:(BOOL)myMediaOn otherSideMedia:(BOOL)otherSideMediaOn video:(BOOL)isVideo { NSString *toastText = @""; int records = 0; _successRecords = 0;//不包括语音对话 //语音对话 if (audioConversationOn) { records++; if (!self.callInfo.audioConversation) { toastText = @"语音对话开始失败"; } } //自己音视频 if (myMediaOn) { records++; if (![self startLocalRecording]) { if (isVideo) { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"自己音视频开始失败" : @",自己音视频开始失败"]; } else { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"自己音频开始失败" : @",自己音频开始失败"]; } } else _successRecords++; } //对方音视频 if (otherSideMediaOn) { records++; if (![self startOtherSideRecording]) { if (isVideo) { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"对方音视频开始失败" : @",对方音视频开始失败"]; } else { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"对方音频开始失败" : @",对方音频开始失败"]; } } else _successRecords++; } //判断是否需要提示 if (![toastText isEqualToString:@""]) { if ([toastText componentsSeparatedByString:@","].count == records) { [self.view makeToast:@"开始录制失败" duration:2 position:CSToastPositionCenter]; } else { [self.view makeToast:toastText duration:2 position:CSToastPositionCenter]; } } } - (void)stopRecordTaskWithVideo:(BOOL)isVideo { NSString *toastText = @""; int records = 0; //结束自己音视频 if (self.callInfo.localRecording) { records++; if (![self stopLocalRecording]) { if (isVideo) { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"自己音视频结束失败" : @",自己音视频结束失败"]; } else { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"自己音频结束失败" : @",自己音频结束失败"]; } } } //结束对方音视频 if (self.callInfo.otherSideRecording) { records++; if (![self stopOtherSideRecording]) { if (isVideo) { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"对方音视频结束失败" : @",对方音视频结束失败"]; } else { toastText = [toastText stringByAppendingString:[toastText isEqualToString:@""] ? @"对方音频结束失败" : @",对方音频结束失败"]; } } } //判断是否要提示 if (![toastText isEqualToString:@""]) { if ([toastText componentsSeparatedByString:@","].count == records) { [self.view makeToast:@"结束录制失败" duration:3 position:CSToastPositionCenter]; } else { [self.view makeToast:toastText duration:3 position:CSToastPositionCenter]; } } } - (BOOL)allRecordsStopped { //当前没有录制任务 if (!self.callInfo.localRecording && !self.callInfo.otherSideRecording && !self.callInfo.audioConversation) { return YES; } else { return NO; } } - (BOOL)allRecordsSucceeed { int num = self.callInfo.localRecording + self.callInfo.otherSideRecording; if (num == _successRecords) { return YES; } else { return NO; } } - (NSString *)peerUid { if (_peerUid) { return _peerUid; } else { if ([_callInfo.callee isEqualToString:[[NIMSDK sharedSDK].loginManager currentAccount]]) { _peerUid = _callInfo.caller; return _callInfo.caller; } else { _peerUid = _callInfo.callee; return _callInfo.callee; } } } //-(void)showRecordSelectView:(BOOL)isVideo //{ // if (!_recordView) { // _recordView = [[NTESRecordSelectView alloc]initWithFrame:CGRectMake(0, 0, 250, 250) Video:isVideo]; // _recordView.delegate = self; // _recordView.centerX = self.view.width/2; // _recordView.centerY = self.view.height/2; // } // [self.view addSubview:_recordView]; //} #pragma mark - NIMNetCallManagerDelegate - (void)onControl:(UInt64)callID from:(NSString *)user type:(NIMNetCallControlType)control { if (user == [[NIMSDK sharedSDK].loginManager currentAccount]) { //多端登录时,自己会收到自己发出的控制指令,这里忽略他 return; } if (callID != self.callInfo.callID) { return; } switch (control) { case NIMNetCallControlTypeFeedabck: { NSMutableArray *room = self.chatRoom; if (room && !room.count && !_userHangup) { [self playSenderRing]; [room addObject:self.callInfo.caller]; //40秒之后查看一下房间状态,如果房间还在一个人的话,就播放铃声超时 __weak typeof(self) wself = self; uint64_t callId = self.callInfo.callID; NSTimeInterval delayTime = NoBodyResponseTimeOut; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSMutableArray *room = wself.chatRoom; if (wself && room && room.count == 1) { [[NIMAVChatSDK sharedSDK].netCallManager hangup:callId]; wself.chatRoom = nil; [wself playTimeoutRing]; [wself.navigationController.view makeToast:@"无人接听" duration:2 position:CSToastPositionCenter]; [wself startChat]; [wself dismiss:nil]; } }); } break; } case NIMNetCallControlTypeBusyLine: { [self onCalleeBusy]; [self playOnCallRing]; _userHangup = YES; [[NIMAVChatSDK sharedSDK].netCallManager hangup:callID]; __weak typeof(self) wself = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (3.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [wself startChat]; [wself dismiss:nil]; }); break; } case NIMNetCallControlTypeStartRecord: [self.view makeToast:@"对方开始了录制" duration:1 position:CSToastPositionCenter]; break; case NIMNetCallControlTypeStopRecord: [self.view makeToast:@"对方结束了录制" duration:1 position:CSToastPositionCenter]; break; default: break; } } - (void)onResponse:(UInt64)callID from:(NSString *)callee accepted:(BOOL)accepted { if (self.callInfo.callID == callID) { if (!accepted) { self.chatRoom = nil; [self.navigationController.view makeToast:@"对方拒绝接听" duration:2 position:CSToastPositionCenter]; [self playHangUpRing]; __weak typeof(self) wself = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (3.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [wself startChat]; [wself dismiss:nil]; }); } else { [self.player stop]; [self onCalling]; [self.chatRoom addObject:callee]; } } } - (void)onCallEstablished:(UInt64)callID { if (self.callInfo.callID == callID) { self.callInfo.startTime = [NSDate date].timeIntervalSince1970; [self.timer startTimer:0.5 delegate:self repeats:YES]; } } - (void)onCallDisconnected:(UInt64)callID withError:(NSError *)error { if (self.callInfo.callID == callID) { [self.timer stopTimer]; [self dismiss:nil]; self.chatRoom = nil; } } - (void)onResponsedByOther:(UInt64)callID accepted:(BOOL)accepted { [self.view.window makeToast:@"已在其他端处理" duration:2 position:CSToastPositionCenter]; [self dismiss:nil]; } - (void)onHangup:(UInt64)callID by:(NSString *)user { if (self.callInfo.callID == callID) { [self.player stop]; if (self.callInfo.localRecording) { __weak typeof(self) wself = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [wself dismiss:nil]; }); } else { [self dismiss:nil]; } } } - (void)onRecordStarted:(UInt64)callID fileURL:(NSURL *)fileURL uid:(NSString *)userId { if (self.callInfo.callID == callID) { if ([userId isEqualToString:[[NIMSDK sharedSDK].loginManager currentAccount]]) { self.callInfo.localRecording = YES; } if ([userId isEqualToString:self.peerUid]) { self.callInfo.otherSideRecording = YES; } } } - (void)onRecordError:(NSError *)error callID:(UInt64)callID uid:(NSString *)userId { NSLog(@"Local record error %@ (%zd)", error.localizedDescription, error.code); if (self.callInfo.callID == callID) { [self.view makeToast:[NSString stringWithFormat:@"录制发生错误: %zd", error.code] duration:2 position:CSToastPositionCenter]; if ([userId isEqualToString:[[NIMSDK sharedSDK].loginManager currentAccount]]) { self.callInfo.localRecording = NO; } if ([userId isEqualToString:self.peerUid]) { self.callInfo.otherSideRecording = NO; } } if (error.code == NIMAVLocalErrorCodeRecordWillStopForLackSpace) { _recordWillStopForLackSpace = YES; } } - (void)onRecordStopped:(UInt64)callID fileURL:(NSURL *)fileURL uid:(NSString *)userId { if (self.callInfo.callID == callID) { if ([userId isEqualToString:[[NIMSDK sharedSDK].loginManager currentAccount]]) { self.callInfo.localRecording = NO; } if ([userId isEqualToString:self.peerUid]) { self.callInfo.otherSideRecording = NO; } //辅助验证: 写到系统相册 [self requestAuthorizationSaveToPhotosAlbum:fileURL]; } } #pragma mark - NTESRecordSelectViewDelegate - (void)onRecordWithAudioConversation:(BOOL)audioConversationOn myMedia:(BOOL)myMediaOn otherSideMedia:(BOOL)otherSideMediaOn { //子类重写 } #pragma mark - M80TimerHolderDelegate - (void)onNTESTimerFired:(NTESTimerHolder *)holder { if (holder == self.diskCheckTimer) { [self checkFreeDiskSpace]; } else if (holder == self.calleeResponseTimer) { if (!_calleeResponsed) { [self.navigationController.view makeToast:@"接听超时" duration:2 position:CSToastPositionCenter]; [self response:NO]; } } } #pragma mark - Misc - (void)checkServiceEnable:(void (^)(BOOL))result { if ([[AVAudioSession sharedInstance] respondsToSelector:@selector(requestRecordPermission:)]) { [[AVAudioSession sharedInstance] performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) { dispatch_async_main_safe(^{ if (granted) { NSString *mediaType = AVMediaTypeVideo; AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType]; if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"相机权限受限,无法视频聊天" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alert showAlertWithCompletionHandler:^(NSInteger idx) { if (result) { result(NO); } }]; } else { if (result) { result(YES); } } } else { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"麦克风权限受限,无法聊天" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil]; [alert showAlertWithCompletionHandler:^(NSInteger idx) { if (result) { result(NO); } }]; } }); }]; } } - (UIStatusBarStyle)preferredStatusBarStyle { return UIStatusBarStyleLightContent; } - (void)requestAuthorizationSaveToPhotosAlbum:(NSURL *)fileURL { __weak typeof(self) wself = self; if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) { [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { if (status == PHAuthorizationStatusRestricted || status == PHAuthorizationStatusDenied) { //没有权限 dispatch_async_main_safe(^{ [wself.navigationController.view makeToast:@"保存失败,没有相册权限" duration:3 position:CSToastPositionCenter]; }); } else if (status == PHAuthorizationStatusAuthorized) { //有权限 [wself saveToPhotosAlbum:fileURL]; } }]; } else { [self saveToPhotosAlbum:fileURL]; } } - (void)saveToPhotosAlbum:(NSURL *)fileURL { if (fileURL) { __weak typeof(self) wself = self; if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) { ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; [library writeVideoAtPathToSavedPhotosAlbum:fileURL completionBlock:^(NSURL *assetURL, NSError *error) { [wself checkVideoSaveToAlbum:error]; }]; } else { [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:fileURL]; } completionHandler:^(BOOL success, NSError *_Nullable error) { dispatch_async_main_safe(^{ [wself checkVideoSaveToAlbum:error]; }); }]; } } } - (void)checkVideoSaveToAlbum:(NSError *)error { NSString *toast = _recordWillStopForLackSpace ? @"你的手机内存不足,录制已结束\n" : @"录制已结束\n"; if (error) { toast = [NSString stringWithFormat:@"%@保存至系统相册失败:%zd", toast, error.code]; } else { toast = [toast stringByAppendingString:@"录制文件已保存至系统相册"]; } if (!self.callInfo.localRecording && !self.callInfo.otherSideRecording && _successRecords == 1) { [self.navigationController.view makeToast:toast duration:3 position:CSToastPositionCenter]; } _successRecords--; } - (void)checkFreeDiskSpace { if (self.callInfo.localRecording) { uint64_t freeSpace = 1000 * MB; NSError *error; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSDictionary *attrbites = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error:&error]; if (attrbites) { NSNumber *freeFileSystemSizeInBytes = [attrbites objectForKey:NSFileSystemFreeSize]; freeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue]; [self udpateLowSpaceWarning:(freeSpace < FreeDiskSpaceWarningThreshold)]; } } } - (void)fillUserSetting:(NIMNetCallOption *)option { option.autoRotateRemoteVideo = [[NTESBundleSetting sharedConfig] videochatAutoRotateRemoteVideo]; option.serverRecordAudio = [[NTESBundleSetting sharedConfig] serverRecordAudio]; option.serverRecordVideo = [[NTESBundleSetting sharedConfig] serverRecordVideo]; option.preferredVideoEncoder = [[NTESBundleSetting sharedConfig] perferredVideoEncoder]; option.preferredVideoDecoder = [[NTESBundleSetting sharedConfig] perferredVideoDecoder]; option.videoMaxEncodeBitrate = [[NTESBundleSetting sharedConfig] videoMaxEncodeKbps] * 1000; option.autoDeactivateAudioSession = [[NTESBundleSetting sharedConfig] autoDeactivateAudioSession]; option.audioDenoise = [[NTESBundleSetting sharedConfig] audioDenoise]; option.voiceDetect = [[NTESBundleSetting sharedConfig] voiceDetect]; option.audioHowlingSuppress = [[NTESBundleSetting sharedConfig] audioHowlingSuppress]; option.preferHDAudio = [[NTESBundleSetting sharedConfig] preferHDAudio]; option.scene = [[NTESBundleSetting sharedConfig] scene]; NIMNetCallVideoCaptureParam *param = [[NIMNetCallVideoCaptureParam alloc] init]; [self fillVideoCaptureSetting:param]; option.videoCaptureParam = param; } - (void)fillVideoCaptureSetting:(NIMNetCallVideoCaptureParam *)param { param.preferredVideoQuality = [[NTESBundleSetting sharedConfig] preferredVideoQuality]; param.videoCrop = [[NTESBundleSetting sharedConfig] videochatVideoCrop]; param.startWithBackCamera = [[NTESBundleSetting sharedConfig] startWithBackCamera]; } #pragma mark - Ring //铃声 - 正在呼叫请稍后 - (void)playConnnetRing { [self.player stop]; NSURL *url = [[NSBundle mainBundle] URLForResource:@"video_connect_chat_tip_sender" withExtension:@"aac"]; self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; [self.player play]; } //铃声 - 对方暂时无法接听 - (void)playHangUpRing { [self.player stop]; NSURL *url = [[NSBundle mainBundle] URLForResource:@"video_chat_tip_HangUp" withExtension:@"aac"]; self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; [self.player play]; } //铃声 - 对方正在通话中 - (void)playOnCallRing { [self.player stop]; NSURL *url = [[NSBundle mainBundle] URLForResource:@"video_chat_tip_OnCall" withExtension:@"aac"]; self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; [self.player play]; } //铃声 - 对方无人接听 - (void)playTimeoutRing { [self.player stop]; NSURL *url = [[NSBundle mainBundle] URLForResource:@"video_chat_tip_onTimer" withExtension:@"aac"]; self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; [self.player play]; } //铃声 - 接收方铃声 - (void)playReceiverRing { [self.player stop]; NSURL *url = [[NSBundle mainBundle] URLForResource:@"video_chat_tip_receiver" withExtension:@"aac"]; self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; self.player.numberOfLoops = 20; [self.player play]; } //铃声 - 拨打方铃声 - (void)playSenderRing { [self.player stop]; NSURL *url = [[NSBundle mainBundle] URLForResource:@"video_chat_tip_sender" withExtension:@"aac"]; self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil]; self.player.numberOfLoops = 20; [self.player play]; } - (void)startChat { NSString *now = [Helper getCurrentTimes]; NSString *str = [NSString stringWithFormat:@"%@order?action=doBegVChat&pk=%@&modelpk=%@&begdate=%@&hasanswer=0", imageURl, [Helper sharedAccount].accid, self.modelPk, now]; NSLog(@"str========%@", str); [[AHHttpManager sharedManager] POST:[str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] parameters:nil success:^(id responseObject) { NSLog(@"%@", responseObject); if ([[responseObject objectForKey:@"msg"] isEqualToString:@"success"]) { } else { //[MBProgressHUD showTextHUD:@"超时" inView:self.view hideAfterDelay:1]; } } failure:^(NSError *error) { NSLog(@"error = %@", error); //[MBProgressHUD showTextHUD:@"超时" inView:self.view hideAfterDelay:1]; }]; } @end