930 lines
32 KiB
Objective-C
Executable File
930 lines
32 KiB
Objective-C
Executable File
//
|
||
// 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 "Toast+UIView.h"
|
||
#import "NTESTimerHolder.h"
|
||
#import "NetCallChatInfo.h"
|
||
#import "NTESBundleSetting.h"
|
||
#import <AVFoundation/AVFoundation.h>
|
||
#import <AssetsLibrary/ALAssetsLibrary.h>
|
||
#import <Photos/Photos.h>
|
||
#import "UIView+NTES.h"
|
||
|
||
//十秒之后如果还是没有收到对方响应的control字段,则自己发起一个假的control,用来激活铃声并自己先进入房间
|
||
#define DelaySelfStartControlTime 10
|
||
//激活铃声后无人接听的超时时间
|
||
#define NoBodyResponseTimeOut 40
|
||
|
||
//周期性检查剩余磁盘空间
|
||
#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
|
||
|
||
NTES_FORBID_INTERACTIVE_POP
|
||
|
||
- (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 = [[NetCallChatInfo alloc] init];
|
||
}
|
||
_timer = [[NTESTimerHolder alloc] init];
|
||
_diskCheckTimer = [[NTESTimerHolder alloc] init];
|
||
//防止应用在后台状态,此时呼入,会走init但是不会走viewDidLoad,此时呼叫方挂断,导致被叫监听不到,界面无法消去的问题。
|
||
id<NIMNetCallManager> manager = [NIMAVChatSDK sharedSDK].netCallManager;
|
||
[manager addDelegate:self];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void)dealloc{
|
||
|
||
[[NIMAVChatSDK sharedSDK].netCallManager removeDelegate:self];
|
||
}
|
||
|
||
- (void)viewDidLoad {
|
||
[super viewDidLoad];
|
||
__weak typeof(self) wself = self;
|
||
[self checkServiceEnable:^(BOOL result) {
|
||
if (result) {
|
||
[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];
|
||
[self setUpStatusBar:UIStatusBarStyleLightContent];
|
||
}
|
||
|
||
- (void)viewDidAppear:(BOOL)animated{
|
||
[super viewDidAppear:animated];
|
||
[UIApplication sharedApplication].idleTimerDisabled = YES;
|
||
}
|
||
|
||
- (void)viewWillDisappear:(BOOL)animated{
|
||
[super viewWillDisappear:animated];
|
||
[self.player stop];
|
||
[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) {
|
||
|
||
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.apnsSound = @"video_chat_tip_receiver.aac";
|
||
[self fillUserSetting:option];
|
||
|
||
__weak typeof(self) wself = self;
|
||
|
||
[[NIMAVChatSDK sharedSDK].netCallManager start:callees type:wself.callInfo.callType option:option completion:^(NSError *error, UInt64 callID) {
|
||
if (!error && wself) {
|
||
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{
|
||
if (error) {
|
||
[wself.navigationController.view makeToast:@"连接失败"
|
||
];
|
||
}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) {
|
||
[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:@"通话失败"
|
||
];
|
||
[wself hangup];
|
||
}
|
||
});
|
||
}else{
|
||
wself.chatRoom = nil;
|
||
[wself.navigationController.view makeToast:@"连接失败"
|
||
];
|
||
[wself dismiss:nil];
|
||
}
|
||
}];
|
||
//dismiss需要放在self后面,否在ios7下会有野指针
|
||
if (accept) {
|
||
[self waitForConnectiong];
|
||
}else{
|
||
[self dismiss:nil];
|
||
}
|
||
}
|
||
|
||
- (void)dismiss:(void (^)(void))completion{
|
||
//由于音视频聊天里头有音频和视频聊天界面的切换,直接用present的话页面过渡会不太自然,这里还是用push,然后做出present的效果
|
||
CATransition *transition = [CATransition animation];
|
||
transition.duration = 0.25;
|
||
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
|
||
transition.type = kCATransitionPush;
|
||
transition.subtype = kCATransitionFromBottom;
|
||
[self.navigationController.view.layer addAnimation:transition forKey:nil];
|
||
self.navigationController.navigationBarHidden = NO;
|
||
[self.navigationController popViewControllerAnimated:NO];
|
||
[self setUpStatusBar:UIStatusBarStyleDefault];
|
||
if (completion) {
|
||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(transition.duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||
completion();
|
||
});
|
||
}
|
||
}
|
||
|
||
- (void)onCalling{
|
||
//子类重写
|
||
}
|
||
|
||
- (void)waitForConnectiong{
|
||
//子类重写
|
||
}
|
||
|
||
-(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:@"开始录制失败"
|
||
];
|
||
}
|
||
else
|
||
{
|
||
[self.view makeToast:toastText
|
||
];
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
- (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:@"结束录制失败"
|
||
];
|
||
}
|
||
else
|
||
{
|
||
[self.view makeToast:toastText
|
||
];
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
-(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:@"无人接听"];
|
||
[wself dismiss:nil];
|
||
}
|
||
});
|
||
}
|
||
break;
|
||
}
|
||
case NIMNetCallControlTypeBusyLine: {
|
||
[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 dismiss:nil];
|
||
});
|
||
break;
|
||
}
|
||
case NIMNetCallControlTypeStartRecord:
|
||
[self.view makeToast:@"对方开始了录制"
|
||
];
|
||
break;
|
||
case NIMNetCallControlTypeStopRecord:
|
||
[self.view makeToast:@"对方结束了录制"
|
||
];
|
||
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:@"对方拒绝接听"
|
||
|
||
];
|
||
[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 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:@"已在其他端处理"
|
||
];
|
||
[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
|
||
{
|
||
|
||
if (self.callInfo.callID == callID) {
|
||
|
||
[self.view makeToast:[NSString stringWithFormat:@"录制发生错误: %zd", error.code]
|
||
];
|
||
|
||
if ([userId isEqualToString:[[NIMSDK sharedSDK].loginManager currentAccount]]) {
|
||
self.callInfo.localRecording = NO;
|
||
}
|
||
if ([userId isEqualToString:self.peerUid]) {
|
||
self.callInfo.otherSideRecording = NO;
|
||
}
|
||
|
||
}
|
||
|
||
if (error.code == NIMLocalErrorCodeRecordWillStopForLackSpace) {
|
||
_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:@"接听超时"];
|
||
[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);
|
||
}
|
||
}];
|
||
}
|
||
|
||
});
|
||
}];
|
||
}
|
||
}
|
||
|
||
- (void)setUpStatusBar:(UIStatusBarStyle)style{
|
||
[[UIApplication sharedApplication] setStatusBarStyle:style
|
||
animated:NO];
|
||
}
|
||
|
||
- (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:@"保存失败,没有相册权限"
|
||
];
|
||
});
|
||
}
|
||
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
|
||
];
|
||
}
|
||
_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.preferredVideoQuality = [[NTESBundleSetting sharedConfig] preferredVideoQuality];
|
||
option.disableVideoCropping = [[NTESBundleSetting sharedConfig] videochatDisableAutoCropping];
|
||
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.startWithBackCamera = [[NTESBundleSetting sharedConfig] startWithBackCamera];
|
||
option.autoDeactivateAudioSession = [[NTESBundleSetting sharedConfig] autoDeactivateAudioSession];
|
||
|
||
option.audioDenoise = [[NTESBundleSetting sharedConfig] audioDenoise];
|
||
option.voiceDetect = [[NTESBundleSetting sharedConfig] voiceDetect];
|
||
option.preferHDAudio = [[NTESBundleSetting sharedConfig] preferHDAudio];
|
||
|
||
}
|
||
|
||
#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];
|
||
}
|
||
|
||
|
||
|
||
@end
|