WFCUMultiVideoViewController.m 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  1. //
  2. // ViewController.m
  3. // WFDemo
  4. //
  5. // Created by heavyrain on 17/9/27.
  6. // Copyright © 2017年 WildFireChat. All rights reserved.
  7. //
  8. #import "WFCUMultiVideoViewController.h"
  9. #import <AVFoundation/AVFoundation.h>
  10. #import <AVKit/AVKit.h>
  11. #if WFCU_SUPPORT_VOIP
  12. #import <WebRTC/WebRTC.h>
  13. #import <WFAVEngineKit/WFAVEngineKit.h>
  14. #import "WFCUFloatingWindow.h"
  15. #import "WFCUParticipantCollectionViewCell.h"
  16. #endif
  17. #import "SDWebImage.h"
  18. #import <WFChatClient/WFCCConversation.h>
  19. #import "WFCUPortraitCollectionViewCell.h"
  20. #import "WFCUParticipantCollectionViewLayout.h"
  21. #import "WFCUSeletedUserViewController.h"
  22. #import "UIView+Toast.h"
  23. @interface WFCUMultiVideoViewController () <UITextFieldDelegate
  24. #if WFCU_SUPPORT_VOIP
  25. ,WFAVCallSessionDelegate
  26. #endif
  27. ,UICollectionViewDataSource
  28. ,UICollectionViewDelegate
  29. >
  30. #if WFCU_SUPPORT_VOIP
  31. @property (nonatomic, strong) UIView *bigVideoView;
  32. @property (nonatomic, strong) UICollectionView *smallCollectionView;
  33. @property (nonatomic, strong) UICollectionView *portraitCollectionView;
  34. @property (nonatomic, strong) UIButton *hangupButton;
  35. @property (nonatomic, strong) UIButton *answerButton;
  36. @property (nonatomic, strong) UIButton *switchCameraButton;
  37. @property (nonatomic, strong) UIButton *audioButton;
  38. @property (nonatomic, strong) UIButton *speakerButton;
  39. @property (nonatomic, strong) UIButton *videoButton;
  40. @property (nonatomic, strong) UIButton *scalingButton;
  41. @property (nonatomic, strong) UIButton *minimizeButton;
  42. @property (nonatomic, strong) UIButton *addParticipantButton;
  43. @property (nonatomic, strong) UIImageView *portraitView;
  44. @property (nonatomic, strong) UILabel *userNameLabel;
  45. @property (nonatomic, strong) UILabel *stateLabel;
  46. @property (nonatomic, strong) UILabel *connectTimeLabel;
  47. @property (nonatomic, strong) WFAVCallSession *currentSession;
  48. @property (nonatomic, assign) WFAVVideoScalingType smallScalingType;
  49. @property (nonatomic, assign) WFAVVideoScalingType bigScalingType;
  50. @property (nonatomic, assign) CGPoint panStartPoint;
  51. @property (nonatomic, assign) CGRect panStartVideoFrame;
  52. @property (nonatomic, strong) NSTimer *connectedTimer;
  53. @property (nonatomic, strong) NSMutableArray<NSString *> *participants;
  54. //视频时,大屏用户正在说话
  55. @property (nonatomic, strong)UIImageView *speakingView;
  56. #endif
  57. @end
  58. #define ButtonSize 60
  59. #define BottomPadding 36
  60. #define SmallVideoView 120
  61. #define OperationTitleFont 10
  62. #define OperationButtonSize 50
  63. #define PortraitItemSize 48
  64. #define PortraitLabelSize 16
  65. @implementation WFCUMultiVideoViewController
  66. #if !WFCU_SUPPORT_VOIP
  67. - (instancetype)initWithSession:(WFAVCallSession *)session {
  68. self = [super init];
  69. return self;
  70. }
  71. - (instancetype)initWithTargets:(NSArray<NSString *> *)targetIds conversation:(WFCCConversation *)conversation audioOnly:(BOOL)audioOnly {
  72. self = [super init];
  73. return self;
  74. }
  75. #else
  76. - (instancetype)initWithSession:(WFAVCallSession *)session {
  77. self = [super init];
  78. if (self) {
  79. self.currentSession = session;
  80. self.currentSession.delegate = self;
  81. [self didChangeState:kWFAVEngineStateIncomming];
  82. [self rearrangeParticipants];
  83. }
  84. return self;
  85. }
  86. - (instancetype)initWithTargets:(NSArray<NSString *> *)targetIds conversation:(WFCCConversation *)conversation audioOnly:(BOOL)audioOnly {
  87. self = [super init];
  88. if (self) {
  89. WFAVCallSession *session = [[WFAVEngineKit sharedEngineKit] startCall:targetIds
  90. audioOnly:audioOnly
  91. conversation:conversation
  92. sessionDelegate:self];
  93. self.currentSession = session;
  94. [self rearrangeParticipants];
  95. }
  96. return self;
  97. }
  98. /*
  99. session的participantIds是除了自己外的所有成员。这里把自己也加入列表,然后把发起者放到最后面。
  100. */
  101. - (void)rearrangeParticipants {
  102. self.participants = [[NSMutableArray alloc] init];
  103. [self.participants addObjectsFromArray:self.currentSession.participantIds];
  104. if ([self.currentSession.initiator isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  105. [self.participants addObject:[WFCCNetworkService sharedInstance].userId];
  106. } else {
  107. [self.participants insertObject:[WFCCNetworkService sharedInstance].userId atIndex:[self.participants indexOfObject:self.currentSession.initiator]];
  108. [self.participants removeObject:self.currentSession.initiator];
  109. [self.participants addObject:self.currentSession.initiator];
  110. }
  111. }
  112. - (void)viewDidLoad {
  113. [super viewDidLoad];
  114. [self.view setBackgroundColor:[UIColor blackColor]];
  115. self.smallScalingType = kWFAVVideoScalingTypeAspectFill;
  116. self.bigScalingType = kWFAVVideoScalingTypeAspectBalanced;
  117. self.bigVideoView = [[UIView alloc] initWithFrame:self.view.bounds];
  118. UITapGestureRecognizer *tapBigVideo = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClickedBigVideoView:)];
  119. [self.bigVideoView addGestureRecognizer:tapBigVideo];
  120. [self.view addSubview:self.bigVideoView];
  121. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
  122. CGFloat itemWidth = (self.view.frame.size.width + layout.minimumLineSpacing)/3 - layout.minimumLineSpacing;
  123. layout.itemSize = CGSizeMake(itemWidth, itemWidth);
  124. layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
  125. self.smallCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, kStatusBarAndNavigationBarHeight, self.view.frame.size.width, itemWidth) collectionViewLayout:layout];
  126. self.smallCollectionView.dataSource = self;
  127. self.smallCollectionView.delegate = self;
  128. [self.smallCollectionView registerClass:[WFCUParticipantCollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
  129. self.smallCollectionView.backgroundColor = [UIColor clearColor];
  130. [self.smallCollectionView addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onSmallVideoPan:)]];
  131. if (self.currentSession.audioOnly) {
  132. self.smallCollectionView.hidden = YES;
  133. }
  134. [self.view addSubview:self.smallCollectionView];
  135. WFCUParticipantCollectionViewLayout *layout2 = [[WFCUParticipantCollectionViewLayout alloc] init];
  136. layout2.itemHeight = PortraitItemSize + PortraitLabelSize;
  137. layout2.itemWidth = PortraitItemSize;
  138. layout2.lineSpace = 6;
  139. layout2.itemSpace = 6;
  140. self.portraitCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(16, self.view.frame.size.height - BottomPadding - ButtonSize - (PortraitItemSize + PortraitLabelSize)*3 - PortraitLabelSize, self.view.frame.size.width - 32, (PortraitItemSize + PortraitLabelSize)*3 + PortraitLabelSize) collectionViewLayout:layout2];
  141. self.portraitCollectionView.dataSource = self;
  142. self.portraitCollectionView.delegate = self;
  143. [self.portraitCollectionView registerClass:[WFCUPortraitCollectionViewCell class] forCellWithReuseIdentifier:@"cell2"];
  144. self.portraitCollectionView.backgroundColor = [UIColor clearColor];
  145. [self.view addSubview:self.portraitCollectionView];
  146. [self checkAVPermission];
  147. if(self.currentSession.state == kWFAVEngineStateOutgoing && !self.currentSession.isAudioOnly) {
  148. [[WFAVEngineKit sharedEngineKit] startPreview];
  149. }
  150. WFCCUserInfo *user = [[WFCCIMService sharedWFCIMService] getUserInfo:self.currentSession.initiator inGroup:self.currentSession.conversation.type == Group_Type ? self.currentSession.conversation.target : nil refresh:NO];
  151. self.portraitView = [[UIImageView alloc] init];
  152. [self.portraitView sd_setImageWithURL:[NSURL URLWithString:[user.portrait stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] placeholderImage:[UIImage imageNamed:@"PersonalChat"]];
  153. self.portraitView.layer.masksToBounds = YES;
  154. self.portraitView.layer.cornerRadius = 8.f;
  155. [self.view addSubview:self.portraitView];
  156. self.userNameLabel = [[UILabel alloc] init];
  157. self.userNameLabel.font = [UIFont systemFontOfSize:26];
  158. self.userNameLabel.text = user.displayName;
  159. self.userNameLabel.textColor = [UIColor whiteColor];
  160. [self.view addSubview:self.userNameLabel];
  161. self.stateLabel = [[UILabel alloc] init];
  162. self.stateLabel.font = [UIFont systemFontOfSize:16];
  163. self.stateLabel.textColor = [UIColor whiteColor];
  164. [self.view addSubview:self.stateLabel];
  165. self.connectTimeLabel = [[UILabel alloc] init];
  166. self.connectTimeLabel.font = [UIFont systemFontOfSize:16];
  167. self.connectTimeLabel.textColor = [UIColor whiteColor];
  168. [self.view addSubview:self.connectTimeLabel];
  169. [self updateTopViewFrame];
  170. [self didChangeState:self.currentSession.state];//update ui
  171. [[NSNotificationCenter defaultCenter] addObserver:self
  172. selector:@selector(onDeviceOrientationDidChange)
  173. name:UIDeviceOrientationDidChangeNotification
  174. object:nil];
  175. [self onDeviceOrientationDidChange];
  176. }
  177. - (UIButton *)hangupButton {
  178. if (!_hangupButton) {
  179. _hangupButton = [[UIButton alloc] init];
  180. [_hangupButton setImage:[UIImage imageNamed:@"hangup"] forState:UIControlStateNormal];
  181. [_hangupButton setImage:[UIImage imageNamed:@"hangup_hover"] forState:UIControlStateHighlighted];
  182. [_hangupButton setImage:[UIImage imageNamed:@"hangup_hover"] forState:UIControlStateSelected];
  183. _hangupButton.backgroundColor = [UIColor clearColor];
  184. [_hangupButton addTarget:self action:@selector(hanupButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  185. _hangupButton.hidden = YES;
  186. [self.view addSubview:_hangupButton];
  187. }
  188. return _hangupButton;
  189. }
  190. - (UIButton *)answerButton {
  191. if (!_answerButton) {
  192. _answerButton = [[UIButton alloc] init];
  193. [_answerButton setImage:[UIImage imageNamed:@"answer"] forState:UIControlStateNormal];
  194. [_answerButton setImage:[UIImage imageNamed:@"answer_hover"] forState:UIControlStateHighlighted];
  195. [_answerButton setImage:[UIImage imageNamed:@"answer_hover"] forState:UIControlStateSelected];
  196. _answerButton.backgroundColor = [UIColor clearColor];
  197. [_answerButton addTarget:self action:@selector(answerButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  198. _answerButton.hidden = YES;
  199. [self.view addSubview:_answerButton];
  200. }
  201. return _answerButton;
  202. }
  203. - (UIButton *)minimizeButton {
  204. if (!_minimizeButton) {
  205. _minimizeButton = [[UIButton alloc] initWithFrame:CGRectMake(16, 26, 30, 30)];
  206. [_minimizeButton setImage:[UIImage imageNamed:@"minimize"] forState:UIControlStateNormal];
  207. [_minimizeButton setImage:[UIImage imageNamed:@"minimize_hover"] forState:UIControlStateHighlighted];
  208. [_minimizeButton setImage:[UIImage imageNamed:@"minimize_hover"] forState:UIControlStateSelected];
  209. _minimizeButton.backgroundColor = [UIColor clearColor];
  210. [_minimizeButton addTarget:self action:@selector(minimizeButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  211. _minimizeButton.hidden = YES;
  212. [self.view addSubview:_minimizeButton];
  213. }
  214. return _minimizeButton;
  215. }
  216. - (UIButton *)addParticipantButton {
  217. if (!_addParticipantButton) {
  218. _addParticipantButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width - 16 - 30, 26, 30, 30)];
  219. [_addParticipantButton setImage:[UIImage imageNamed:@"plus-circle"] forState:UIControlStateNormal];
  220. [_addParticipantButton setImage:[UIImage imageNamed:@"plus-circle"] forState:UIControlStateHighlighted];
  221. [_addParticipantButton setImage:[UIImage imageNamed:@"plus-circle"] forState:UIControlStateSelected];
  222. _addParticipantButton.backgroundColor = [UIColor clearColor];
  223. [_addParticipantButton addTarget:self action:@selector(addParticipantButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  224. _addParticipantButton.hidden = YES;
  225. [self.view addSubview:_addParticipantButton];
  226. }
  227. return _addParticipantButton;
  228. }
  229. - (UIButton *)switchCameraButton {
  230. if (!_switchCameraButton) {
  231. _switchCameraButton = [[UIButton alloc] init];
  232. [_switchCameraButton setImage:[UIImage imageNamed:@"switchcamera"] forState:UIControlStateNormal];
  233. [_switchCameraButton setImage:[UIImage imageNamed:@"switchcamera_hover"] forState:UIControlStateHighlighted];
  234. [_switchCameraButton setImage:[UIImage imageNamed:@"switchcamera_hover"] forState:UIControlStateSelected];
  235. _switchCameraButton.backgroundColor = [UIColor clearColor];
  236. [_switchCameraButton addTarget:self action:@selector(switchCameraButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  237. _switchCameraButton.hidden = YES;
  238. [self.view addSubview:_switchCameraButton];
  239. }
  240. return _switchCameraButton;
  241. }
  242. - (UIButton *)audioButton {
  243. if (!_audioButton) {
  244. _audioButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-ButtonSize/2, self.view.frame.size.height-10-ButtonSize, ButtonSize, ButtonSize)];
  245. [_audioButton setImage:[UIImage imageNamed:@"mute"] forState:UIControlStateNormal];
  246. [_audioButton setImage:[UIImage imageNamed:@"mute_hover"] forState:UIControlStateHighlighted];
  247. [_audioButton setImage:[UIImage imageNamed:@"mute_hover"] forState:UIControlStateSelected];
  248. _audioButton.backgroundColor = [UIColor clearColor];
  249. [_audioButton addTarget:self action:@selector(audioButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  250. _audioButton.hidden = YES;
  251. [self updateAudioButton];
  252. [self.view addSubview:_audioButton];
  253. }
  254. return _audioButton;
  255. }
  256. - (UIButton *)speakerButton {
  257. if (!_speakerButton) {
  258. _speakerButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-ButtonSize/2, self.view.frame.size.height-10-ButtonSize, ButtonSize, ButtonSize)];
  259. [_speakerButton setImage:[UIImage imageNamed:@"speaker"] forState:UIControlStateNormal];
  260. [_speakerButton setImage:[UIImage imageNamed:@"speaker_hover"] forState:UIControlStateHighlighted];
  261. [_speakerButton setImage:[UIImage imageNamed:@"speaker_hover"] forState:UIControlStateSelected];
  262. _speakerButton.backgroundColor = [UIColor clearColor];
  263. [_speakerButton addTarget:self action:@selector(speakerButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  264. _speakerButton.hidden = YES;
  265. [self.view addSubview:_speakerButton];
  266. }
  267. return _speakerButton;
  268. }
  269. - (UIButton *)videoButton {
  270. if (!_videoButton) {
  271. _videoButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width*3/4-ButtonSize/4, self.view.frame.size.height-45-ButtonSize-ButtonSize/2-2, ButtonSize/2, ButtonSize/2)];
  272. [_videoButton setImage:[UIImage imageNamed:@"enable_video"] forState:UIControlStateNormal];
  273. _videoButton.backgroundColor = [UIColor clearColor];
  274. [_videoButton addTarget:self action:@selector(videoButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  275. _videoButton.hidden = YES;
  276. [self updateVideoButton];
  277. [self.view addSubview:_videoButton];
  278. }
  279. return _videoButton;
  280. }
  281. - (UIButton *)scalingButton {
  282. if (!_scalingButton) {
  283. _scalingButton = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-ButtonSize/2, self.view.frame.size.height-10-ButtonSize, ButtonSize, ButtonSize)];
  284. [_scalingButton setTitle:WFCString(@"Scale") forState:UIControlStateNormal];
  285. _scalingButton.backgroundColor = [UIColor greenColor];
  286. [_scalingButton addTarget:self action:@selector(scalingButtonDidTap:) forControlEvents:UIControlEventTouchDown];
  287. _scalingButton.hidden = YES;
  288. [self.view addSubview:_scalingButton];
  289. }
  290. return _scalingButton;
  291. }
  292. - (UIImageView *)speakingView {
  293. if (!_speakingView) {
  294. _speakingView = [[UIImageView alloc] initWithFrame:CGRectMake(0, self.bigVideoView.bounds.size.height - 20, 20, 20)];
  295. _speakingView.layer.masksToBounds = YES;
  296. _speakingView.layer.cornerRadius = 2.f;
  297. _speakingView.image = [UIImage imageNamed:@"speaking"];
  298. _speakingView.hidden = YES;
  299. [self.bigVideoView addSubview:_speakingView];
  300. }
  301. return _speakingView;
  302. }
  303. - (void)startConnectedTimer {
  304. [self stopConnectedTimer];
  305. self.connectedTimer = [NSTimer scheduledTimerWithTimeInterval:1
  306. target:self
  307. selector:@selector(updateConnectedTimeLabel)
  308. userInfo:nil
  309. repeats:YES];
  310. [self.connectedTimer fire];
  311. }
  312. - (void)stopConnectedTimer {
  313. if (self.connectedTimer) {
  314. [self.connectedTimer invalidate];
  315. self.connectedTimer = nil;
  316. }
  317. }
  318. - (void)setFocusUser:(NSString *)userId {
  319. if (userId) {
  320. [self.participants removeObject:userId];
  321. [self.participants addObject:userId];
  322. [self reloadVideoUI];
  323. }
  324. }
  325. - (void)updateConnectedTimeLabel {
  326. long sec = [[NSDate date] timeIntervalSince1970] - self.currentSession.connectedTime / 1000;
  327. if (sec < 60 * 60) {
  328. self.connectTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld", sec / 60, sec % 60];
  329. } else {
  330. self.connectTimeLabel.text = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", sec / 60 / 60, (sec / 60) % 60, sec % 60];
  331. }
  332. }
  333. - (void)hanupButtonDidTap:(UIButton *)button {
  334. if(self.currentSession.state != kWFAVEngineStateIdle) {
  335. [self.currentSession endCall];
  336. }
  337. }
  338. - (void)answerButtonDidTap:(UIButton *)button {
  339. if (self.currentSession.state == kWFAVEngineStateIncomming) {
  340. [self.currentSession answerCall:NO];
  341. }
  342. }
  343. - (void)minimizeButtonDidTap:(UIButton *)button {
  344. __block NSString *focusUser = [self.participants lastObject];
  345. [WFCUFloatingWindow startCallFloatingWindow:self.currentSession focusUser:focusUser withTouchedBlock:^(WFAVCallSession *callSession) {
  346. WFCUMultiVideoViewController *vc = [[WFCUMultiVideoViewController alloc] initWithSession:callSession];
  347. [vc setFocusUser:focusUser];
  348. [[WFAVEngineKit sharedEngineKit] presentViewController:vc];
  349. }];
  350. [[WFAVEngineKit sharedEngineKit] dismissViewController:self];
  351. }
  352. - (void)addParticipantButtonDidTap:(UIButton *)button {
  353. WFCUSeletedUserViewController *pvc = [[WFCUSeletedUserViewController alloc] init];
  354. NSMutableArray *disabledUser = [[NSMutableArray alloc] init];
  355. [disabledUser addObjectsFromArray:self.participants];
  356. pvc.disableUserIds = disabledUser;
  357. pvc.maxSelectCount = self.currentSession.audioOnly ? [WFAVEngineKit sharedEngineKit].maxAudioCallCount : [WFAVEngineKit sharedEngineKit].maxVideoCallCount;
  358. pvc.groupId = self.currentSession.conversation.target;
  359. NSMutableArray *candidateUser = [[NSMutableArray alloc] init];
  360. NSArray<WFCCGroupMember *> *members = [[WFCCIMService sharedWFCIMService] getGroupMembers:self.currentSession.conversation.target forceUpdate:NO];
  361. for (WFCCGroupMember *member in members) {
  362. [candidateUser addObject:member.memberId];
  363. }
  364. pvc.candidateUsers = candidateUser;
  365. pvc.type = Vertical;
  366. __weak typeof(self)ws = self;
  367. pvc.selectResult = ^(NSArray<NSString *> *contacts) {
  368. if (contacts.count) {
  369. [ws.currentSession inviteNewParticipants:contacts];
  370. }
  371. };
  372. UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:pvc];
  373. navi.modalPresentationStyle = UIModalPresentationFullScreen;
  374. dispatch_async(dispatch_get_main_queue(), ^{
  375. [ws presentViewController:navi animated:YES completion:nil];
  376. });
  377. }
  378. - (void)switchCameraButtonDidTap:(UIButton *)button {
  379. if (self.currentSession.state != kWFAVEngineStateIdle) {
  380. [self.currentSession switchCamera];
  381. }
  382. }
  383. - (void)audioButtonDidTap:(UIButton *)button {
  384. if (self.currentSession.state != kWFAVEngineStateIdle) {
  385. [self.currentSession muteAudio:!self.currentSession.audioMuted];
  386. [self updateAudioButton];
  387. }
  388. }
  389. - (void)updateAudioButton {
  390. if (self.currentSession.audioMuted) {
  391. [self.audioButton setImage:[UIImage imageNamed:@"mute_hover"] forState:UIControlStateNormal];
  392. } else {
  393. [self.audioButton setImage:[UIImage imageNamed:@"mute"] forState:UIControlStateNormal];
  394. }
  395. }
  396. - (void)speakerButtonDidTap:(UIButton *)button {
  397. if (self.currentSession.state != kWFAVEngineStateIdle) {
  398. [self.currentSession enableSpeaker:!self.currentSession.isSpeaker];
  399. [self updateSpeakerButton];
  400. }
  401. }
  402. - (void)updateSpeakerButton {
  403. if (!self.currentSession.isSpeaker) {
  404. [self.speakerButton setImage:[UIImage imageNamed:@"speaker"] forState:UIControlStateNormal];
  405. } else {
  406. [self.speakerButton setImage:[UIImage imageNamed:@"speaker_hover"] forState:UIControlStateNormal];
  407. }
  408. }
  409. - (void)updateVideoButton {
  410. if (self.currentSession.videoMuted) {
  411. [self.videoButton setImage:[UIImage imageNamed:@"disable_video"] forState:UIControlStateNormal];
  412. } else {
  413. [self.videoButton setImage:[UIImage imageNamed:@"enable_video"] forState:UIControlStateNormal];
  414. }
  415. }
  416. //1.决定当前界面是否开启自动转屏,如果返回NO,后面两个方法也不会被调用,只是会支持默认的方向
  417. - (BOOL)shouldAutorotate {
  418. return YES;
  419. }
  420. //2.返回支持的旋转方向
  421. //iPad设备上,默认返回值UIInterfaceOrientationMaskAllButUpSideDwon
  422. //iPad设备上,默认返回值是UIInterfaceOrientationMaskAll
  423. - (UIInterfaceOrientationMask)supportedInterfaceOrientations{
  424. return UIDeviceOrientationLandscapeLeft | UIDeviceOrientationLandscapeRight | UIDeviceOrientationPortrait;
  425. }
  426. //3.返回进入界面默认显示方向
  427. - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
  428. return UIInterfaceOrientationPortrait;
  429. }
  430. - (BOOL)onDeviceOrientationDidChange{
  431. //获取当前设备Device
  432. UIDevice *device = [UIDevice currentDevice] ;
  433. switch (device.orientation) {
  434. case UIDeviceOrientationFaceUp:
  435. NSLog(@"屏幕幕朝上平躺");
  436. break;
  437. case UIDeviceOrientationFaceDown:
  438. NSLog(@"屏幕朝下平躺");
  439. break;
  440. case UIDeviceOrientationUnknown:
  441. //系统当前无法识别设备朝向,可能是倾斜
  442. NSLog(@"未知方向");
  443. break;
  444. case UIDeviceOrientationLandscapeLeft:
  445. self.bigVideoView.transform = CGAffineTransformMakeRotation(M_PI_2);
  446. NSLog(@"屏幕向左橫置");
  447. break;
  448. case UIDeviceOrientationLandscapeRight:
  449. self.bigVideoView.transform = CGAffineTransformMakeRotation(-M_PI_2);
  450. NSLog(@"屏幕向右橫置");
  451. break;
  452. case UIDeviceOrientationPortrait:
  453. self.bigVideoView.transform = CGAffineTransformMakeRotation(0);
  454. NSLog(@"屏幕直立");
  455. break;
  456. case UIDeviceOrientationPortraitUpsideDown:
  457. NSLog(@"屏幕直立,上下顛倒");
  458. break;
  459. default:
  460. NSLog(@"無法识别");
  461. break;
  462. }
  463. if (!self.smallCollectionView.hidden) {
  464. [self.smallCollectionView reloadData];
  465. }
  466. return YES;
  467. }
  468. - (void)viewWillAppear:(BOOL)animated {
  469. [super viewWillAppear:animated];
  470. if (_currentSession.state == kWFAVEngineStateConnected) {
  471. [self updateConnectedTimeLabel];
  472. [self startConnectedTimer];
  473. }
  474. }
  475. - (void)viewWillDisappear:(BOOL)animated {
  476. [super viewWillDisappear:animated];
  477. [self stopConnectedTimer];
  478. }
  479. - (void)setPanStartPoint:(CGPoint)panStartPoint {
  480. _panStartPoint = panStartPoint;
  481. _panStartVideoFrame = self.smallCollectionView.frame;
  482. }
  483. - (void)moveToPanPoint:(CGPoint)panPoint {
  484. CGRect frame = self.panStartVideoFrame;
  485. CGSize moveSize = CGSizeMake(panPoint.x - self.panStartPoint.x, panPoint.y - self.panStartPoint.y);
  486. frame.origin.x += moveSize.width;
  487. frame.origin.y += moveSize.height;
  488. self.smallCollectionView.frame = frame;
  489. }
  490. - (void)onSmallVideoPan:(UIPanGestureRecognizer *)recognize {
  491. switch (recognize.state) {
  492. case UIGestureRecognizerStateBegan:
  493. self.panStartPoint = [recognize translationInView:self.view];
  494. break;
  495. case UIGestureRecognizerStateChanged: {
  496. CGPoint currentPoint = [recognize translationInView:self.view];
  497. [self moveToPanPoint:currentPoint];
  498. break;
  499. }
  500. case UIGestureRecognizerStateEnded: {
  501. CGPoint endPoint = [recognize translationInView:self.view];
  502. [self moveToPanPoint:endPoint];
  503. break;
  504. }
  505. case UIGestureRecognizerStateCancelled:
  506. case UIGestureRecognizerStateFailed:
  507. default:
  508. break;
  509. }
  510. }
  511. - (void)videoButtonDidTap:(UIButton *)button {
  512. if (self.currentSession.state != kWFAVEngineStateIdle) {
  513. [self.currentSession muteVideo:!self.currentSession.isVideoMuted];
  514. [self updateVideoButton];
  515. }
  516. }
  517. - (void)scalingButtonDidTap:(UIButton *)button {
  518. // if (self.currentSession.state != kWFAVEngineStateIdle) {
  519. // if (self.scalingType < kWFAVVideoScalingTypeAspectBalanced) {
  520. // self.scalingType++;
  521. // } else {
  522. // self.scalingType = kWFAVVideoScalingTypeAspectFit;
  523. // }
  524. //
  525. //// [self.currentSession setupLocalVideoView:self.smallVideoView scalingType:self.scalingType];
  526. //// [self.currentSession setupRemoteVideoView:self.bigVideoView scalingType:self.scalingType forUser:self.currentSession.participants[0]];
  527. // }
  528. }
  529. - (CGRect)getButtomCenterButtonFrame {
  530. return CGRectMake(self.view.frame.size.width/2-ButtonSize/2, self.view.frame.size.height-BottomPadding-ButtonSize, ButtonSize, ButtonSize);
  531. }
  532. - (CGRect)getButtomLeftButtonFrame {
  533. return CGRectMake(self.view.frame.size.width/4-ButtonSize/2, self.view.frame.size.height-BottomPadding-ButtonSize, ButtonSize, ButtonSize);
  534. }
  535. - (CGRect)getButtomRightButtonFrame {
  536. return CGRectMake(self.view.frame.size.width*3/4-ButtonSize/2, self.view.frame.size.height-BottomPadding-ButtonSize, ButtonSize, ButtonSize);
  537. }
  538. - (CGRect)getToAudioButtonFrame {
  539. return CGRectMake(self.view.frame.size.width*3/4-ButtonSize/2, self.view.frame.size.height-BottomPadding-ButtonSize-ButtonSize-2, ButtonSize, ButtonSize);
  540. }
  541. - (void)updateTopViewFrame {
  542. // if (self.currentSession.isAudioOnly) {
  543. CGFloat containerWidth = self.view.bounds.size.width;
  544. self.portraitView.frame = CGRectMake((containerWidth-64)/2, kStatusBarAndNavigationBarHeight, 64, 64);;
  545. self.userNameLabel.frame = CGRectMake((containerWidth - 240)/2, kStatusBarAndNavigationBarHeight + 64 + 8, 240, 26);
  546. self.userNameLabel.textAlignment = NSTextAlignmentCenter;
  547. self.connectTimeLabel.frame = CGRectMake((containerWidth - 240)/2, self.smallCollectionView.frame.origin.y + self.smallCollectionView.frame.size.height + 8, 240, 16);
  548. self.connectTimeLabel.textAlignment = NSTextAlignmentCenter;
  549. self.stateLabel.frame = CGRectMake((containerWidth - 240)/2, self.smallCollectionView.frame.origin.y + self.smallCollectionView.frame.size.height + 30, 240, 16);
  550. self.stateLabel.textAlignment = NSTextAlignmentCenter;
  551. // } else {
  552. // self.portraitView.frame = CGRectMake(16, kStatusBarAndNavigationBarHeight, 64, 64);
  553. // self.userNameLabel.frame = CGRectMake(96, kStatusBarAndNavigationBarHeight + 8, 240, 26);
  554. // if(![NSThread isMainThread]) {
  555. // NSLog(@"error not main thread");
  556. // }
  557. // self.userNameLabel.textAlignment = NSTextAlignmentLeft;
  558. // if(self.currentSession.state == kWFAVEngineStateConnected) {
  559. // self.stateLabel.frame = CGRectMake(54, 30, 240, 20);
  560. // } else {
  561. // self.stateLabel.frame = CGRectMake(96, kStatusBarAndNavigationBarHeight + 26 + 14, 240, 16);
  562. // }
  563. // self.stateLabel.textAlignment = NSTextAlignmentLeft;
  564. // }
  565. }
  566. - (void)onClickedBigVideoView:(id)sender {
  567. if (self.currentSession.state != kWFAVEngineStateConnected) {
  568. return;
  569. }
  570. if (self.currentSession.audioOnly) {
  571. return;
  572. }
  573. if (self.smallCollectionView.hidden) {
  574. if (self.hangupButton.hidden) {
  575. self.hangupButton.hidden = NO;
  576. self.audioButton.hidden = NO;
  577. if (self.currentSession.audioOnly) {
  578. self.videoButton.hidden = YES;
  579. } else {
  580. self.videoButton.hidden = NO;
  581. }
  582. self.switchCameraButton.hidden = NO;
  583. self.smallCollectionView.hidden = NO;
  584. self.minimizeButton.hidden = NO;
  585. self.addParticipantButton.hidden = NO;
  586. } else {
  587. self.hangupButton.hidden = YES;
  588. self.audioButton.hidden = YES;
  589. self.videoButton.hidden = YES;
  590. self.switchCameraButton.hidden = YES;
  591. self.minimizeButton.hidden = YES;
  592. self.addParticipantButton.hidden = YES;
  593. }
  594. } else {
  595. self.smallCollectionView.hidden = YES;
  596. }
  597. }
  598. #pragma mark - WFAVEngineDelegate
  599. - (void)didChangeState:(WFAVEngineState)state {
  600. if (!self.viewLoaded) {
  601. return;
  602. }
  603. switch (state) {
  604. case kWFAVEngineStateIdle:
  605. self.answerButton.hidden = YES;
  606. self.hangupButton.hidden = YES;
  607. self.switchCameraButton.hidden = YES;
  608. self.audioButton.hidden = YES;
  609. self.videoButton.hidden = YES;
  610. self.scalingButton.hidden = YES;
  611. [self stopConnectedTimer];
  612. self.userNameLabel.hidden = YES;
  613. self.portraitView.hidden = YES;
  614. self.stateLabel.text = WFCString(@"CallEnded");
  615. self.smallCollectionView.hidden = YES;
  616. self.portraitCollectionView.hidden = YES;
  617. self.bigVideoView.hidden = YES;
  618. self.minimizeButton.hidden = YES;
  619. self.speakerButton.hidden = YES;
  620. self.addParticipantButton.hidden = YES;
  621. [self updateTopViewFrame];
  622. break;
  623. case kWFAVEngineStateOutgoing:
  624. self.answerButton.hidden = YES;
  625. self.connectTimeLabel.hidden = YES;
  626. self.hangupButton.hidden = NO;
  627. self.hangupButton.frame = [self getButtomCenterButtonFrame];
  628. self.switchCameraButton.hidden = YES;
  629. if (self.currentSession.isAudioOnly) {
  630. self.speakerButton.hidden = YES;
  631. [self updateSpeakerButton];
  632. self.speakerButton.frame = [self getButtomRightButtonFrame];
  633. self.audioButton.hidden = YES;
  634. self.audioButton.frame = [self getButtomLeftButtonFrame];
  635. } else {
  636. self.speakerButton.hidden = YES;
  637. self.audioButton.hidden = YES;
  638. }
  639. self.videoButton.hidden = YES;
  640. self.scalingButton.hidden = YES;
  641. [self.currentSession setupLocalVideoView:self.bigVideoView scalingType:self.bigScalingType];
  642. self.stateLabel.text = WFCString(@"WaitingAccept");
  643. self.smallCollectionView.hidden = YES;
  644. self.portraitCollectionView.hidden = NO;
  645. [self.portraitCollectionView reloadData];
  646. self.userNameLabel.hidden = YES;
  647. self.portraitView.hidden = YES;
  648. [self updateTopViewFrame];
  649. break;
  650. case kWFAVEngineStateConnecting:
  651. self.answerButton.hidden = YES;
  652. self.hangupButton.hidden = NO;
  653. self.speakerButton.hidden = YES;
  654. self.hangupButton.frame = [self getButtomCenterButtonFrame];
  655. self.switchCameraButton.hidden = YES;
  656. self.audioButton.hidden = YES;
  657. self.videoButton.hidden = YES;
  658. self.scalingButton.hidden = YES;
  659. [self.currentSession setupLocalVideoView:self.bigVideoView scalingType:self.bigScalingType];
  660. if (self.currentSession.audioOnly) {
  661. self.smallCollectionView.hidden = YES;
  662. self.portraitCollectionView.hidden = NO;
  663. [self.portraitCollectionView reloadData];
  664. self.portraitCollectionView.center = self.view.center;
  665. } else {
  666. self.smallCollectionView.hidden = NO;
  667. [self.smallCollectionView reloadData];
  668. self.portraitCollectionView.hidden = YES;
  669. }
  670. self.stateLabel.text = WFCString(@"CallConnecting");
  671. self.portraitView.hidden = YES;
  672. self.userNameLabel.hidden = YES;
  673. break;
  674. case kWFAVEngineStateConnected:
  675. self.answerButton.hidden = YES;
  676. self.hangupButton.hidden = NO;
  677. self.connectTimeLabel.hidden = NO;
  678. self.stateLabel.hidden = YES;
  679. self.hangupButton.frame = [self getButtomCenterButtonFrame];
  680. if (self.currentSession.isAudioOnly) {
  681. self.speakerButton.hidden = NO;
  682. self.speakerButton.frame = [self getButtomRightButtonFrame];
  683. [self updateSpeakerButton];
  684. self.audioButton.hidden = NO;
  685. self.audioButton.frame = [self getButtomLeftButtonFrame];
  686. self.switchCameraButton.hidden = YES;
  687. self.videoButton.hidden = YES;
  688. } else {
  689. self.speakerButton.hidden = YES;
  690. [self.currentSession enableSpeaker:YES];
  691. self.audioButton.hidden = NO;
  692. self.audioButton.frame = [self getButtomLeftButtonFrame];
  693. self.switchCameraButton.hidden = NO;
  694. self.switchCameraButton.frame = [self getButtomRightButtonFrame];
  695. self.videoButton.hidden = NO;
  696. }
  697. self.scalingButton.hidden = YES;
  698. self.minimizeButton.hidden = NO;
  699. self.addParticipantButton.hidden = NO;
  700. if (self.currentSession.isAudioOnly) {
  701. [self.currentSession setupLocalVideoView:nil scalingType:self.bigScalingType];
  702. self.smallCollectionView.hidden = YES;
  703. self.bigVideoView.hidden = YES;
  704. self.portraitCollectionView.hidden = NO;
  705. [self.portraitCollectionView reloadData];
  706. } else {
  707. NSString *lastUser = [self.participants lastObject];
  708. if ([lastUser isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  709. [self.currentSession setupLocalVideoView:self.bigVideoView scalingType:self.bigScalingType];
  710. } else {
  711. [self.currentSession setupRemoteVideoView:self.bigVideoView scalingType:self.bigScalingType forUser:lastUser];
  712. }
  713. self.smallCollectionView.hidden = NO;
  714. [self.smallCollectionView reloadData];
  715. self.bigVideoView.hidden = NO;
  716. self.portraitCollectionView.hidden = YES;
  717. }
  718. // if (!_currentSession.isAudioOnly) {
  719. self.userNameLabel.hidden = YES;
  720. self.portraitView.hidden = YES;
  721. // } else {
  722. // self.userNameLabel.hidden = NO;
  723. // self.portraitView.hidden = NO;
  724. // }
  725. [self updateConnectedTimeLabel];
  726. [self startConnectedTimer];
  727. [self updateTopViewFrame];
  728. break;
  729. case kWFAVEngineStateIncomming:
  730. self.connectTimeLabel.hidden = YES;
  731. self.answerButton.hidden = NO;
  732. self.answerButton.frame = [self getButtomRightButtonFrame];
  733. self.hangupButton.hidden = NO;
  734. self.hangupButton.frame = [self getButtomLeftButtonFrame];
  735. self.switchCameraButton.hidden = YES;
  736. self.audioButton.hidden = YES;
  737. self.videoButton.hidden = YES;
  738. self.scalingButton.hidden = YES;
  739. [self.currentSession setupLocalVideoView:self.bigVideoView scalingType:self.bigScalingType];
  740. self.stateLabel.text = WFCString(@"InvitingYou");
  741. self.smallCollectionView.hidden = YES;
  742. self.portraitCollectionView.hidden = NO;
  743. [self.portraitCollectionView reloadData];
  744. break;
  745. default:
  746. break;
  747. }
  748. }
  749. - (void)didCreateLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
  750. }
  751. - (void)didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack fromUser:(NSString *)userId {
  752. }
  753. - (void)didVideoMuted:(BOOL)videoMuted fromUser:(NSString *)userId {
  754. if ([self.participants.lastObject isEqualToString:userId]) {
  755. for (int i = 0; i < self.participants.count-1; i++) {
  756. NSString *pid = [self.participants objectAtIndex:i];
  757. if ([pid isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  758. if (!self.currentSession.myProfile.videoMuted) {
  759. [self switchVideoView:i];
  760. return;
  761. }
  762. continue;
  763. }
  764. for (WFAVParticipantProfile *p in self.currentSession.participants) {
  765. if ([p.userId isEqualToString:pid]) {
  766. if (!p.videoMuted && p.state == kWFAVEngineStateConnected) {
  767. [self switchVideoView:i];
  768. return;
  769. }
  770. break;
  771. }
  772. }
  773. }
  774. [self reloadVideoUI];
  775. } else {
  776. [self reloadVideoUI];
  777. }
  778. }
  779. - (void)didReportAudioVolume:(NSInteger)volume ofUser:(NSString *)userId {
  780. NSLog(@"user %@ report volume %ld", userId, volume);
  781. [[NSNotificationCenter defaultCenter] postNotificationName:@"wfavVolumeUpdated" object:userId userInfo:@{@"volume":@(volume)}];
  782. if (!self.currentSession.audioOnly && [userId isEqualToString:self.participants.lastObject]) {
  783. if (volume > 1000) {
  784. [self.bigVideoView bringSubviewToFront:self.speakingView];
  785. self.speakingView.hidden = NO;
  786. } else {
  787. self.speakingView.hidden = YES;
  788. }
  789. }
  790. }
  791. - (void)didCallEndWithReason:(WFAVCallEndReason)reason {
  792. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  793. [[WFAVEngineKit sharedEngineKit] dismissViewController:self];
  794. });
  795. }
  796. - (void)didParticipantJoined:(NSString *)userId {
  797. if ([self.participants containsObject:userId] || [userId isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  798. return;
  799. }
  800. [self.participants insertObject:userId atIndex:0];
  801. [self reloadVideoUI];
  802. }
  803. - (void)didParticipantConnected:(NSString *)userId {
  804. [self reloadVideoUI];
  805. }
  806. - (void)didParticipantLeft:(NSString *)userId withReason:(WFAVCallEndReason)reason {
  807. [self.participants removeObject:userId];
  808. [self reloadVideoUI];
  809. WFCCUserInfo *userInfo = [[WFCCIMService sharedWFCIMService] getUserInfo:userId inGroup:self.currentSession.conversation.type == Group_Type ? self.currentSession.conversation.target : nil refresh:NO];
  810. NSString *reasonStr;
  811. if (reason == kWFAVCallEndReasonTimeout) {
  812. reasonStr = @"未接听";
  813. } else if(reason == kWFAVCallEndReasonBusy) {
  814. reasonStr = @"网络忙";
  815. } else if(reason == kWFAVCallEndReasonRemoteHangup) {
  816. reasonStr = @"离开会议";
  817. } else {
  818. reasonStr = @"离开会议"; //"网络错误";
  819. }
  820. [self.view makeToast:[NSString stringWithFormat:@"%@ %@", userInfo.displayName, reasonStr] duration:1 position:CSToastPositionCenter];
  821. }
  822. - (void)didChangeMode:(BOOL)isAudioOnly {
  823. [self didChangeState:self.currentSession.state];
  824. }
  825. - (void)didError:(NSError *)error {
  826. }
  827. - (void)didGetStats:(NSArray *)stats {
  828. }
  829. - (void)checkAVPermission {
  830. [self checkCapturePermission:nil];
  831. [self checkRecordPermission:nil];
  832. }
  833. - (void)checkCapturePermission:(void (^)(BOOL granted))complete {
  834. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  835. if (authStatus == AVAuthorizationStatusDenied || authStatus == AVAuthorizationStatusRestricted) {
  836. if (complete) {
  837. complete(NO);
  838. }
  839. } else if (authStatus == AVAuthorizationStatusNotDetermined) {
  840. [AVCaptureDevice
  841. requestAccessForMediaType:AVMediaTypeVideo
  842. completionHandler:^(BOOL granted) {
  843. if (complete) {
  844. complete(granted);
  845. }
  846. }];
  847. } else {
  848. if (complete) {
  849. complete(YES);
  850. }
  851. }
  852. }
  853. - (void)checkRecordPermission:(void (^)(BOOL granted))complete {
  854. if ([[AVAudioSession sharedInstance] respondsToSelector:@selector(requestRecordPermission:)]) {
  855. [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
  856. if (complete) {
  857. complete(granted);
  858. }
  859. }];
  860. }
  861. }
  862. - (void)reloadVideoUI {
  863. if (!self.currentSession.audioOnly) {
  864. if (self.currentSession.state == kWFAVEngineStateConnecting || self.currentSession.state == kWFAVEngineStateConnected) {
  865. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
  866. CGFloat itemWidth = (self.view.frame.size.width + layout.minimumLineSpacing)/3 - layout.minimumLineSpacing;
  867. if (self.participants.count - 1 > 3) {
  868. self.smallCollectionView.frame = CGRectMake(0, kStatusBarAndNavigationBarHeight, self.view.frame.size.width, itemWidth * 2 + layout.minimumLineSpacing);
  869. } else {
  870. self.smallCollectionView.frame = CGRectMake(0, kStatusBarAndNavigationBarHeight, self.view.frame.size.width, itemWidth);
  871. }
  872. _speakingView.hidden = YES;
  873. NSString *userId = [self.participants lastObject];
  874. if ([userId isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  875. if (self.currentSession.myProfile.videoMuted) {
  876. [self.currentSession setupLocalVideoView:nil scalingType:self.bigScalingType];
  877. self.stateLabel.text = WFCString(@"VideoClosed");
  878. self.stateLabel.hidden = NO;
  879. } else {
  880. [self.currentSession setupLocalVideoView:self.bigVideoView scalingType:self.bigScalingType];
  881. self.stateLabel.text = nil;
  882. self.stateLabel.hidden = YES;
  883. }
  884. } else {
  885. for (WFAVParticipantProfile *profile in self.currentSession.participants) {
  886. if ([profile.userId isEqualToString:userId]) {
  887. if (profile.videoMuted) {
  888. [self.currentSession setupRemoteVideoView:nil scalingType:self.bigScalingType forUser:userId];
  889. self.stateLabel.text = WFCString(@"VideoClosed");
  890. self.stateLabel.hidden = NO;
  891. } else {
  892. [self.currentSession setupRemoteVideoView:self.bigVideoView scalingType:self.bigScalingType forUser:userId];
  893. self.stateLabel.text = nil;
  894. self.stateLabel.hidden = YES;
  895. }
  896. break;
  897. }
  898. }
  899. }
  900. [self.smallCollectionView reloadData];
  901. } else {
  902. [self.portraitCollectionView reloadData];
  903. }
  904. } else {
  905. [self.portraitCollectionView reloadData];
  906. }
  907. }
  908. - (BOOL)switchVideoView:(NSUInteger)index {
  909. NSString *userId = self.participants[index];
  910. BOOL canSwitch = NO;
  911. for (WFAVParticipantProfile *profile in self.currentSession.participants) {
  912. if ([profile.userId isEqualToString:userId]) {
  913. if (profile.state == kWFAVEngineStateConnected) {
  914. canSwitch = YES;
  915. }
  916. break;
  917. }
  918. }
  919. if ([userId isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  920. if (self.currentSession.state == kWFAVEngineStateConnected) {
  921. canSwitch = YES;
  922. }
  923. }
  924. if (canSwitch) {
  925. NSString *lastId = [self.participants lastObject];
  926. [self.participants removeLastObject];
  927. [self.participants insertObject:lastId atIndex:index];
  928. [self.participants removeObject:userId];
  929. [self.participants addObject:userId];
  930. }
  931. [self reloadVideoUI];
  932. return canSwitch;
  933. }
  934. #pragma mark - UICollectionViewDataSource
  935. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
  936. if (collectionView == self.portraitCollectionView) {
  937. if (self.currentSession.audioOnly && (self.currentSession.state == kWFAVEngineStateConnecting || self.currentSession.state == kWFAVEngineStateConnected)) {
  938. return self.participants.count;
  939. }
  940. }
  941. return self.participants.count - 1;
  942. }
  943. // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
  944. - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  945. NSString *userId = self.participants[indexPath.row];
  946. if (collectionView == self.smallCollectionView) {
  947. WFCUParticipantCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
  948. WFCCUserInfo *userInfo = [[WFCCIMService sharedWFCIMService] getUserInfo:userId inGroup:self.currentSession.conversation.type == Group_Type ? self.currentSession.conversation.target : nil refresh:NO];
  949. UIDevice *device = [UIDevice currentDevice] ;
  950. if (device.orientation == UIDeviceOrientationLandscapeLeft) {
  951. cell.transform = CGAffineTransformMakeRotation(M_PI_2);
  952. } else if (device.orientation == UIDeviceOrientationLandscapeRight) {
  953. cell.transform = CGAffineTransformMakeRotation(-M_PI_2);
  954. } else {
  955. cell.transform = CGAffineTransformMakeRotation(0);
  956. }
  957. if ([userId isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  958. WFAVParticipantProfile *profile = self.currentSession.myProfile;
  959. [cell setUserInfo:userInfo callProfile:profile];
  960. if (profile.videoMuted) {
  961. [self.currentSession setupLocalVideoView:nil scalingType:self.smallScalingType];
  962. } else {
  963. [self.currentSession setupLocalVideoView:cell scalingType:self.smallScalingType];
  964. }
  965. } else {
  966. for (WFAVParticipantProfile *profile in self.currentSession.participants) {
  967. if ([profile.userId isEqualToString:userId]) {
  968. [cell setUserInfo:userInfo callProfile:profile];
  969. if (profile.videoMuted) {
  970. [self.currentSession setupRemoteVideoView:nil scalingType:self.smallScalingType forUser:userId];
  971. } else {
  972. [self.currentSession setupRemoteVideoView:cell scalingType:self.smallScalingType forUser:userId];
  973. }
  974. break;
  975. }
  976. }
  977. }
  978. return cell;
  979. } else {
  980. WFCUPortraitCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell2" forIndexPath:indexPath];
  981. cell.itemSize = PortraitItemSize;
  982. cell.labelSize = PortraitLabelSize;
  983. WFCCUserInfo *userInfo = [[WFCCIMService sharedWFCIMService] getUserInfo:userId inGroup:self.currentSession.conversation.type == Group_Type ? self.currentSession.conversation.target : nil refresh:NO];
  984. cell.userInfo = userInfo;
  985. if ([userId isEqualToString:[WFCCNetworkService sharedInstance].userId]) {
  986. cell.profile = self.currentSession.myProfile;
  987. } else {
  988. for (WFAVParticipantProfile *profile in self.currentSession.participants) {
  989. if ([profile.userId isEqualToString:userId]) {
  990. cell.profile = profile;
  991. break;
  992. }
  993. }
  994. }
  995. return cell;
  996. }
  997. }
  998. #pragma mark - UICollectionViewDelegate
  999. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  1000. if (collectionView == self.smallCollectionView) {
  1001. [self switchVideoView:indexPath.row];
  1002. }
  1003. }
  1004. #endif
  1005. @end