JXAVCallViewController.m 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. //
  2. // JXAVCallViewController.m
  3. // shiku_im
  4. //
  5. // Created by p on 2017/12/26.
  6. // Copyright © 2017年 Reese. All rights reserved.
  7. //
  8. #import "JXAVCallViewController.h"
  9. #import "JXMediaObject.h"
  10. //#import <ReplayKit/ReplayKit.h>
  11. #import <AssetsLibrary/AssetsLibrary.h>
  12. #import "JXSelectFriendsVC.h"
  13. #ifdef Live_Version
  14. #import "JXLiveJidManager.h"
  15. #endif
  16. #import "JXTalkViewController.h"
  17. #import "JXTalkModel.h"
  18. #import "AppleReachability.h"
  19. #import <JitsiMeet/JitsiMeetUserInfo.h>
  20. @interface JXAVCallViewController ()<UIAlertViewDelegate, JXTalkViewControllerDelegate>
  21. @property (nonatomic, assign) NSTimeInterval startTime;
  22. @property (nonatomic, strong) UIView *localVideoView;
  23. @property (nonatomic, strong) AVCaptureSession *session;
  24. @property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
  25. @property (nonatomic, strong) UIButton *suspensionBtn;
  26. @property (nonatomic, strong) UILabel *suspensionLabel;
  27. @property (nonatomic, assign) CGRect subWindowFrame;
  28. @property (nonatomic, strong) NSTimer *timer;
  29. @property (nonatomic, assign) int timerIndex;
  30. @property (nonatomic, strong) UIButton *recorderBtn;
  31. //@property (nonatomic, strong) RPPreviewViewController *previewVC;
  32. @property (nonatomic, assign) BOOL isRecording;
  33. @property (nonatomic, strong) NSTimer *pingTimer;
  34. @property (nonatomic, assign) int pingTimerIndex;
  35. @property (nonatomic, assign) int pingCount;
  36. @property (nonatomic, assign) BOOL isOldVersion;
  37. @property (nonatomic, strong) JXSelectFriendsVC *selVC;
  38. @property (nonatomic, strong) JXTalkViewController *talkVC;
  39. @property (nonatomic, assign) BOOL isMuted;
  40. @end
  41. @implementation JXAVCallViewController
  42. - (void)viewDidLoad {
  43. [super viewDidLoad];
  44. // Do any additional setup after loading the view from its nib.
  45. if (_pSelf) {
  46. return;
  47. }
  48. #if TARGET_IPHONE_SIMULATOR
  49. [self actionQuit];
  50. [JXMyTools showTipView:@"模拟器不支持音视频通话"];
  51. return;
  52. #endif
  53. g_meeting.isMeeting = YES;
  54. _pSelf = self;
  55. [self createSuspensionView];
  56. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  57. // [self creatLocalVideoView];
  58. JitsiMeetView *view = (JitsiMeetView *)self.view;
  59. view.delegate = self;
  60. // view.welcomePageEnabled = NO;
  61. // [view loadURL:nil];
  62. // NSString *url = [NSString stringWithFormat:@"https://www.pinba.co/%@",self.roomNum];
  63. BOOL audioMuted = NO;
  64. BOOL videoMuted = NO;
  65. NSString *serverStr;
  66. if (self.isGroup) {
  67. serverStr = g_config.jitsiServer;
  68. }else {
  69. if ([g_config.isOpenCluster integerValue] == 1 && [self.meetUrl length] > 0) {
  70. serverStr = self.meetUrl;
  71. }else {
  72. serverStr = g_config.jitsiServer;
  73. }
  74. }
  75. g_meeting.roomNum = self.roomNum;
  76. // NSString *url = [NSString stringWithFormat:@"%@%@",serverStr,self.roomNum];
  77. NSString *room;
  78. if (self.isAudio && self.isGroup) {
  79. // url = [NSString stringWithFormat:@"%@audio%@",serverStr,self.roomNum];
  80. if (self.isGroup) {
  81. room = [NSString stringWithFormat:@"audio%@",self.roomNum];
  82. }else{
  83. room = self.roomNum;
  84. }
  85. videoMuted = YES;
  86. }
  87. if (self.isTalk) {
  88. // url = [NSString stringWithFormat:@"%@talk%@",serverStr,self.roomNum];
  89. room = [NSString stringWithFormat:@"talk%@",self.roomNum];
  90. audioMuted = YES;
  91. }
  92. if (!room) {
  93. room = self.roomNum;
  94. }
  95. if (self.isAudio) {
  96. videoMuted = YES;
  97. }
  98. if (!_toUserName) {
  99. _toUserName = self.roomNum;
  100. }
  101. JitsiMeetUserInfo *userInfo = [[JitsiMeetUserInfo alloc] init];
  102. userInfo.displayName = _toUserName;
  103. NSString *avatarURL = [self getMyHeadUrl];
  104. if (avatarURL) {
  105. // view.avatarURL = avatarURL;
  106. userInfo.avatar = [NSURL URLWithString:avatarURL];
  107. }
  108. // [view loadURLObject:@{
  109. // @"config": @{
  110. // @"startWithAudioMuted": [NSNumber numberWithBool:audioMuted],
  111. // @"startWithVideoMuted": [NSNumber numberWithBool:videoMuted]
  112. // },
  113. // @"nickName" : _toUserName,
  114. // @"isCallKit" : g_App.uuid ? @NO : @YES,
  115. // @"url": url
  116. // /*@"url" : @"https://192.168.0.47/123"*/
  117. //
  118. // }];
  119. JitsiMeetConferenceOptions *options = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
  120. builder.welcomePageEnabled = NO;
  121. builder.serverURL = [NSURL URLWithString:serverStr];;
  122. builder.room = room;
  123. builder.audioOnly = NO;
  124. builder.audioMuted = audioMuted;
  125. builder.videoMuted = videoMuted;
  126. builder.userInfo = userInfo;
  127. builder.subject = _toUserName;
  128. [builder setFeatureFlag:@"chat.enabled" withBoolean:false];
  129. }];
  130. [view join:options];
  131. UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(20, 30, 38, 38)];
  132. [btn setImage:[UIImage imageNamed:@"callHide"] forState:UIControlStateNormal];
  133. [btn addTarget:self action:@selector(hideAudioView) forControlEvents:UIControlEventTouchUpInside];
  134. [self.view addSubview:btn];
  135. _recorderBtn = [[UIButton alloc] initWithFrame:CGRectMake(JX_SCREEN_WIDTH - 20 - 38, 30, 30, 30)];
  136. // [_recorderBtn setTitle:Localized(@"JX_Recording") forState:UIControlStateNormal];
  137. // [_recorderBtn setTitle:Localized(@"STOP_IT") forState:UIControlStateSelected];
  138. [_recorderBtn setImage:[UIImage imageNamed:@"audio_invite"] forState:UIControlStateNormal];
  139. // [_recorderBtn setImage:[UIImage imageNamed:@"stoped"] forState:UIControlStateSelected];
  140. // [_recorderBtn setImageEdgeInsets:UIEdgeInsetsMake(0, 0, 22, 0)];
  141. // [_recorderBtn setTitleEdgeInsets:UIEdgeInsetsMake(30, -60, 0, 0)];
  142. [_recorderBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  143. [_recorderBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
  144. [_recorderBtn setTitleColor:[UIColor grayColor] forState:UIControlStateDisabled];
  145. _recorderBtn.titleLabel.font = [UIFont systemFontOfSize:15.0];
  146. [_recorderBtn addTarget:self action:@selector(onInvite) forControlEvents:UIControlEventTouchUpInside];
  147. [self.view addSubview:_recorderBtn];
  148. if (self.isGroup) {
  149. _recorderBtn.hidden = NO;
  150. }else {
  151. _recorderBtn.hidden = YES;
  152. }
  153. if (self.isTalk) {
  154. [self createTalkView];
  155. }
  156. });
  157. [g_notify addObserver:self selector:@selector(newMsgCome:) name:kXMPPNewMsgNotifaction object:nil];
  158. [g_notify addObserver:self selector:@selector(callEndNotification:) name:kCallEndNotification object:nil];
  159. _startTime = 0;
  160. [self networkStatusChange];
  161. self.isOldVersion = YES;
  162. if (!self.isGroup) {
  163. self.pingCount = 0;
  164. [self sendPing];
  165. self.pingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(pingTimerAction:) userInfo:nil repeats:YES];
  166. }
  167. }
  168. - (void)createTalkView {
  169. _talkVC = [[JXTalkViewController alloc] init];
  170. _talkVC.delegate = self;
  171. _talkVC.roomNum = self.roomNum;
  172. [self.view addSubview:_talkVC.view];
  173. JXMessageObject *msg=[[JXMessageObject alloc]init];
  174. msg.content = nil;
  175. msg.timeSend = [NSDate date];
  176. msg.fromUserId = MY_USER_ID;
  177. msg.fromUserName = MY_USER_NAME;
  178. msg.toUserId = self.roomNum;
  179. msg.isGroup = YES;
  180. msg.type = [NSNumber numberWithInt:kWCMessageTypeTalkJoin];
  181. msg.isSend = [NSNumber numberWithInt:transfer_status_ing];
  182. msg.isRead = [NSNumber numberWithBool:NO];
  183. msg.isReadDel = [NSNumber numberWithInt:NO];
  184. msg.sendCount = 1;
  185. msg.objectId = self.roomNum;
  186. [msg insert:nil];
  187. [g_xmpp sendMessage:msg roomName:self.roomNum];//发送消息
  188. }
  189. - (void)talkVCCloseBtnAction {
  190. [self.pingTimer invalidate];
  191. self.pingTimer = nil;
  192. [_talkVC actionQuit];
  193. [self actionQuit];
  194. }
  195. - (void)talkVCTalkStart {
  196. JitsiMeetView *view = (JitsiMeetView *)self.view;
  197. [view setAudioMuted:NO];
  198. }
  199. - (void)talkVCTalkStop {
  200. JitsiMeetView *view = (JitsiMeetView *)self.view;
  201. [view setAudioMuted:YES];
  202. }
  203. - (NSString *)getMyHeadUrl {
  204. NSString* s;
  205. if([MY_USER_ID isKindOfClass:[NSNumber class]])
  206. s = [(NSNumber*)MY_USER_ID stringValue];
  207. else
  208. s = MY_USER_ID;
  209. if([s length]<=0)
  210. return nil;
  211. NSString* dir = [NSString stringWithFormat:@"%d",[s intValue] % 10000];
  212. NSString* url = [NSString stringWithFormat:@"%@avatar/o/%@/%@.jpg",g_config.downloadAvatarUrl,dir,s];
  213. return url;
  214. }
  215. - (void)pingTimerAction:(NSTimer *)timer {
  216. self.pingTimerIndex ++;
  217. if (self.pingTimerIndex >= 3) {
  218. self.pingTimerIndex = 0;
  219. [self sendPing];
  220. if (self.pingCount >= 0) {
  221. self.pingCount ++;
  222. }else {
  223. [timer invalidate];
  224. timer = nil;
  225. return;
  226. }
  227. NSLog(@"timerPingCount = %d", self.pingCount);
  228. if (self.pingCount >= 10) {
  229. self.pingCount = -1;
  230. [timer invalidate];
  231. timer = nil;
  232. if (self.isOldVersion) {
  233. return;
  234. }
  235. if (g_xmpp.isLogined == login_status_yes) {
  236. if (!self.isGroup) {
  237. // int n = [[NSDate date] timeIntervalSince1970]-_startTime;
  238. int type = kWCMessageTypeVideoChatEnd;
  239. if (self.isAudio) {
  240. type = kWCMessageTypeAudioChatEnd;
  241. }
  242. [g_meeting sendEnd:type toUserId:self.toUserId toUserName:self.toUserName timeLen:self.timerIndex];
  243. }
  244. [self actionQuit];
  245. [g_App showAlert:Localized(@"JX_AVAbnormalDisconnection") delegate:self tag:2457 onlyConfirm:YES];
  246. }else {
  247. [self actionQuit];
  248. }
  249. }
  250. }
  251. }
  252. - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
  253. dispatch_async(dispatch_get_main_queue(), ^{
  254. // if (!self.isGroup) {
  255. // // int n = [[NSDate date] timeIntervalSince1970]-_startTime;
  256. // int type = kWCMessageTypeVideoChatEnd;
  257. // if (self.isAudio) {
  258. // type = kWCMessageTypeAudioChatEnd;
  259. // }
  260. // [g_meeting sendEnd:type toUserId:self.toUserId toUserName:self.toUserName timeLen:self.timerIndex];
  261. // }
  262. [self actionQuit];
  263. });
  264. }
  265. - (void)sendPing {
  266. JXMessageObject *msg=[[JXMessageObject alloc]init];
  267. msg.timeSend = [NSDate date];
  268. msg.fromUserId = MY_USER_ID;
  269. msg.fromUserName = MY_USER_NAME;
  270. msg.toUserName = self.toUserName;
  271. msg.toUserId = self.toUserId;
  272. msg.isGroup = NO;
  273. msg.type = [NSNumber numberWithInt:kWCMessageTypeAVPing];
  274. msg.isSend = [NSNumber numberWithInt:transfer_status_ing];
  275. msg.isRead = [NSNumber numberWithBool:NO];
  276. [g_xmpp sendMessage:msg roomName:nil];//发送消息
  277. }
  278. -(void)onInvite{
  279. JXUserObject *user = [[JXUserObject sharedInstance] getUserById:self.roomNum];
  280. NSDictionary * groupDict = [user toDictionary];
  281. roomData * roomdata = [[roomData alloc] init];
  282. [roomdata getDataFromDict:groupDict];
  283. NSMutableSet* p = [[NSMutableSet alloc]init];
  284. _selVC = [JXSelectFriendsVC alloc];
  285. _selVC.isAddWindow = YES;
  286. _selVC.isNewRoom = NO;
  287. _selVC.isShowMySelf = NO;
  288. if (self.isGroup && [self.roomNum isEqualToString:MY_USER_ID]) {
  289. _selVC.type = JXSelectFriendTypeSelFriends;
  290. }else {
  291. _selVC.type = JXSelectFriendTypeSelMembers;
  292. }
  293. _selVC.room = roomdata;
  294. _selVC.existSet = p;
  295. _selVC.delegate = self;
  296. _selVC.didSelect = @selector(meetingAddMember:);
  297. _selVC = [_selVC init];
  298. [g_window addSubview:_selVC.view];
  299. // [g_navigation pushViewController:vc animated:YES];
  300. }
  301. -(void)meetingAddMember:(JXSelectFriendsVC*)vc{
  302. int type;
  303. if (self.isAudio) {
  304. type = kWCMessageTypeAudioMeetingInvite;
  305. }else {
  306. type = kWCMessageTypeVideoMeetingInvite;
  307. }
  308. for(NSNumber* n in vc.set){
  309. if (self.isGroup && [self.roomNum isEqualToString:MY_USER_ID]) {
  310. JXUserObject *user;
  311. if (vc.seekTextField.text.length > 0) {
  312. user = vc.searchArray[[n intValue] % 100000-1];
  313. }else{
  314. user = [[vc.letterResultArr objectAtIndex:[n intValue] / 100000-1] objectAtIndex:[n intValue] % 100000-1];
  315. }
  316. NSString* s = [NSString stringWithFormat:@"%@",user.userId];
  317. [g_meeting sendMeetingInvite:s toUserName:user.userNickname roomJid:self.roomNum callId:@"0" type:type];
  318. }else {
  319. memberData *user;
  320. if (vc.seekTextField.text.length > 0) {
  321. user = vc.searchArray[[n intValue] % 100000-1];
  322. }else{
  323. user = [[vc.letterResultArr objectAtIndex:[n intValue] / 100000-1] objectAtIndex:[n intValue] % 100000-1];
  324. }
  325. NSString* s = [NSString stringWithFormat:@"%ld",user.userId];
  326. [g_meeting sendMeetingInvite:s toUserName:user.userName roomJid:self.roomNum callId:@"0" type:type];
  327. }
  328. }
  329. }
  330. //- (void)recorderBtnAction:(UIButton *)btn {
  331. //
  332. // if (!btn.selected) {
  333. // self.isRecording = NO;
  334. // //如果还没有开始录制,判断系统是否支持
  335. // if ([RPScreenRecorder sharedRecorder].isAvailable && [[UIDevice currentDevice].systemVersion floatValue] > 9.0) {
  336. // //如果支持,就使用下面的方法可以启动录制回放
  337. // [btn setTitle:Localized(@"JX_Opening") forState:UIControlStateDisabled];
  338. // btn.enabled = NO;
  339. // [self startRecord];
  340. //
  341. // } else {
  342. // [JXMyTools showTipView:Localized(@"JX_NotScreenRecording")];
  343. // }
  344. // }else {
  345. // [btn setTitle:Localized(@"JX_Stopping") forState:UIControlStateDisabled];
  346. // btn.enabled = NO;
  347. // [self stopRecord];
  348. // }
  349. //}
  350. //- (void)startRecord {
  351. // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  352. // if (!self.isRecording) {
  353. //
  354. // [self startRecord];
  355. // }
  356. // });
  357. // NSLog(@"recorder -- OK");
  358. // [[RPScreenRecorder sharedRecorder] startRecordingWithMicrophoneEnabled:YES handler:^(NSError * _Nullable error) {
  359. // NSLog(@"%@", error);
  360. // if (!error) {
  361. // NSLog(@"recorder -- 已开启");
  362. // self.isRecording = YES;
  363. // dispatch_async(dispatch_get_main_queue(), ^{
  364. // _recorderBtn.enabled = YES;
  365. // _recorderBtn.selected = YES;
  366. // });
  367. // }
  368. // //处理发生的错误,如设用户权限原因无法开始录制等
  369. // }];
  370. //}
  371. //- (void)stopRecord {
  372. // dispatch_async(dispatch_get_main_queue(), ^{
  373. //
  374. // NSLog(@"stopRecord");
  375. //
  376. // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  377. // if (self.isRecording) {
  378. //
  379. // dispatch_async(dispatch_get_main_queue(), ^{
  380. // _recorderBtn.enabled = YES;
  381. // _recorderBtn.selected = NO;
  382. // });
  383. //
  384. // [JXMyTools showTipView:@"录屏失败,请重新录制"];
  385. //
  386. //// [self stopRecord];
  387. // }
  388. // });
  389. //
  390. // //停止录制回放,并显示回放的预览,在预览中用户可以选择保存视频到相册中、放弃、或者分享出去
  391. // [[RPScreenRecorder sharedRecorder] stopRecordingWithHandler:^(RPPreviewViewController * _Nullable previewViewController, NSError * _Nullable error) {
  392. // _previewVC = previewViewController;
  393. //
  394. // self.isRecording = NO;
  395. // dispatch_async(dispatch_get_main_queue(), ^{
  396. // _recorderBtn.enabled = YES;
  397. // _recorderBtn.selected = NO;
  398. // });
  399. //
  400. // NSLog(@"recorder -- stop");
  401. // if (error) {
  402. // NSLog(@"recorder -- errro:%@", error);
  403. // //处理发生的错误,如磁盘空间不足而停止等
  404. // }else {
  405. // NSURL *url = [_previewVC valueForKey:@"movieURL"];
  406. //
  407. //
  408. //// [[NSFileManager defaultManager] copyItemAtURL:url toURL:[NSURL URLWithString:str] error:nil];
  409. ////// [[NSFileManager defaultManager] moveItemAtURL:url toURL:[NSURL URLWithString:str] error:nil];
  410. ////
  411. //// NSString *str = [FileInfo getUUIDFileName:@"mp4"];
  412. //// JXMediaObject* p = [[JXMediaObject alloc]init];
  413. //// p.userId = g_server.myself.userId;
  414. //// p.fileName = str;
  415. //// p.isVideo = [NSNumber numberWithBool:YES];
  416. //// // p.timeLen = [NSNumber numberWithInteger:timeLen];
  417. //// [p insert];
  418. //
  419. // ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
  420. // [library writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) {
  421. //
  422. // if (error) {
  423. // [JXMyTools showTipView:Localized(@"JX_SaveFiled")];
  424. // }else {
  425. // [JXMyTools showTipView:Localized(@"JX_SaveSuessed")];
  426. // }
  427. // }];
  428. //
  429. //// if (_previewVC) {
  430. //// //设置预览页面到代理
  431. //// _previewVC.previewControllerDelegate = self;
  432. ////
  433. //// [g_window addSubview:_previewVC.view];
  434. //// [g_navigation.subViews.lastObject presentViewController:previewViewController animated:YES completion:nil];
  435. //// }
  436. //
  437. // }
  438. //
  439. // }];
  440. // });
  441. //}
  442. //- (void)previewControllerDidFinish:(RPPreviewViewController *)previewController {
  443. // [_previewVC dismissViewControllerAnimated:YES completion:nil];
  444. //}
  445. - (void)creatLocalVideoView {
  446. self.localVideoView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
  447. self.localVideoView.backgroundColor = HEXCOLOR(0x1F2025);
  448. [g_window addSubview:self.localVideoView];
  449. // 获取需要的设备
  450. AVCaptureDevice *device = [self cameraWithPosition:AVCaptureDevicePositionFront];
  451. if (self.isAudio || !device) {
  452. UIImageView *headImage = [[UIImageView alloc] initWithFrame:CGRectMake(JX_SCREEN_WIDTH / 2 - 50, JX_SCREEN_HEIGHT / 2 - 110, 100, 100)];
  453. headImage.layer.cornerRadius = headImage.frame.size.width / 2;
  454. headImage.layer.masksToBounds = YES;
  455. headImage.image = [UIImage imageNamed:@"ALOGO_1200"];
  456. [self.localVideoView addSubview:headImage];
  457. }else {
  458. NSError *error = nil;
  459. // 初始化会话
  460. _session = [[AVCaptureSession alloc] init];
  461. AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device
  462. error:&error];
  463. [_session addInput:input];
  464. [_session startRunning];
  465. //预览层的生成,实时获取摄像头数据
  466. self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_session];
  467. self.previewLayer.frame = [UIScreen mainScreen].bounds;
  468. self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
  469. [self.localVideoView.layer addSublayer:self.previewLayer];
  470. }
  471. UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, JX_SCREEN_HEIGHT / 2, JX_SCREEN_WIDTH, 20)];
  472. label.font = g_factory.font17;
  473. label.text = Localized(@"JX_Connection");
  474. label.textColor = [UIColor whiteColor];
  475. label.textAlignment = NSTextAlignmentCenter;
  476. [self.localVideoView addSubview:label];
  477. }
  478. - (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position
  479. {
  480. NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
  481. for ( AVCaptureDevice *device in devices )
  482. if ( device.position == position )
  483. return device;
  484. return nil;
  485. }
  486. - (void)createSuspensionView {
  487. _suspensionBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 80, 100)];
  488. _suspensionBtn.backgroundColor = [UIColor whiteColor];
  489. _suspensionBtn.layer.cornerRadius = 2.0;
  490. _suspensionBtn.layer.masksToBounds = YES;
  491. _suspensionBtn.layer.borderWidth = 0.5;
  492. _suspensionBtn.layer.borderColor = [[UIColor grayColor] CGColor];
  493. [_suspensionBtn addTarget:self action:@selector(showAudioView) forControlEvents:UIControlEventTouchUpInside];
  494. g_subWindow.frame = CGRectMake(JX_SCREEN_WIDTH - 80 - 10, 50, _suspensionBtn.frame.size.width, _suspensionBtn.frame.size.height);
  495. g_subWindow.backgroundColor = [UIColor cyanColor];
  496. [g_subWindow addSubview:_suspensionBtn];
  497. g_subWindow.hidden = YES;
  498. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)];
  499. [g_subWindow addGestureRecognizer:pan];
  500. UIImageView *suspensionImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
  501. suspensionImage.image = [UIImage imageNamed:@"callShow"];
  502. suspensionImage.center = CGPointMake(_suspensionBtn.frame.size.width / 2, _suspensionBtn.frame.size.height / 2 - 10);
  503. [_suspensionBtn addSubview:suspensionImage];
  504. _suspensionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(suspensionImage.frame) + 5, _suspensionBtn.frame.size.width, 20)];
  505. _suspensionLabel.textColor = THEMECOLOR;
  506. _suspensionLabel.textAlignment = NSTextAlignmentCenter;
  507. _suspensionLabel.font = g_factory.font13;
  508. _suspensionLabel.text = @"00:00";
  509. [_suspensionBtn addSubview:_suspensionLabel];
  510. }
  511. - (void)panAction:(UIPanGestureRecognizer *)pan {
  512. if (pan.state == UIGestureRecognizerStateBegan) {
  513. self.subWindowFrame = g_subWindow.frame;
  514. }
  515. CGPoint offset = [pan translationInView:g_App.window];
  516. CGPoint offset1 = [pan translationInView:g_subWindow];
  517. NSLog(@"pan - offset = %@, offset1 = %@", NSStringFromCGPoint(offset), NSStringFromCGPoint(offset1));
  518. CGRect frame = self.subWindowFrame;
  519. frame.origin.x += offset.x;
  520. frame.origin.y += offset.y;
  521. g_subWindow.frame = frame;
  522. if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
  523. if (frame.origin.x <= JX_SCREEN_WIDTH / 2) {
  524. frame.origin.x = 10;
  525. }else {
  526. frame.origin.x = JX_SCREEN_WIDTH - frame.size.width - 10;
  527. }
  528. if (frame.origin.y < 0) {
  529. frame.origin.y = 10;
  530. }
  531. if ((frame.origin.y + frame.size.height) > JX_SCREEN_HEIGHT) {
  532. frame.origin.y = JX_SCREEN_HEIGHT - frame.size.height - 10;
  533. }
  534. [UIView animateWithDuration:0.5 animations:^{
  535. g_subWindow.frame = frame;
  536. }];
  537. }
  538. }
  539. - (void)callTimerAction:(NSTimer *)timer {
  540. self.timerIndex ++;
  541. NSString *str = [NSString stringWithFormat:@"%.2d:%.2d", self.timerIndex / 60,self.timerIndex % 60];
  542. self.suspensionLabel.text = str;
  543. }
  544. - (void)hideAudioView {
  545. [UIView animateWithDuration:0.5 animations:^{
  546. self.view.frame = CGRectMake(JX_SCREEN_WIDTH, 0, self.view.frame.size.width, 0);
  547. } completion:^(BOOL finished) {
  548. g_subWindow.hidden = NO;
  549. self.view.hidden = YES;
  550. [[UIApplication sharedApplication] setStatusBarHidden:NO];
  551. }];
  552. }
  553. - (void)showAudioView {
  554. g_subWindow.hidden = YES;
  555. self.view.hidden = NO;
  556. self.view.frame = CGRectMake(JX_SCREEN_WIDTH, 0, self.view.frame.size.width, 0);
  557. [UIView animateWithDuration:0.5 animations:^{
  558. self.view.frame = CGRectMake(0, 0, JX_SCREEN_WIDTH, JX_SCREEN_HEIGHT);
  559. } completion:^(BOOL finished) {
  560. [[UIApplication sharedApplication] setStatusBarHidden:YES];
  561. }];
  562. }
  563. -(void)newMsgCome:(NSNotification *)notifacation{
  564. JXMessageObject *msg = (JXMessageObject *)notifacation.object;
  565. if ([msg.type intValue] == kWCMessageTypeVideoChatEnd || [msg.type intValue] == kWCMessageTypeAudioChatEnd || [msg.type intValue] == kWCMessageTypeAudioChatCancel || [msg.type intValue] == kWCMessageTypeVideoChatCancel) {
  566. if ([msg.fromUserId isEqualToString:self.toUserId]) {
  567. [self actionQuit];
  568. }
  569. }
  570. if ([msg.type intValue] == kWCMessageTypeAVPing) {
  571. if ([msg.fromUserId isEqualToString:self.toUserId]) {
  572. self.isOldVersion = NO;
  573. self.pingCount = 0;
  574. NSLog(@"timerPingCountNew = %d",self.pingCount);
  575. }
  576. }
  577. if ([msg.fromUserId isEqualToString:MY_USER_ID]) {
  578. if ([msg.type intValue] == kWCMessageTypeAudioChatAsk || [msg.type intValue] == kWCMessageTypeVideoChatAsk || [msg.type intValue] == kWCMessageTypeVideoMeetingInvite || [msg.type intValue] == kWCMessageTypeAudioMeetingInvite || [msg.type intValue] == kWCMessageTypeAudioChatAccept || [msg.type intValue] == kWCMessageTypeVideoChatAccept) {
  579. return;
  580. }
  581. }
  582. if(msg==nil)
  583. return;
  584. #ifdef Live_Version
  585. if([[JXLiveJidManager shareArray] contains:msg.toUserId] || [[JXLiveJidManager shareArray] contains:msg.fromUserId])
  586. return;
  587. #endif
  588. if([msg.toUserId isEqualToString:MY_USER_ID] && [g_meeting.roomNum isEqualToString:msg.fromUserId]){
  589. if([msg.type intValue] == kWCMessageTypeVideoChatAsk || [msg.type intValue] == kWCMessageTypeAudioChatAsk){
  590. [self actionQuit];
  591. }
  592. }
  593. }
  594. // 监听网络状态
  595. - (void)networkStatusChange {
  596. AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
  597. [manager startMonitoring];
  598. [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
  599. if (status == AFNetworkReachabilityStatusNotReachable) {
  600. [self performSelector:@selector(networkNotReachable) withObject:nil afterDelay:8];
  601. }
  602. }];
  603. }
  604. - (void)networkNotReachable {
  605. AppleReachability *reach = [AppleReachability reachabilityWithHostName:@"www.apple.com"];
  606. NetworkStatus internetStatus = [reach currentReachabilityStatus];
  607. switch (internetStatus) {
  608. case NotReachable:{
  609. [self actionQuit];
  610. }
  611. break;
  612. default:
  613. break;
  614. }
  615. }
  616. -(void)callEndNotification:(NSNotification *)notifacation{
  617. if (self.timerIndex == 5) {
  618. return;
  619. }
  620. if (!self.isGroup) {
  621. // int n = [[NSDate date] timeIntervalSince1970]-_startTime;
  622. int type = kWCMessageTypeVideoChatEnd;
  623. if (self.isAudio) {
  624. type = kWCMessageTypeAudioChatEnd;
  625. }
  626. [g_meeting sendEnd:type toUserId:self.toUserId toUserName:self.toUserName timeLen:self.timerIndex];
  627. }
  628. [self actionQuit];
  629. }
  630. void _onJitsiMeetViewDelegateEvent(NSString *name, NSDictionary *data) {
  631. NSLog(
  632. @"[%s:%d] JitsiMeetViewDelegate %@ %@",
  633. __FILE__, __LINE__, name, data);
  634. }
  635. - (void)_onJitsiMeetViewDelegateEvent:(NSString *)name
  636. withData:(NSDictionary *)data {
  637. NSLog(
  638. @"[%s:%d] JitsiMeetViewDelegate %@ %@",
  639. __FILE__, __LINE__, name, data);
  640. NSAssert(
  641. [NSThread isMainThread],
  642. @"JitsiMeetViewDelegate %@ method invoked on a non-main thread",
  643. name);
  644. }
  645. //- (void)conferenceFailed:(NSDictionary *)data {
  646. // _onJitsiMeetViewDelegateEvent(@"CONFERENCE_FAILED", data);
  647. //
  648. // dispatch_async(dispatch_get_main_queue(), ^{
  649. //// _startTime = [[NSDate date] timeIntervalSince1970];
  650. // self.session = nil;
  651. // self.localVideoView.hidden = YES;
  652. // [self.previewLayer removeFromSuperlayer];
  653. // [self.localVideoView removeFromSuperview];
  654. // self.localVideoView = nil;
  655. // });
  656. //}
  657. - (void)conferenceJoined:(NSDictionary *)data {
  658. _onJitsiMeetViewDelegateEvent(@"CONFERENCE_JOINED", data);
  659. dispatch_async(dispatch_get_main_queue(), ^{
  660. if (!g_meeting.isMeeting) {
  661. [self actionQuit];
  662. return;
  663. }
  664. _startTime = [[NSDate date] timeIntervalSince1970];
  665. self.timerIndex = 0;
  666. // 通话计时
  667. self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(callTimerAction:) userInfo:nil repeats:YES];
  668. self.session = nil;
  669. self.localVideoView.hidden = YES;
  670. [self.previewLayer removeFromSuperlayer];
  671. [self.localVideoView removeFromSuperview];
  672. self.localVideoView = nil;
  673. });
  674. }
  675. //- (void)conferenceLeft:(NSDictionary *)data {
  676. // _onJitsiMeetViewDelegateEvent(@"CONFERENCE_LEFT", data);
  677. //
  678. //
  679. //}
  680. - (void)conferenceWillJoin:(NSDictionary *)data {
  681. _onJitsiMeetViewDelegateEvent(@"CONFERENCE_WILL_JOIN", data);
  682. }
  683. //- (void)conferenceWillLeave:(NSDictionary *)data {
  684. // _onJitsiMeetViewDelegateEvent(@"CONFERENCE_WILL_LEAVE", data);
  685. //
  686. // [self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_JOINED" withData:data];
  687. //
  688. // dispatch_async(dispatch_get_main_queue(), ^{
  689. //
  690. // [self.pingTimer invalidate];
  691. // self.pingTimer = nil;
  692. //
  693. // if (!self.isGroup) {
  694. //// int n = [[NSDate date] timeIntervalSince1970]-_startTime;
  695. // int type = kWCMessageTypeVideoChatEnd;
  696. // if (self.isAudio) {
  697. // type = kWCMessageTypeAudioChatEnd;
  698. // }
  699. // [g_meeting sendEnd:type toUserId:self.toUserId toUserName:self.toUserName timeLen:self.timerIndex];
  700. // }
  701. // [self actionQuit];
  702. // });
  703. //}
  704. - (void)conferenceTerminated:(NSDictionary *)data{
  705. id error = [data objectForKey:@"error"];
  706. if (error) {
  707. _onJitsiMeetViewDelegateEvent(@"LOAD_CONFIG_ERROR", data);
  708. dispatch_async(dispatch_get_main_queue(), ^{
  709. // _startTime = [[NSDate date] timeIntervalSince1970];
  710. self.session = nil;
  711. self.localVideoView.hidden = YES;
  712. [self.previewLayer removeFromSuperlayer];
  713. [self.localVideoView removeFromSuperview];
  714. self.localVideoView = nil;
  715. });
  716. }else{
  717. _onJitsiMeetViewDelegateEvent(@"CONFERENCE_WILL_LEAVE", data);
  718. [self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_JOINED" withData:data];
  719. dispatch_async(dispatch_get_main_queue(), ^{
  720. [self.pingTimer invalidate];
  721. self.pingTimer = nil;
  722. if (!self.isGroup) {
  723. // int n = [[NSDate date] timeIntervalSince1970]-_startTime;
  724. int type = kWCMessageTypeVideoChatEnd;
  725. if (self.isAudio) {
  726. type = kWCMessageTypeAudioChatEnd;
  727. }
  728. [g_meeting sendEnd:type toUserId:self.toUserId toUserName:self.toUserName timeLen:self.timerIndex];
  729. }
  730. [self actionQuit];
  731. });
  732. }
  733. }
  734. - (void)loadConfigError:(NSDictionary *)data {
  735. _onJitsiMeetViewDelegateEvent(@"LOAD_CONFIG_ERROR", data);
  736. }
  737. - (void)didReceiveMemoryWarning {
  738. [super didReceiveMemoryWarning];
  739. // Dispose of any resources that can be recreated.
  740. }
  741. - (void)actionQuit {
  742. // if (_recorderBtn.selected) {
  743. // [self stopRecord];
  744. // }
  745. #if !TARGET_IPHONE_SIMULATOR
  746. JitsiMeetView *view = (JitsiMeetView *)self.view;
  747. [view leave];
  748. #endif
  749. g_meeting.roomNum = nil;
  750. [self.pingTimer invalidate];
  751. self.pingTimer = nil;
  752. [g_App endCall];
  753. [self.timer invalidate];
  754. self.timer = nil;
  755. g_meeting.isMeeting = NO;
  756. // [self dismissViewControllerAnimated:YES completion:nil];
  757. [g_notify removeObserver:self];
  758. [g_subWindow removeFromSuperview];
  759. g_subWindow = nil;
  760. [self.view removeFromSuperview];
  761. self.view = nil;
  762. _pSelf = nil;
  763. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  764. g_meeting.isMeeting = NO;
  765. [UIApplication sharedApplication].statusBarHidden = NO;
  766. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
  767. });
  768. }
  769. - (void)dealloc {
  770. NSLog(@"%@ -- dealloc",NSStringFromClass([self class]));
  771. [g_notify removeObserver:self];
  772. }
  773. /*
  774. #pragma mark - Navigation
  775. // In a storyboard-based application, you will often want to do a little preparation before navigation
  776. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  777. // Get the new view controller using [segue destinationViewController].
  778. // Pass the selected object to the new view controller.
  779. }
  780. */
  781. @end