NCCallController.m 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import "NCCallController.h"
  6. #import <WebRTC/RTCConfiguration.h>
  7. #import <WebRTC/RTCDataChannelConfiguration.h>
  8. #import <WebRTC/RTCMediaConstraints.h>
  9. #import <WebRTC/RTCMediaStream.h>
  10. #import <WebRTC/RTCPeerConnectionFactory.h>
  11. #import <WebRTC/RTCAudioTrack.h>
  12. #import <WebRTC/RTCVideoTrack.h>
  13. #import <WebRTC/RTCVideoCapturer.h>
  14. #import <WebRTC/RTCVideoSource.h>
  15. #import <WebRTC/RTCCameraVideoCapturer.h>
  16. #import <WebRTC/RTCDefaultVideoEncoderFactory.h>
  17. #import <WebRTC/RTCDefaultVideoDecoderFactory.h>
  18. #import "ARDCaptureController.h"
  19. #import "CallConstants.h"
  20. #import "CallKitManager.h"
  21. #import "NCAPIController.h"
  22. #import "NCAudioController.h"
  23. #import "NCDatabaseManager.h"
  24. #import "NCSettingsController.h"
  25. #import "NCSignalingController.h"
  26. #import "NCExternalSignalingController.h"
  27. #import "NCScreensharingController.h"
  28. #import "NextcloudTalk-Swift.h"
  29. static NSString * const kNCMediaStreamId = @"NCMS";
  30. static NSString * const kNCAudioTrackId = @"NCa0";
  31. static NSString * const kNCVideoTrackId = @"NCv0";
  32. static NSString * const kNCVideoTrackKind = @"video";
  33. static NSString * const kNCScreenTrackId = @"NCs0";
  34. static NSString * const kNCScreenTrackKind = @"screen";
  35. @interface NCCallController () <NCPeerConnectionDelegate, NCSignalingControllerObserver, NCExternalSignalingControllerDelegate, NCCameraControllerDelegate>
  36. @property (nonatomic, assign) BOOL isAudioOnly;
  37. @property (nonatomic, assign) BOOL leavingCall;
  38. @property (nonatomic, assign) BOOL preparedForRejoin;
  39. @property (nonatomic, assign) BOOL joinedCallOnce;
  40. @property (nonatomic, assign) BOOL shouldRejoinCallUsingInternalSignaling;
  41. @property (nonatomic, assign) BOOL serverSupportsConversationPermissions;
  42. @property (nonatomic, assign) NSInteger joinCallAttempts;
  43. @property (nonatomic, strong) AVAudioRecorder *recorder;
  44. @property (nonatomic, strong) NSTimer *micAudioLevelTimer;
  45. @property (nonatomic, assign) BOOL speaking;
  46. @property (nonatomic, assign) NSInteger userInCall;
  47. @property (nonatomic, assign) NSInteger userPermissions;
  48. @property (nonatomic, strong) NSTimer *sendCurrentStateTimer;
  49. @property (nonatomic, strong) NSArray *usersInRoom;
  50. @property (nonatomic, strong) NSArray *sessionsInCall;
  51. @property (nonatomic, strong) NSArray *peersInCall;
  52. @property (nonatomic, strong) NCPeerConnection *publisherPeerConnection;
  53. @property (nonatomic, strong) NCPeerConnection *screenPublisherPeerConnection;
  54. @property (nonatomic, strong) NSMutableDictionary *connectionsDict;
  55. @property (nonatomic, strong) NSMutableDictionary *pendingOffersDict;
  56. @property (nonatomic, strong) RTCAudioTrack *localAudioTrack;
  57. @property (nonatomic, strong) RTCVideoTrack *localVideoTrack;
  58. @property (nonatomic, strong) RTCVideoTrack *localScreenTrack;
  59. @property (nonatomic, strong) ARDCaptureController *localVideoCaptureController;
  60. @property (nonatomic, strong) NCSignalingController *signalingController;
  61. @property (nonatomic, strong) NCExternalSignalingController *externalSignalingController;
  62. @property (nonatomic, strong) TalkAccount *account;
  63. @property (nonatomic, strong) NSURLSessionTask *joinCallTask;
  64. @property (nonatomic, strong) NSURLSessionTask *getPeersForCallTask;
  65. @property (nonatomic, strong) NCCameraController *cameraController;
  66. @property (nonatomic, strong) NCScreensharingController *screensharingController;
  67. @end
  68. @implementation NCCallController
  69. - (instancetype)initWithDelegate:(id<NCCallControllerDelegate>)delegate inRoom:(NCRoom *)room forAudioOnlyCall:(BOOL)audioOnly withSessionId:(NSString *)sessionId andVoiceChatMode:(BOOL)voiceChatMode
  70. {
  71. self = [super init];
  72. if (self) {
  73. [NCUtils log:[NSString stringWithFormat:@"Creating call controller for token %@", room.token]];
  74. _delegate = delegate;
  75. _room = room;
  76. _userPermissions = _room.permissions;
  77. _isAudioOnly = audioOnly;
  78. _userSessionId = sessionId;
  79. _connectionsDict = [[NSMutableDictionary alloc] init];
  80. _pendingOffersDict = [[NSMutableDictionary alloc] init];
  81. _usersInRoom = [[NSArray alloc] init];
  82. _sessionsInCall = [[NSArray alloc] init];
  83. _peersInCall = [[NSArray alloc] init];
  84. _signalingController = [[NCSignalingController alloc] initForRoom:room];
  85. _signalingController.observer = self;
  86. _account = [[NCDatabaseManager sharedInstance] activeAccount];
  87. // NCCallController is only initialized after joining the room. At that point we ensured that there's
  88. // an external signaling controller set, in case we are using external signaling.
  89. _externalSignalingController = [[NCSettingsController sharedInstance] externalSignalingControllerForAccountId:_account.accountId];
  90. _externalSignalingController.delegate = self;
  91. // 'conversation-permissions' capability was not added in Talk 13 release, so we check for 'direct-mention-flag' capability
  92. // as a workaround.
  93. _serverSupportsConversationPermissions =
  94. [[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityConversationPermissions forAccountId:_account.accountId] ||
  95. [[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityDirectMentionFlag forAccountId:_account.accountId];
  96. [[WebRTCCommon shared] dispatch:^{
  97. if (audioOnly || voiceChatMode) {
  98. [[NCAudioController sharedInstance] setAudioSessionToVoiceChatMode];
  99. } else {
  100. [[NCAudioController sharedInstance] setAudioSessionToVideoChatMode];
  101. }
  102. }];
  103. [self initRecorder];
  104. // Screensharing is done in an extension, therefore we need to listen to systemwide notifications#
  105. [[DarwinNotificationCenter shared] addHandlerWithNotificationName:DarwinNotificationCenter.broadcastStartedNotification owner:self completionBlock:^{
  106. [[WebRTCCommon shared] dispatch:^{
  107. [self startScreenshare];
  108. }];
  109. }];
  110. [[DarwinNotificationCenter shared] addHandlerWithNotificationName:DarwinNotificationCenter.broadcastStoppedNotification owner:self completionBlock:^{
  111. [[WebRTCCommon shared] dispatch:^{
  112. [self stopScreenshare];
  113. }];
  114. }];
  115. _screensharingController = [[NCScreensharingController alloc] init];
  116. [[AllocationTracker shared] addAllocation:@"NCCallController"];
  117. }
  118. return self;
  119. }
  120. - (void)startCall
  121. {
  122. [NCUtils log:[NSString stringWithFormat:@"Start call in NCCallController for token %@", self.room.token]];
  123. // Make sure the signaling controller has retrieved the settings before joining a call
  124. [_signalingController updateSignalingSettingsWithCompletionBlock:^(SignalingSettings *signalingSettings) {
  125. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  126. if (!self->_isAudioOnly && authStatus == AVAuthorizationStatusNotDetermined) {
  127. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  128. [self createLocalMedia];
  129. [self joinCall];
  130. }];
  131. return;
  132. }
  133. [self createLocalMedia];
  134. [self joinCall];
  135. }];
  136. }
  137. - (NSString *)signalingSessionId
  138. {
  139. if (_externalSignalingController) {
  140. return [_externalSignalingController sessionId];
  141. }
  142. return _userSessionId;
  143. }
  144. - (NSInteger)joinCallFlags
  145. {
  146. NSInteger flags = CallFlagInCall;
  147. if ((_userPermissions & NCPermissionCanPublishAudio) != 0 || !_serverSupportsConversationPermissions) {
  148. flags += CallFlagWithAudio;
  149. }
  150. if (!_isAudioOnly && ((_userPermissions & NCPermissionCanPublishVideo) != 0 || !_serverSupportsConversationPermissions)) {
  151. flags += CallFlagWithVideo;
  152. }
  153. return flags;
  154. }
  155. - (void)joinCall
  156. {
  157. [NCUtils log:[NSString stringWithFormat:@"Join call in NCCallController for token %@", self.room.token]];
  158. _joinCallTask = [[NCAPIController sharedInstance] joinCall:_room.token withCallFlags:[self joinCallFlags] silently:_silentCall recordingConsent:_recordingConsent forAccount:_account withCompletionBlock:^(NSError *error, NSInteger statusCode) {
  159. [[WebRTCCommon shared] dispatch:^{
  160. if (!error) {
  161. [NCUtils log:[NSString stringWithFormat:@"Did join call in NCCallController for token %@", self.room.token]];
  162. [self.delegate callControllerDidJoinCall:self];
  163. [self getPeersForCall];
  164. [self startMonitoringMicrophoneAudioLevel];
  165. if (self->_externalSignalingController) {
  166. if ([self->_externalSignalingController hasMCU]) {
  167. [self createPublisherPeerConnection];
  168. }
  169. } else {
  170. [self->_signalingController startPullingSignalingMessages];
  171. }
  172. self->_joinedCallOnce = YES;
  173. self->_joinCallAttempts = 0;
  174. } else {
  175. if (error.code == NSURLErrorCancelled) {
  176. self->_joinCallAttempts = 0;
  177. return;
  178. }
  179. if (self->_joinCallAttempts < 3) {
  180. [NCUtils log:[NSString stringWithFormat:@"Could not join call in %@, retrying. %ld", self.room.token, self.joinCallAttempts]];
  181. self->_joinCallAttempts += 1;
  182. if (statusCode == 404) {
  183. // The conversation was not correctly joined by us / our session expired
  184. // Instead of joining again, try to reconnect to correctly join the conversation again
  185. [self forceReconnect];
  186. } else {
  187. [self joinCall];
  188. }
  189. return;
  190. }
  191. [self.delegate callControllerDidFailedJoiningCall:self statusCode:statusCode errorReason:[self getJoinCallErrorReason:statusCode]];
  192. [NCUtils log:[NSString stringWithFormat:@"Could not join call in %@, StatusCode: %ld, Error: %@", self.room.token, statusCode, error.description]];
  193. }
  194. }];
  195. }];
  196. }
  197. - (NSString *)getJoinCallErrorReason:(NSInteger)statusCode
  198. {
  199. NSString *errorReason = NSLocalizedString(@"Unknown error occurred", nil);
  200. switch (statusCode) {
  201. case 0:
  202. errorReason = NSLocalizedString(@"No response from server", nil);
  203. break;
  204. case 400:
  205. errorReason = NSLocalizedString(@"Recording consent is required", nil);
  206. break;
  207. case 403:
  208. errorReason = NSLocalizedString(@"This conversation is read-only", nil);
  209. break;
  210. case 404:
  211. errorReason = NSLocalizedString(@"Conversation not found or not joined", nil);
  212. break;
  213. case 412:
  214. errorReason = NSLocalizedString(@"Lobby is still active and you're not a moderator", nil);
  215. break;
  216. }
  217. return errorReason;
  218. }
  219. - (void)shouldRejoinCall
  220. {
  221. [self createLocalMedia];
  222. _joinCallTask = [[NCAPIController sharedInstance] joinCall:_room.token withCallFlags:[self joinCallFlags] silently:_silentCall recordingConsent:_recordingConsent forAccount:_account withCompletionBlock:^(NSError *error, NSInteger statusCode) {
  223. [[WebRTCCommon shared] dispatch:^{
  224. if (!error) {
  225. [self.delegate callControllerDidJoinCall:self];
  226. NSLog(@"Rejoined call");
  227. if ([self->_externalSignalingController hasMCU]) {
  228. [self createPublisherPeerConnection];
  229. }
  230. self->_joinCallAttempts = 0;
  231. } else {
  232. if (self->_joinCallAttempts < 3) {
  233. NSLog(@"Could not rejoin call, retrying. %ld", (long)self->_joinCallAttempts);
  234. self->_joinCallAttempts += 1;
  235. [self shouldRejoinCall];
  236. return;
  237. }
  238. [self.delegate callControllerDidFailedJoiningCall:self statusCode:statusCode errorReason:[self getJoinCallErrorReason:statusCode]];
  239. NSLog(@"Could not rejoin call. Error: %@", error.description);
  240. }
  241. }];
  242. }];
  243. }
  244. - (void)willRejoinCall
  245. {
  246. NSLog(@"willRejoinCall");
  247. [[WebRTCCommon shared] dispatch:^{
  248. self->_userInCall = 0;
  249. [self cleanCurrentPeerConnections];
  250. [self.delegate callControllerIsReconnectingCall:self];
  251. self->_preparedForRejoin = YES;
  252. }];
  253. }
  254. - (void)willSwitchToCall:(NSString *)token
  255. {
  256. NSLog(@"willSwitchToCall");
  257. [[WebRTCCommon shared] dispatch:^{
  258. BOOL isAudioEnabled = [self isAudioEnabled];
  259. BOOL isVideoEnabled = [self isVideoEnabled];
  260. [self stopCallController];
  261. [self leaveCallInServerForAll:NO withCompletionBlock:^(NSError *error) {
  262. if (error) {
  263. NSLog(@"Could not leave call. Error: %@", error.description);
  264. }
  265. [self.delegate callController:self isSwitchingToCall:token withAudioEnabled:isAudioEnabled andVideoEnabled:isVideoEnabled];
  266. }];
  267. }];
  268. }
  269. - (void)forceReconnect
  270. {
  271. [NCUtils log:@"Force reconnect"];
  272. [[WebRTCCommon shared] dispatch:^{
  273. [self.joinCallTask cancel];
  274. self.joinCallTask = nil;
  275. self->_userInCall = 0;
  276. [self cleanCurrentPeerConnections];
  277. [self.delegate callControllerIsReconnectingCall:self];
  278. // Remember current audio and video status before rejoin the call
  279. self->_disableAudioAtStart = ![self isAudioEnabled];
  280. self->_disableVideoAtStart = ![self isVideoEnabled];
  281. if (!self->_externalSignalingController) {
  282. [self rejoinCallUsingInternalSignaling];
  283. return;
  284. }
  285. [self->_externalSignalingController forceReconnectForRejoin];
  286. }];
  287. }
  288. - (void)rejoinCallUsingInternalSignaling
  289. {
  290. [[NCAPIController sharedInstance] leaveCall:_room.token forAllParticipants:NO forAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSError *error) {
  291. if (!error) {
  292. self->_shouldRejoinCallUsingInternalSignaling = YES;
  293. }
  294. }];
  295. }
  296. - (void)stopCallController
  297. {
  298. [self setLeavingCall:YES];
  299. [self stopSendingCurrentState];
  300. [[NSNotificationCenter defaultCenter] removeObserver:self];
  301. [[DarwinNotificationCenter shared] removeHandlerWithNotificationName:DarwinNotificationCenter.broadcastStartedNotification owner:self];
  302. [[DarwinNotificationCenter shared] removeHandlerWithNotificationName:DarwinNotificationCenter.broadcastStoppedNotification owner:self];
  303. _externalSignalingController.delegate = nil;
  304. [self->_cameraController stopAVCaptureSession];
  305. [[WebRTCCommon shared] dispatch:^{
  306. [self stopScreenshare];
  307. [self cleanCurrentPeerConnections];
  308. self->_localAudioTrack = nil;
  309. self->_localVideoTrack = nil;
  310. self->_connectionsDict = nil;
  311. }];
  312. [self stopMonitoringMicrophoneAudioLevel];
  313. [_signalingController stopAllRequests];
  314. [_getPeersForCallTask cancel];
  315. _getPeersForCallTask = nil;
  316. [_joinCallTask cancel];
  317. _joinCallTask = nil;
  318. }
  319. - (void)leaveCallInServerForAll:(BOOL)allParticipants withCompletionBlock:(LeaveCallCompletionBlock)block
  320. {
  321. if (_userInCall) {
  322. [[NCAPIController sharedInstance] leaveCall:_room.token forAllParticipants:allParticipants forAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSError *error) {
  323. block(error);
  324. }];
  325. } else {
  326. block(nil);
  327. }
  328. }
  329. - (void)leaveCallForAll:(BOOL)allParticipants
  330. {
  331. [self stopCallController];
  332. [self leaveCallInServerForAll:allParticipants withCompletionBlock:^(NSError *error) {
  333. if (error) {
  334. NSLog(@"Could not leave call. Error: %@", error.description);
  335. }
  336. [self.delegate callControllerDidEndCall:self];
  337. }];
  338. [[WebRTCCommon shared] dispatch:^{
  339. [[NCAudioController sharedInstance] disableAudioSession];
  340. }];
  341. }
  342. - (void)dealloc
  343. {
  344. [[NSNotificationCenter defaultCenter] removeObserver:self];
  345. [[DarwinNotificationCenter shared] removeHandlerWithNotificationName:DarwinNotificationCenter.broadcastStartedNotification owner:self];
  346. [[DarwinNotificationCenter shared] removeHandlerWithNotificationName:DarwinNotificationCenter.broadcastStoppedNotification owner:self];
  347. [[AllocationTracker shared] removeAllocation:@"NCCallController"];
  348. NSLog(@"NCCallController dealloc");
  349. }
  350. - (BOOL)isVideoEnabled
  351. {
  352. [[WebRTCCommon shared] assertQueue];
  353. return _localVideoTrack ? _localVideoTrack.isEnabled : NO;
  354. }
  355. - (BOOL)isAudioEnabled
  356. {
  357. [[WebRTCCommon shared] assertQueue];
  358. return _localAudioTrack ? _localAudioTrack.isEnabled : NO;
  359. }
  360. - (void)getVideoEnabledStateWithCompletionBlock:(GetAudioEnabledStateCompletionBlock)block
  361. {
  362. [[WebRTCCommon shared] dispatch:^{
  363. if (block) {
  364. block([self isVideoEnabled]);
  365. }
  366. }];
  367. }
  368. - (void)getAudioEnabledStateWithCompletionBlock:(GetAudioEnabledStateCompletionBlock)block
  369. {
  370. [[WebRTCCommon shared] dispatch:^{
  371. if (block) {
  372. block([self isAudioEnabled]);
  373. }
  374. }];
  375. }
  376. - (void)switchCamera
  377. {
  378. [self.cameraController switchCamera];
  379. }
  380. - (void)enableVideo:(BOOL)enable
  381. {
  382. [[WebRTCCommon shared] dispatch:^{
  383. if (enable) {
  384. [self->_localVideoCaptureController startCapture];
  385. } else {
  386. [self->_localVideoCaptureController stopCapture];
  387. }
  388. [self->_localVideoTrack setIsEnabled:enable];
  389. [self sendMessageToAllOfType:enable ? @"videoOn" : @"videoOff" withPayload:nil];
  390. }];
  391. }
  392. - (void)enableAudio:(BOOL)enable
  393. {
  394. [[WebRTCCommon shared] dispatch:^{
  395. [self->_localAudioTrack setIsEnabled:enable];
  396. [self sendMessageToAllOfType:enable ? @"audioOn" : @"audioOff" withPayload:nil];
  397. if (!enable) {
  398. self->_speaking = NO;
  399. [self sendMessageToAllOfType:@"stoppedSpeaking" withPayload:nil];
  400. }
  401. }];
  402. }
  403. - (BOOL)isBackgroundBlurEnabled
  404. {
  405. return [self.cameraController isBackgroundBlurEnabled];
  406. }
  407. - (void)enableBackgroundBlur:(BOOL)enable
  408. {
  409. [self.cameraController enableBackgroundBlurWithEnable:enable];
  410. }
  411. - (BOOL)isCameraAccessAvailable {
  412. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  413. return (authStatus == AVAuthorizationStatusAuthorized);
  414. }
  415. - (BOOL)isMicrophoneAccessAvailable
  416. {
  417. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  418. return (authStatus == AVAuthorizationStatusAuthorized);
  419. }
  420. - (void)stopCapturing
  421. {
  422. [self.cameraController stopAVCaptureSession];
  423. }
  424. - (void)raiseHand:(BOOL)raised
  425. {
  426. [[WebRTCCommon shared] dispatch:^{
  427. NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970] * 1000;
  428. NSDictionary *payload = @{
  429. @"state": @(raised),
  430. @"timestamp": [NSString stringWithFormat:@"%.0f", timeStamp]
  431. };
  432. for (NCPeerConnection *peer in [self->_connectionsDict allValues]) {
  433. NCRaiseHandMessage *message = [[NCRaiseHandMessage alloc] initWithFrom:[self signalingSessionId]
  434. to:peer.peerId
  435. sid:peer.sid
  436. roomType:peer.roomType
  437. payload:payload];
  438. if (self->_externalSignalingController) {
  439. [self->_externalSignalingController sendCallMessage:message];
  440. } else {
  441. [self->_signalingController sendSignalingMessage:message];
  442. }
  443. }
  444. }];
  445. // Request or stop requesting assistance if we are in a breakout room and we are not moderators
  446. if (![_room isBreakoutRoom] || _room.canModerate) {
  447. return;
  448. }
  449. if (raised) {
  450. [[NCAPIController sharedInstance] requestAssistanceInRoom:_room.token forAccount:_account withCompletionBlock:^(NSError *error) {
  451. if (error) {
  452. NSLog(@"Error requesting assistance");
  453. }
  454. }];
  455. } else {
  456. [[NCAPIController sharedInstance] stopRequestingAssistanceInRoom:_room.token forAccount:_account withCompletionBlock:^(NSError *error) {
  457. if (error) {
  458. NSLog(@"Error on stop requesting assisntance");
  459. }
  460. }];
  461. }
  462. }
  463. - (void)sendReaction:(NSString *)reaction
  464. {
  465. [[WebRTCCommon shared] dispatch:^{
  466. NSDictionary *payload = @{
  467. @"reaction": reaction
  468. };
  469. for (NCPeerConnection *peer in [self->_connectionsDict allValues]) {
  470. NCReactionMessage *message = [[NCReactionMessage alloc] initWithFrom:[self signalingSessionId]
  471. to:peer.peerId
  472. sid:peer.sid
  473. roomType:peer.roomType
  474. payload:payload];
  475. if (self->_externalSignalingController) {
  476. [self->_externalSignalingController sendCallMessage:message];
  477. } else {
  478. [self->_signalingController sendSignalingMessage:message];
  479. }
  480. }
  481. }];
  482. }
  483. - (void)startRecording
  484. {
  485. [[NCAPIController sharedInstance] startRecording:_room.token forAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSError *error) {
  486. if (error) {
  487. NSLog(@"Could not start call recording. Error: %@", error.description);
  488. }
  489. }];
  490. }
  491. - (void)stopRecording
  492. {
  493. [[NCAPIController sharedInstance] stopRecording:_room.token forAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSError *error) {
  494. if (error) {
  495. NSLog(@"Could not stop call recording. Error: %@", error.description);
  496. }
  497. }];
  498. }
  499. - (void)startScreenshare
  500. {
  501. [[WebRTCCommon shared] assertQueue];
  502. if (_screensharingActive) {
  503. return;
  504. }
  505. RTCPeerConnectionFactory *peerConnectionFactory = [WebRTCCommon shared].peerConnectionFactory;
  506. RTCVideoSource *videoSource = [peerConnectionFactory videoSource];
  507. RTCVideoCapturer *videoCapturer = [[RTCVideoCapturer alloc] initWithDelegate:videoSource];
  508. [_screensharingController startCaptureWithVideoSource:videoSource withVideoCapturer:videoCapturer];
  509. _localScreenTrack = [peerConnectionFactory videoTrackWithSource:videoSource trackId:kNCScreenTrackId];
  510. if (_externalSignalingController && [_externalSignalingController hasMCU]) {
  511. [self createScreenPublisherPeerConnection];
  512. } else {
  513. for (NSString *session in _sessionsInCall) {
  514. if (![session isEqualToString:[self signalingSessionId]]) {
  515. [self sendScreensharingOfferToSessionId:session];
  516. }
  517. }
  518. }
  519. _screensharingActive = YES;
  520. [self.delegate callControllerDidChangeScreenrecording:self];
  521. }
  522. - (void)sendScreensharingOfferToSessionId:(NSString *)sessionId
  523. {
  524. NCPeerConnection *peerConnectionWrapper = [self getOrCreatePeerConnectionWrapperForSessionId:sessionId withSid:nil ofType:kRoomTypeScreen forOwnScreenshare:YES];
  525. [peerConnectionWrapper sendPublisherOffer];
  526. }
  527. - (void)stopScreenshare {
  528. [[WebRTCCommon shared] assertQueue];
  529. [_screensharingController stopCapture];
  530. if (_externalSignalingController) {
  531. // Close screen publisher peer connection
  532. [self->_screenPublisherPeerConnection close];
  533. self->_screenPublisherPeerConnection = nil;
  534. NSString *peerKey = [self getPeerKeyWithSessionId:[self signalingSessionId] ofType:kRoomTypeScreen forOwnScreenshare:YES];
  535. [_connectionsDict removeObjectForKey:peerKey];
  536. // Send unshare screen signaling message to all the other peers
  537. [_externalSignalingController sendRoomMessageOfType:@"unshareScreen" andRoomType:kRoomTypeScreen];
  538. } else {
  539. for (NCPeerConnection *peer in [self->_connectionsDict allValues]) {
  540. // Close all own screen peer connections
  541. if (peer.isOwnScreensharePeer) {
  542. [self cleanPeerConnectionForSessionId:peer.peerId ofType:kRoomTypeScreen forOwnScreenshare:YES];
  543. }
  544. // Send unshare screen signaling message to all the other peers
  545. else {
  546. NCSignalingMessage *message = [[NCUnshareScreenMessage alloc] initWithFrom:[self signalingSessionId]
  547. to:peer.peerId
  548. sid:peer.sid
  549. roomType:peer.roomType
  550. payload:@{}];
  551. [_signalingController sendSignalingMessage:message];
  552. }
  553. }
  554. }
  555. _screensharingActive = NO;
  556. [self.delegate callControllerDidChangeScreenrecording:self];
  557. }
  558. #pragma mark - Call controller
  559. - (void)cleanCurrentPeerConnections
  560. {
  561. [[WebRTCCommon shared] assertQueue];
  562. for (NCPeerConnection *peerConnectionWrapper in [_connectionsDict allValues]) {
  563. if (!peerConnectionWrapper.isMCUPublisherPeer) {
  564. if ([peerConnectionWrapper.roomType isEqualToString:kRoomTypeVideo]) {
  565. [self.delegate callController:self peerLeft:peerConnectionWrapper];
  566. } else if ([peerConnectionWrapper.roomType isEqualToString:kRoomTypeScreen]) {
  567. [self.delegate callController:self didReceiveUnshareScreenFromPeer:peerConnectionWrapper];
  568. }
  569. }
  570. peerConnectionWrapper.delegate = nil;
  571. [peerConnectionWrapper close];
  572. }
  573. for (NSTimer *pendingOfferTimer in [_pendingOffersDict allValues]) {
  574. [pendingOfferTimer invalidate];
  575. }
  576. _connectionsDict = [[NSMutableDictionary alloc] init];
  577. _pendingOffersDict = [[NSMutableDictionary alloc] init];
  578. _usersInRoom = [[NSArray alloc] init];
  579. _sessionsInCall = [[NSArray alloc] init];
  580. _publisherPeerConnection = nil;
  581. _screenPublisherPeerConnection = nil;
  582. }
  583. - (void)cleanPeerConnectionForSessionId:(NSString *)sessionId ofType:(NSString *)roomType forOwnScreenshare:(BOOL)ownScreenshare
  584. {
  585. [[WebRTCCommon shared] assertQueue];
  586. NSString *peerKey = [self getPeerKeyWithSessionId:sessionId ofType:roomType forOwnScreenshare:ownScreenshare];
  587. NCPeerConnection *removedPeerConnection = [_connectionsDict objectForKey:peerKey];
  588. if (removedPeerConnection) {
  589. if ([roomType isEqualToString:kRoomTypeVideo]) {
  590. NSLog(@"Removing peer from call: %@", sessionId);
  591. [self.delegate callController:self peerLeft:removedPeerConnection];
  592. } else if ([roomType isEqualToString:kRoomTypeScreen] && !ownScreenshare) {
  593. NSLog(@"Removing screensharing from peer: %@", sessionId);
  594. [self.delegate callController:self didReceiveUnshareScreenFromPeer:removedPeerConnection];
  595. }
  596. removedPeerConnection.delegate = nil;
  597. [removedPeerConnection close];
  598. [_connectionsDict removeObjectForKey:peerKey];
  599. }
  600. }
  601. - (void)cleanAllPeerConnectionsForSessionId:(NSString *)sessionId
  602. {
  603. [[WebRTCCommon shared] assertQueue];
  604. [self cleanPeerConnectionForSessionId:sessionId ofType:kRoomTypeVideo forOwnScreenshare:NO];
  605. [self cleanPeerConnectionForSessionId:sessionId ofType:kRoomTypeScreen forOwnScreenshare:NO];
  606. // Invalidate possible request timers
  607. NSString *peerVideoKey = [sessionId stringByAppendingString:kRoomTypeVideo];
  608. NSTimer *pendingVideoRequestTimer = [_pendingOffersDict objectForKey:peerVideoKey];
  609. if (pendingVideoRequestTimer) {
  610. dispatch_async(dispatch_get_main_queue(), ^{
  611. [pendingVideoRequestTimer invalidate];
  612. });
  613. }
  614. NSString *peerScreenKey = [sessionId stringByAppendingString:kRoomTypeVideo];
  615. NSTimer *pendingScreenRequestTimer = [_pendingOffersDict objectForKey:peerScreenKey];
  616. if (pendingScreenRequestTimer) {
  617. dispatch_async(dispatch_get_main_queue(), ^{
  618. [pendingScreenRequestTimer invalidate];
  619. });
  620. }
  621. }
  622. #pragma mark - Microphone audio level
  623. - (void)startMonitoringMicrophoneAudioLevel
  624. {
  625. dispatch_async(dispatch_get_main_queue(), ^{
  626. self->_micAudioLevelTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkMicAudioLevel) userInfo:nil repeats:YES];
  627. });
  628. }
  629. - (void)stopMonitoringMicrophoneAudioLevel
  630. {
  631. dispatch_async(dispatch_get_main_queue(), ^{
  632. [self->_micAudioLevelTimer invalidate];
  633. self->_micAudioLevelTimer = nil;
  634. [self->_recorder stop];
  635. self->_recorder = nil;
  636. });
  637. }
  638. - (void)initRecorder
  639. {
  640. NSURL *url = [NSURL fileURLWithPath:@"/dev/null"];
  641. NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
  642. [NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
  643. [NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
  644. [NSNumber numberWithInt: 0], AVNumberOfChannelsKey,
  645. [NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
  646. nil];
  647. NSError *error;
  648. _recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
  649. if (_recorder) {
  650. [_recorder prepareToRecord];
  651. _recorder.meteringEnabled = YES;
  652. [_recorder record];
  653. } else {
  654. NSLog(@"Failed initializing recorder.");
  655. }
  656. }
  657. - (void)checkMicAudioLevel
  658. {
  659. [[WebRTCCommon shared] dispatch:^{
  660. if ([self isAudioEnabled]) {
  661. [self->_recorder updateMeters];
  662. float averagePower = [self->_recorder averagePowerForChannel:0];
  663. if (averagePower >= -50.0f && !self->_speaking) {
  664. self->_speaking = YES;
  665. [self sendMessageToAllOfType:@"speaking" withPayload:nil];
  666. } else if (averagePower < -50.0f && self->_speaking) {
  667. self->_speaking = NO;
  668. [self sendMessageToAllOfType:@"stoppedSpeaking" withPayload:nil];
  669. }
  670. }
  671. }];
  672. }
  673. #pragma mark - Call participants
  674. - (void)getPeersForCall
  675. {
  676. _getPeersForCallTask = [[NCAPIController sharedInstance] getPeersForCall:_room.token forAccount:_account withCompletionBlock:^(NSMutableArray *peers, NSError *error, NSInteger statusCode) {
  677. if (error) {
  678. return;
  679. }
  680. [[WebRTCCommon shared] dispatch:^{
  681. self->_peersInCall = peers;
  682. }];
  683. }];
  684. }
  685. #pragma mark - Audio & Video senders
  686. - (void)createLocalAudioTrack
  687. {
  688. [[WebRTCCommon shared] assertQueue];
  689. RTCPeerConnectionFactory *peerConnectionFactory = [WebRTCCommon shared].peerConnectionFactory;
  690. RTCAudioSource *source = [peerConnectionFactory audioSourceWithConstraints:nil];
  691. _localAudioTrack = [peerConnectionFactory audioTrackWithSource:source trackId:kNCAudioTrackId];
  692. [_localAudioTrack setIsEnabled:!_disableAudioAtStart];
  693. if ([CallKitManager isCallKitAvailable]) {
  694. [[CallKitManager sharedInstance] changeAudioMuted:_disableAudioAtStart forCall:_room.token];
  695. }
  696. [self.delegate callController:self didCreateLocalAudioTrack:_localAudioTrack];
  697. }
  698. - (void)createLocalVideoTrack
  699. {
  700. [[WebRTCCommon shared] assertQueue];
  701. #if !TARGET_IPHONE_SIMULATOR
  702. RTCPeerConnectionFactory *peerConnectionFactory = [WebRTCCommon shared].peerConnectionFactory;
  703. RTCVideoSource *videoSource = [peerConnectionFactory videoSource];
  704. RTCVideoCapturer *videoCapturer = [[RTCVideoCapturer alloc] initWithDelegate:videoSource];
  705. _localVideoTrack = [peerConnectionFactory videoTrackWithSource:videoSource trackId:kNCVideoTrackId];
  706. [_localVideoTrack setIsEnabled:!_disableVideoAtStart];
  707. [self.delegate callController:self didCreateLocalVideoTrack:_localVideoTrack];
  708. self.cameraController = [[NCCameraController alloc] initWithVideoSource:videoSource videoCapturer:videoCapturer];
  709. self.cameraController.delegate = self;
  710. [self.delegate callController:self didCreateCameraController:self.cameraController];
  711. #endif
  712. }
  713. - (void)createLocalMedia
  714. {
  715. [self->_cameraController stopAVCaptureSession];
  716. [[WebRTCCommon shared] dispatch:^{
  717. self->_localAudioTrack = nil;
  718. self->_localVideoTrack = nil;
  719. BOOL hasPublishAudioPermission = ((self->_userPermissions & NCPermissionCanPublishAudio) != 0 || !self->_serverSupportsConversationPermissions);
  720. if (hasPublishAudioPermission && [self isMicrophoneAccessAvailable]) {
  721. [self createLocalAudioTrack];
  722. } else {
  723. [self.delegate callController:self didCreateLocalAudioTrack:nil];
  724. }
  725. BOOL hasPublishVideoPermission = ((self->_userPermissions & NCPermissionCanPublishVideo) != 0 || !self->_serverSupportsConversationPermissions);
  726. if (!self->_isAudioOnly && hasPublishVideoPermission && [self isCameraAccessAvailable]) {
  727. [self createLocalVideoTrack];
  728. } else {
  729. [self.delegate callController:self didCreateLocalVideoTrack:nil];
  730. }
  731. }];
  732. }
  733. #pragma mark - Peer Connection Wrapper
  734. - (NSString *)getPeerKeyWithSessionId:(NSString *)sessionId ofType:(NSString *)roomType forOwnScreenshare:(BOOL)ownScreenshare
  735. {
  736. NSString *peerKey = [sessionId stringByAppendingString:roomType];
  737. if (ownScreenshare) {
  738. // If this is our own screensharing peer, we add "own" to the key, to distinguish our peer
  739. // to a receiving peer in case we are using internal signaling
  740. peerKey = [peerKey stringByAppendingString:@"own"];
  741. }
  742. return peerKey;
  743. }
  744. - (NCPeerConnection *)getPeerConnectionWrapperForSessionId:(NSString *)sessionId ofType:(NSString *)roomType
  745. {
  746. return [self getPeerConnectionWrapperForSessionId:sessionId ofType:roomType forOwnScreenshare:NO];
  747. }
  748. - (NCPeerConnection *)getPeerConnectionWrapperForSessionId:(NSString *)sessionId ofType:(NSString *)roomType forOwnScreenshare:(BOOL)ownScreenshare
  749. {
  750. [[WebRTCCommon shared] assertQueue];
  751. NSString *peerKey = [self getPeerKeyWithSessionId:sessionId ofType:roomType forOwnScreenshare:ownScreenshare];
  752. NCPeerConnection *peerConnectionWrapper = [_connectionsDict objectForKey:peerKey];
  753. return peerConnectionWrapper;
  754. }
  755. - (NCPeerConnection *)getOrCreatePeerConnectionWrapperForSessionId:(NSString *)sessionId withSid:(NSString *)sid ofType:(NSString *)roomType
  756. {
  757. return [self getOrCreatePeerConnectionWrapperForSessionId:sessionId withSid:sid ofType:roomType forOwnScreenshare:NO];
  758. }
  759. - (NCPeerConnection *)getOrCreatePeerConnectionWrapperForSessionId:(NSString *)sessionId withSid:(NSString *)sid ofType:(NSString *)roomType forOwnScreenshare:(BOOL)ownScreenshare
  760. {
  761. [[WebRTCCommon shared] assertQueue];
  762. NSString *peerKey = [self getPeerKeyWithSessionId:sessionId ofType:roomType forOwnScreenshare:ownScreenshare];
  763. NCPeerConnection *peerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:sessionId ofType:roomType forOwnScreenshare:ownScreenshare];
  764. // When using internal signaling, if you and another participant are sharing the screen and you receive a candidate message
  765. // we can not know whether the message is for the sending or the received screen share only from the "from" field and the type.
  766. // We need to use the "sid"
  767. BOOL screensharingPeer = [roomType isEqualToString:kRoomTypeScreen];
  768. if (screensharingPeer) {
  769. // We check if the signaling message was send to our own screen peer.
  770. // If the "sid" doesn't match, we have grabbed the correct peer connection above (if it existed)
  771. NCPeerConnection *ownScreenPeerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:sessionId ofType:roomType forOwnScreenshare:YES];
  772. if (ownScreenPeerConnectionWrapper && [ownScreenPeerConnectionWrapper.sid isEqualToString:sid]) {
  773. peerConnectionWrapper = ownScreenPeerConnectionWrapper;
  774. }
  775. }
  776. if (!peerConnectionWrapper) {
  777. // Create peer connection.
  778. NSLog(@"Creating a peer for %@", sessionId);
  779. NSArray *iceServers = [_signalingController getIceServers];
  780. peerConnectionWrapper = [[NCPeerConnection alloc] initWithSessionId:sessionId sid:sid andICEServers:iceServers forAudioOnlyCall:screensharingPeer ? NO : _isAudioOnly];
  781. peerConnectionWrapper.roomType = roomType;
  782. peerConnectionWrapper.delegate = self;
  783. peerConnectionWrapper.isOwnScreensharePeer = ownScreenshare;
  784. // Try to get displayName early
  785. TalkActor *actor = [self getActorFromSessionId:sessionId];
  786. if (actor && ![actor.rawDisplayName isEqualToString:@""]) {
  787. [peerConnectionWrapper setPeerName:actor.displayName];
  788. }
  789. // Do not add local stream when using a MCU or to screensharing peers
  790. if (![_externalSignalingController hasMCU]) {
  791. RTCPeerConnection *peerConnection = [peerConnectionWrapper getPeerConnection];
  792. if (!screensharingPeer) {
  793. if (_localAudioTrack) {
  794. [peerConnection addTrack:_localAudioTrack streamIds:@[kNCMediaStreamId]];
  795. }
  796. if (_localVideoTrack) {
  797. [peerConnection addTrack:_localVideoTrack streamIds:@[kNCMediaStreamId]];
  798. }
  799. } else if (_localScreenTrack) {
  800. [peerConnection addTrack:_localScreenTrack streamIds:@[kNCMediaStreamId]];
  801. }
  802. }
  803. // Add peer connection to the connections dictionary
  804. [_connectionsDict setObject:peerConnectionWrapper forKey:peerKey];
  805. // Notify about the new peer
  806. if (!screensharingPeer) {
  807. [self.delegate callController:self peerJoined:peerConnectionWrapper];
  808. }
  809. }
  810. return peerConnectionWrapper;
  811. }
  812. - (void)sendMessageToAllOfType:(NSString *)type withPayload:(id)payload
  813. {
  814. [[WebRTCCommon shared] assertQueue];
  815. if ([self->_externalSignalingController hasMCU]) {
  816. [self->_publisherPeerConnection sendDataChannelMessageOfType:type withPayload:payload];
  817. } else {
  818. NSArray *connectionWrappers = [self.connectionsDict allValues];
  819. for (NCPeerConnection *peerConnection in connectionWrappers) {
  820. [peerConnection sendDataChannelMessageOfType:type withPayload:payload];
  821. }
  822. }
  823. // Send a signaling message only if we are using an external signaling server
  824. if (!self->_externalSignalingController) {
  825. return;
  826. }
  827. for (NCPeerConnection *peer in [self->_connectionsDict allValues]) {
  828. NCSignalingMessage *message = nil;
  829. NSDictionary *payload = nil;
  830. NSString *from = [self signalingSessionId];
  831. if ([type isEqualToString:@"audioOn"]) {
  832. payload = @{@"name": @"audio"};
  833. message = [[NCUnmuteMessage alloc] initWithFrom:from to:peer.peerId sid:peer.sid roomType:peer.roomType payload:payload];
  834. } else if ([type isEqualToString:@"audioOff"]) {
  835. payload = @{@"name": @"audio"};
  836. message = [[NCMuteMessage alloc] initWithFrom:from to:peer.peerId sid:peer.sid roomType:peer.roomType payload:payload];
  837. } else if ([type isEqualToString:@"videoOn"]) {
  838. payload = @{@"name": @"video"};
  839. message = [[NCUnmuteMessage alloc] initWithFrom:from to:peer.peerId sid:peer.sid roomType:peer.roomType payload:payload];
  840. } else if ([type isEqualToString:@"videoOff"]) {
  841. payload = @{@"name": @"video"};
  842. message = [[NCMuteMessage alloc] initWithFrom:from to:peer.peerId sid:peer.sid roomType:peer.roomType payload:payload];
  843. } else if ([type isEqualToString:@"nickChanged"]) {
  844. payload = @{@"name": _account.userDisplayName};
  845. message = [[NCNickChangedMessage alloc] initWithFrom:from to:peer.peerId sid:peer.sid roomType:peer.roomType payload:payload];
  846. }
  847. if (message) {
  848. [self->_externalSignalingController sendCallMessage:message];
  849. }
  850. }
  851. }
  852. #pragma mark - External signaling support
  853. - (void)createPublisherPeerConnection
  854. {
  855. [[WebRTCCommon shared] assertQueue];
  856. if (self->_publisherPeerConnection || (!self->_localAudioTrack && !self->_localVideoTrack)) {
  857. NSLog(@"Not creating publisher peer connection. Already created or no local media.");
  858. return;
  859. }
  860. [NCUtils log:[NSString stringWithFormat:@"Creating publisher peer connection with sessionId: %@", [self signalingSessionId]]];
  861. NSArray *iceServers = [self->_signalingController getIceServers];
  862. self->_publisherPeerConnection = [[NCPeerConnection alloc] initForPublisherWithSessionId:[self signalingSessionId] andICEServers:iceServers forAudioOnlyCall:YES];
  863. self->_publisherPeerConnection.roomType = kRoomTypeVideo;
  864. self->_publisherPeerConnection.delegate = self;
  865. NSString *peerKey = [[self signalingSessionId] stringByAppendingString:kRoomTypeVideo];
  866. [self->_connectionsDict setObject:self->_publisherPeerConnection forKey:peerKey];
  867. RTCPeerConnection *peerConnection = [self->_publisherPeerConnection getPeerConnection];
  868. if (self->_localAudioTrack) {
  869. [peerConnection addTrack:self->_localAudioTrack streamIds:@[kNCMediaStreamId]];
  870. }
  871. if (self->_localVideoTrack) {
  872. [peerConnection addTrack:self->_localVideoTrack streamIds:@[kNCMediaStreamId]];
  873. }
  874. [self->_publisherPeerConnection sendPublisherOffer];
  875. }
  876. - (void)createScreenPublisherPeerConnection
  877. {
  878. [[WebRTCCommon shared] assertQueue];
  879. if (self->_screenPublisherPeerConnection || !self->_localScreenTrack) {
  880. NSLog(@"Not creating publisher peer connection. Already created or no local media.");
  881. return;
  882. }
  883. NSLog(@"Creating publisher peer connection with sessionId: %@", [self signalingSessionId]);
  884. NSArray *iceServers = [self->_signalingController getIceServers];
  885. self->_screenPublisherPeerConnection = [[NCPeerConnection alloc] initForPublisherWithSessionId:[self signalingSessionId] andICEServers:iceServers forAudioOnlyCall:YES];
  886. self->_screenPublisherPeerConnection.roomType = kRoomTypeScreen;
  887. self->_screenPublisherPeerConnection.isOwnScreensharePeer = YES;
  888. self->_screenPublisherPeerConnection.delegate = self;
  889. NSString *peerKey = [self getPeerKeyWithSessionId:[self signalingSessionId] ofType:kRoomTypeScreen forOwnScreenshare:YES];
  890. [self->_connectionsDict setObject:self->_screenPublisherPeerConnection forKey:peerKey];
  891. if (self->_localScreenTrack) {
  892. RTCPeerConnection *peerConnection = [self->_screenPublisherPeerConnection getPeerConnection];
  893. [peerConnection addTrack:self->_localScreenTrack streamIds:@[kNCMediaStreamId]];
  894. }
  895. [self->_screenPublisherPeerConnection sendPublisherOffer];
  896. }
  897. - (void)requestOfferWithRepetitionForSessionId:(NSString *)sessionId andRoomType:(NSString *)roomType
  898. {
  899. [[WebRTCCommon shared] assertQueue];
  900. NSNumber *timeout = [NSNumber numberWithInt:[[NSDate date] timeIntervalSince1970] + 60];
  901. NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
  902. [userInfo setObject:sessionId forKey:@"sessionId"];
  903. [userInfo setObject:roomType forKey:@"roomType"];
  904. [userInfo setValue:timeout forKey:@"timeout"];
  905. NSTimer *pendingOfferTimer = [NSTimer timerWithTimeInterval:8.0 target:self selector:@selector(requestNewOffer:) userInfo:userInfo repeats:YES];
  906. NSString *peerKey = [sessionId stringByAppendingString:roomType];
  907. [self->_pendingOffersDict setObject:pendingOfferTimer forKey:peerKey];
  908. // Request new offer
  909. [self->_externalSignalingController requestOfferForSessionId:sessionId andRoomType:roomType];
  910. dispatch_async(dispatch_get_main_queue(), ^{
  911. [[NSRunLoop mainRunLoop] addTimer:pendingOfferTimer forMode:NSDefaultRunLoopMode];
  912. });
  913. }
  914. - (void)requestNewOffer:(NSTimer *)timer
  915. {
  916. NSString *sessionId = [timer.userInfo objectForKey:@"sessionId"];
  917. NSString *roomType = [timer.userInfo objectForKey:@"roomType"];
  918. NSInteger timeout = [[timer.userInfo objectForKey:@"timeout"] integerValue];
  919. [[WebRTCCommon shared] dispatch:^{
  920. if ([[NSDate date] timeIntervalSince1970] < timeout) {
  921. NSLog(@"Re-requesting an offer to session: %@", sessionId);
  922. [self->_externalSignalingController requestOfferForSessionId:sessionId andRoomType:roomType];
  923. } else {
  924. dispatch_async(dispatch_get_main_queue(), ^{
  925. [timer invalidate];
  926. });
  927. }
  928. }];
  929. }
  930. - (void)checkIfPendingOffer:(NCSignalingMessage *)signalingMessage
  931. {
  932. if (signalingMessage.messageType == kNCSignalingMessageTypeOffer) {
  933. NSString *peerKey = [signalingMessage.from stringByAppendingString:signalingMessage.roomType];
  934. NSTimer *pendingRequestTimer = [_pendingOffersDict objectForKey:peerKey];
  935. if (pendingRequestTimer) {
  936. NSLog(@"Pending requested offer arrived. Removing timer.");
  937. dispatch_async(dispatch_get_main_queue(), ^{
  938. [pendingRequestTimer invalidate];
  939. });
  940. }
  941. }
  942. }
  943. #pragma mark - Nick & Media info
  944. - (void)sendNick
  945. {
  946. NSDictionary *payload = @{
  947. @"userid":_account.userId,
  948. @"name":_account.userDisplayName
  949. };
  950. [[WebRTCCommon shared] dispatch:^{
  951. [self sendMessageToAllOfType:@"nickChanged" withPayload:payload];
  952. }];
  953. }
  954. - (void)sendMediaState
  955. {
  956. [[WebRTCCommon shared] dispatch:^{
  957. // Send current audio state
  958. if (self.isAudioEnabled) {
  959. NSLog(@"Send audioOn to all");
  960. [self sendMessageToAllOfType:@"audioOn" withPayload:nil];
  961. } else {
  962. NSLog(@"Send audioOff to all");
  963. [self sendMessageToAllOfType:@"audioOff" withPayload:nil];
  964. }
  965. // Send current video state
  966. if (self.isVideoEnabled) {
  967. NSLog(@"Send videoOn to all");
  968. [self sendMessageToAllOfType:@"videoOn" withPayload:nil];
  969. } else {
  970. NSLog(@"Send videoOff to all");
  971. [self sendMessageToAllOfType:@"videoOff" withPayload:nil];
  972. }
  973. }];
  974. }
  975. - (void)startSendingCurrentState
  976. {
  977. dispatch_async(dispatch_get_main_queue(), ^{
  978. [self->_sendCurrentStateTimer invalidate];
  979. self->_sendCurrentStateTimer = nil;
  980. [self sendCurrentStateWithTimer:nil];
  981. });
  982. }
  983. - (void)stopSendingCurrentState
  984. {
  985. dispatch_async(dispatch_get_main_queue(), ^{
  986. [self->_sendCurrentStateTimer invalidate];
  987. self->_sendCurrentStateTimer = nil;
  988. });
  989. }
  990. - (void)sendCurrentStateWithTimer:(NSTimer *)timer
  991. {
  992. __block NSInteger interval = [[timer.userInfo objectForKey:@"interval"] integerValue];
  993. dispatch_async(dispatch_get_main_queue(), ^{
  994. [self sendNick];
  995. [self sendMediaState];
  996. if (interval == 0) {
  997. interval = 1;
  998. } else {
  999. interval *= 2;
  1000. }
  1001. if (interval > 16) {
  1002. return;
  1003. }
  1004. NSDictionary *userInfo = @{@"interval" : @(interval)};
  1005. self->_sendCurrentStateTimer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(sendCurrentStateWithTimer:) userInfo:userInfo repeats:NO];
  1006. });
  1007. }
  1008. #pragma mark - External Signaling Controller Delegate
  1009. - (void)externalSignalingController:(NCExternalSignalingController *)externalSignalingController didReceivedSignalingMessage:(NSDictionary *)signalingMessageDict
  1010. {
  1011. //NSLog(@"External signaling message received: %@", signalingMessageDict);
  1012. [[WebRTCCommon shared] dispatch:^{
  1013. NCSignalingMessage *signalingMessage = [NCSignalingMessage messageFromExternalSignalingJSONDictionary:signalingMessageDict];
  1014. [self checkIfPendingOffer:signalingMessage];
  1015. [self processSignalingMessage:signalingMessage];
  1016. }];
  1017. }
  1018. - (void)externalSignalingController:(NCExternalSignalingController *)externalSignalingController didReceivedParticipantListMessage:(NSDictionary *)participantListMessageDict
  1019. {
  1020. //NSLog(@"External participants message received: %@", participantListMessageDict);
  1021. [[WebRTCCommon shared] dispatch:^{
  1022. NSArray *usersInRoom = [participantListMessageDict objectForKey:@"users"];
  1023. // Update for "all" participants
  1024. if ([[participantListMessageDict objectForKey:@"all"] boolValue]) {
  1025. // Check if "incall" key exist
  1026. if ([[participantListMessageDict allKeys] containsObject:@"incall"]) {
  1027. // Clear usersInRoom array if incall=false
  1028. if (![[participantListMessageDict objectForKey:@"incall"] boolValue]) {
  1029. usersInRoom = @[];
  1030. }
  1031. }
  1032. }
  1033. [self processUsersInRoom:usersInRoom];
  1034. }];
  1035. }
  1036. - (void)externalSignalingControllerShouldRejoinCall:(NCExternalSignalingController *)externalSignalingController
  1037. {
  1038. // Call controller should rejoin the call if it was notifiy with the willRejoin notification first.
  1039. // Also we should check that it has joined the call first with the startCall method.
  1040. [[WebRTCCommon shared] dispatch:^{
  1041. if (self->_preparedForRejoin) {
  1042. self->_preparedForRejoin = NO;
  1043. if (self->_joinedCallOnce) {
  1044. [self shouldRejoinCall];
  1045. } else {
  1046. [self joinCall];
  1047. }
  1048. }
  1049. }];
  1050. }
  1051. - (void)externalSignalingControllerWillRejoinCall:(NCExternalSignalingController *)externalSignalingController
  1052. {
  1053. [[WebRTCCommon shared] dispatch:^{
  1054. [self willRejoinCall];
  1055. }];
  1056. }
  1057. - (void)externalSignalingController:(NCExternalSignalingController *)externalSignalingController shouldSwitchToCall:(NSString *)roomToken
  1058. {
  1059. [self willSwitchToCall:roomToken];
  1060. }
  1061. #pragma mark - Signaling Controller Delegate
  1062. - (void)signalingController:(NCSignalingController *)signalingController didReceiveSignalingMessage:(NSDictionary *)message
  1063. {
  1064. [[WebRTCCommon shared] dispatch:^{
  1065. NSString *messageType = [message objectForKey:@"type"];
  1066. if (self->_leavingCall) {
  1067. return;
  1068. }
  1069. if ([messageType isEqualToString:@"usersInRoom"]) {
  1070. [self processUsersInRoom:[message objectForKey:@"data"]];
  1071. } else if ([messageType isEqualToString:@"message"]) {
  1072. NCSignalingMessage *signalingMessage = [NCSignalingMessage messageFromJSONString:[message objectForKey:@"data"]];
  1073. [self processSignalingMessage:signalingMessage];
  1074. } else {
  1075. NSLog(@"Uknown message: %@", [message objectForKey:@"data"]);
  1076. }
  1077. }];
  1078. }
  1079. #pragma mark - NCCamera Controller Delegate
  1080. - (void)didDrawFirstFrameOnLocalView {
  1081. [self.delegate callControllerDidDrawFirstLocalFrame:self];
  1082. }
  1083. #pragma mark - Signaling functions
  1084. - (void)processSignalingMessage:(NCSignalingMessage *)signalingMessage
  1085. {
  1086. if (!signalingMessage) {
  1087. return;
  1088. }
  1089. [[WebRTCCommon shared] assertQueue];
  1090. switch (signalingMessage.messageType) {
  1091. case kNCSignalingMessageTypeOffer:
  1092. case kNCSignalingMessageTypeAnswer:
  1093. {
  1094. // If we receive an answer to a "screen" type, it can only be our own publishing peer
  1095. BOOL isAnswerToOwnScreenshare = signalingMessage.messageType == kNCSignalingMessageTypeAnswer && [signalingMessage.roomType isEqualToString:kRoomTypeScreen];
  1096. // If there is already a peer connection but a new offer is received with a different sid the existing
  1097. // peer connection is stale, so it needs to be removed and a new one created instead.
  1098. NCPeerConnection *peerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:signalingMessage.from ofType:signalingMessage.roomType forOwnScreenshare:isAnswerToOwnScreenshare];
  1099. NSString *peerName;
  1100. if (signalingMessage.messageType == kNCSignalingMessageTypeOffer && peerConnectionWrapper &&
  1101. signalingMessage.sid.length > 0 && ![signalingMessage.sid isEqualToString:peerConnectionWrapper.sid]) {
  1102. // Remember the peerName for the new connectionWrapper
  1103. peerName = peerConnectionWrapper.peerName;
  1104. [self cleanPeerConnectionForSessionId:signalingMessage.from ofType:signalingMessage.roomType forOwnScreenshare:isAnswerToOwnScreenshare];
  1105. }
  1106. peerConnectionWrapper = [self getOrCreatePeerConnectionWrapperForSessionId:signalingMessage.from withSid:signalingMessage.sid ofType:signalingMessage.roomType forOwnScreenshare:isAnswerToOwnScreenshare];
  1107. NCSessionDescriptionMessage *sdpMessage = (NCSessionDescriptionMessage *)signalingMessage;
  1108. RTCSessionDescription *sessionDescription = sdpMessage.sessionDescription;
  1109. [peerConnectionWrapper setRemoteDescription:sessionDescription];
  1110. if (sdpMessage.nick && ![sdpMessage.nick isEqualToString:@""]) {
  1111. [peerConnectionWrapper setPeerName:sdpMessage.nick];
  1112. } else if (peerName) {
  1113. [peerConnectionWrapper setPeerName:peerName];
  1114. }
  1115. break;
  1116. }
  1117. case kNCSignalingMessageTypeCandidate:
  1118. {
  1119. NCPeerConnection *peerConnectionWrapper = [self getOrCreatePeerConnectionWrapperForSessionId:signalingMessage.from withSid:signalingMessage.sid ofType:signalingMessage.roomType];
  1120. NCICECandidateMessage *candidateMessage = (NCICECandidateMessage *)signalingMessage;
  1121. [peerConnectionWrapper addICECandidate:candidateMessage.candidate];
  1122. break;
  1123. }
  1124. case kNCSignalingMessageTypeUnshareScreen:
  1125. {
  1126. NCPeerConnection *peerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:signalingMessage.from ofType:signalingMessage.roomType];
  1127. if (peerConnectionWrapper) {
  1128. NSString *peerKey = [self getPeerKeyWithSessionId:peerConnectionWrapper.peerId ofType:kRoomTypeScreen forOwnScreenshare:NO];
  1129. NCPeerConnection *screenPeerConnection = [self->_connectionsDict objectForKey:peerKey];
  1130. if (screenPeerConnection) {
  1131. [screenPeerConnection close];
  1132. [self->_connectionsDict removeObjectForKey:peerKey];
  1133. }
  1134. [self.delegate callController:self didReceiveUnshareScreenFromPeer:peerConnectionWrapper];
  1135. }
  1136. break;
  1137. }
  1138. case kNCSignalingMessageTypeControl:
  1139. {
  1140. NSString *action = [signalingMessage.payload objectForKey:@"action"];
  1141. if ([action isEqualToString:@"forceMute"]) {
  1142. NSString *peerId = [signalingMessage.payload objectForKey:@"peerId"];
  1143. [self.delegate callController:self didReceiveForceMuteActionForPeerId:peerId];
  1144. }
  1145. break;
  1146. }
  1147. case kNCSignalingMessageTypeMute:
  1148. case kNCSignalingMessageTypeUnmute:
  1149. {
  1150. NCPeerConnection *peerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:signalingMessage.from ofType:signalingMessage.roomType];
  1151. if (peerConnectionWrapper) {
  1152. NSString *name = [signalingMessage.payload objectForKey:@"name"];
  1153. if ([name isEqualToString:@"audio"]) {
  1154. NSString *messageType = (signalingMessage.messageType == kNCSignalingMessageTypeMute) ? @"audioOff" : @"audioOn";
  1155. [peerConnectionWrapper setStatusForDataChannelMessageType:messageType withPayload:nil];
  1156. } else if ([name isEqualToString:@"video"]) {
  1157. NSString *messageType = (signalingMessage.messageType == kNCSignalingMessageTypeMute) ? @"videoOff" : @"videoOn";
  1158. [peerConnectionWrapper setStatusForDataChannelMessageType:messageType withPayload:nil];
  1159. }
  1160. }
  1161. break;
  1162. }
  1163. case kNCSignalingMessageTypeNickChanged:
  1164. {
  1165. NCPeerConnection *peerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:signalingMessage.from ofType:signalingMessage.roomType];
  1166. if (peerConnectionWrapper) {
  1167. NSString *name = [signalingMessage.payload objectForKey:@"name"];
  1168. if (name.length > 0) {
  1169. [peerConnectionWrapper setStatusForDataChannelMessageType:@"nickChanged" withPayload:name];
  1170. }
  1171. }
  1172. break;
  1173. }
  1174. case kNCSignalingMessageTypeRaiseHand:
  1175. {
  1176. NCPeerConnection *peerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:signalingMessage.from ofType:signalingMessage.roomType];
  1177. if (peerConnectionWrapper) {
  1178. BOOL raised = [[signalingMessage.payload objectForKey:@"state"] boolValue];
  1179. [peerConnectionWrapper setStatusForDataChannelMessageType:@"raiseHand" withPayload:@(raised)];
  1180. }
  1181. break;
  1182. }
  1183. case kNCSignalingMessageTypeRecording:
  1184. {
  1185. NCRecordingMessage *recordingMessage = (NCRecordingMessage *)signalingMessage;
  1186. self->_room.callRecording = recordingMessage.status;
  1187. [self.delegate callControllerDidChangeRecording:self];
  1188. break;
  1189. }
  1190. case kNCSignalingMessageTypeReaction:
  1191. {
  1192. NCPeerConnection *peerConnectionWrapper = [self getPeerConnectionWrapperForSessionId:signalingMessage.from ofType:signalingMessage.roomType];
  1193. if (peerConnectionWrapper) {
  1194. NSString *reaction = [signalingMessage.payload objectForKey:@"reaction"];
  1195. [self.delegate callController:self didReceiveReaction:reaction fromPeer:peerConnectionWrapper];
  1196. }
  1197. break;
  1198. }
  1199. case kNCSignalingMessageTypeUnknown:
  1200. NSLog(@"Received an unknown signaling message: %@", signalingMessage);
  1201. break;
  1202. }
  1203. }
  1204. - (void)processUsersInRoom:(NSArray *)users
  1205. {
  1206. [[WebRTCCommon shared] assertQueue];
  1207. _usersInRoom = users;
  1208. NSInteger previousUserInCall = _userInCall;
  1209. NSMutableArray *newSessions = [self getInCallSessionsFromUsersInRoom:users];
  1210. if (_leavingCall) {
  1211. return;
  1212. }
  1213. // Detect if user should rejoin call (internal signaling)
  1214. if (!_userInCall && _shouldRejoinCallUsingInternalSignaling) {
  1215. _shouldRejoinCallUsingInternalSignaling = NO;
  1216. [self shouldRejoinCall];
  1217. }
  1218. if (!previousUserInCall) {
  1219. // Do nothing if app user is stil not in the call
  1220. if (!_userInCall) {
  1221. return;
  1222. }
  1223. // Create publisher peer connection
  1224. if ([_externalSignalingController hasMCU]) {
  1225. [self createPublisherPeerConnection];
  1226. }
  1227. }
  1228. NSMutableArray *oldSessions = [NSMutableArray arrayWithArray:_sessionsInCall];
  1229. //Save current sessions in call
  1230. _sessionsInCall = [NSArray arrayWithArray:newSessions];
  1231. // Calculate sessions that left the call
  1232. NSMutableArray *leftSessions = [NSMutableArray arrayWithArray:oldSessions];
  1233. [leftSessions removeObjectsInArray:newSessions];
  1234. // Calculate sessions that join the call
  1235. [newSessions removeObjectsInArray:oldSessions];
  1236. if (newSessions.count > 0) {
  1237. [self getPeersForCall];
  1238. }
  1239. if (_serverSupportsConversationPermissions) {
  1240. [self checkUserPermissionsChange];
  1241. }
  1242. // Create new peer connections for new sessions in call
  1243. for (NSString *sessionId in newSessions) {
  1244. NSString *peerKey = [sessionId stringByAppendingString:kRoomTypeVideo];
  1245. if (![_connectionsDict objectForKey:peerKey] && ![[self signalingSessionId] isEqualToString:sessionId]) {
  1246. // Always create a peer connection, so the peer is added to the call view.
  1247. // When using a MCU we request an offer, but in case there are no streams published, we won't get an offer.
  1248. // When using internal signaling if we and the other participant are not publishing any stream,
  1249. // we won't receive or send any offer.
  1250. NCPeerConnection *peerConnectionWrapper = [self getOrCreatePeerConnectionWrapperForSessionId:sessionId withSid:nil ofType:kRoomTypeVideo];
  1251. if ([_externalSignalingController hasMCU]) {
  1252. // Only request offer if user is sharing audio or video streams
  1253. if ([self userHasStreams:sessionId]) {
  1254. NSLog(@"Requesting offer to the MCU for session: %@", sessionId);
  1255. [self requestOfferWithRepetitionForSessionId:sessionId andRoomType:kRoomTypeVideo];
  1256. } else {
  1257. // Set peer as dummyPeer if it has no streams
  1258. peerConnectionWrapper.isDummyPeer = YES;
  1259. }
  1260. } else {
  1261. NSComparisonResult result = [sessionId compare:[self signalingSessionId]];
  1262. if (result == NSOrderedAscending) {
  1263. NSLog(@"Creating offer...");
  1264. [peerConnectionWrapper sendOffer];
  1265. } else {
  1266. NSLog(@"Waiting for offer...");
  1267. }
  1268. if (self.screensharingActive) {
  1269. // If screensharing is active and we are using internal signaling, we need to send a offer to the newly joined user
  1270. [self sendScreensharingOfferToSessionId:peerConnectionWrapper.peerId];
  1271. }
  1272. }
  1273. }
  1274. }
  1275. // Close old peer connections for sessions that left the call
  1276. for (NSString *sessionId in leftSessions) {
  1277. // Hang up call if user sessionId is no longer in the call
  1278. // Could be because a moderator "ended the call for everyone"
  1279. if ([[self signalingSessionId] isEqualToString:sessionId]) {
  1280. NSLog(@"User sessionId is no longer in the call -> hang up call");
  1281. [self.delegate callControllerWantsToHangUpCall:self];
  1282. return;
  1283. }
  1284. // Remove all peer connections for that user
  1285. [self cleanAllPeerConnectionsForSessionId:sessionId];
  1286. }
  1287. }
  1288. - (BOOL)userHasStreams:(NSString *)sessionId
  1289. {
  1290. for (NSMutableDictionary *user in _usersInRoom) {
  1291. NSString *userSession = [user objectForKey:@"sessionId"];
  1292. if ([userSession isEqualToString:sessionId]) {
  1293. NSInteger userCallFlags = [[user objectForKey:@"inCall"] integerValue];
  1294. NSInteger requiredFlags = CallFlagWithAudio | CallFlagWithVideo;
  1295. return (userCallFlags & requiredFlags) != 0;
  1296. }
  1297. }
  1298. return NO;
  1299. }
  1300. - (void)checkUserPermissionsChange
  1301. {
  1302. for (NSMutableDictionary *user in _usersInRoom) {
  1303. NSString *userSession = [user objectForKey:@"sessionId"];
  1304. id userPermissionValue = [user objectForKey:@"participantPermissions"];
  1305. if ([userSession isEqualToString:[self signalingSessionId]] && [userPermissionValue isKindOfClass:[NSNumber class]]) {
  1306. NSInteger userPermissions = [userPermissionValue integerValue];
  1307. NSInteger changedPermissions = userPermissions ^ _userPermissions;
  1308. if ((changedPermissions & NCPermissionCanPublishAudio) || (changedPermissions & NCPermissionCanPublishVideo)) {
  1309. [NCUtils log:@"User permissions changed"];
  1310. _userPermissions = userPermissions;
  1311. [self.delegate callController:self userPermissionsChanged:_userPermissions];
  1312. [self forceReconnect];
  1313. }
  1314. }
  1315. }
  1316. }
  1317. - (NSMutableArray *)getInCallSessionsFromUsersInRoom:(NSArray *)users
  1318. {
  1319. NSMutableArray *sessions = [[NSMutableArray alloc] init];
  1320. for (NSMutableDictionary *user in users) {
  1321. NSString *sessionId = [user objectForKey:@"sessionId"];
  1322. NSInteger inCall = [[user objectForKey:@"inCall"] integerValue];
  1323. BOOL internalClient = [[user objectForKey:@"internal"] boolValue];
  1324. // Set inCall flag for app user
  1325. if ([sessionId isEqualToString:[self signalingSessionId]]) {
  1326. _userInCall = inCall;
  1327. }
  1328. // Add session if inCall and if it's not an internal client
  1329. if (inCall && !internalClient) {
  1330. [sessions addObject:sessionId];
  1331. }
  1332. }
  1333. //NSLog(@"InCall sessions: %@", sessions);
  1334. return sessions;
  1335. }
  1336. - (TalkActor * _Nullable)getActorFromSessionId:(NSString *)sessionId
  1337. {
  1338. [[WebRTCCommon shared] assertQueue];
  1339. if (_externalSignalingController) {
  1340. return [_externalSignalingController getParticipantFromSessionId:sessionId].actor;
  1341. }
  1342. NSInteger callAPIVersion = [[NCAPIController sharedInstance] callAPIVersionForAccount:_account];
  1343. for (NSMutableDictionary *user in _peersInCall) {
  1344. NSString *userSessionId = [user objectForKey:@"sessionId"];
  1345. if ([userSessionId isEqualToString:sessionId]) {
  1346. TalkActor *actor = [[TalkActor alloc] initWithActorId:[user objectForKey:@"userId"] actorType:@"users" actorDisplayName:[user objectForKey:@"displayName"]];
  1347. if (callAPIVersion >= APIv3) {
  1348. [actor setId:[user objectForKey:@"actorId"]];
  1349. [actor setType:[user objectForKey:@"actorType"]];
  1350. }
  1351. return actor;
  1352. }
  1353. }
  1354. return nil;
  1355. }
  1356. #pragma mark - NCPeerConnectionDelegate
  1357. // Delegates from NCPeerConnection are already dispatched to the webrtc worker queue
  1358. - (void)peerConnection:(NCPeerConnection *)peerConnection didAddStream:(RTCMediaStream *)stream
  1359. {
  1360. if (!peerConnection.isMCUPublisherPeer) {
  1361. [self.delegate callController:self didAddStream:stream ofPeer:peerConnection];
  1362. }
  1363. }
  1364. - (void)peerConnection:(NCPeerConnection *)peerConnection didRemoveStream:(RTCMediaStream *)stream
  1365. {
  1366. if (!peerConnection.isMCUPublisherPeer) {
  1367. [self.delegate callController:self didRemoveStream:stream ofPeer:peerConnection];
  1368. }
  1369. }
  1370. - (void)peerConnection:(NCPeerConnection *)peerConnection didChangeIceConnectionState:(RTCIceConnectionState)newState
  1371. {
  1372. if (newState == RTCIceConnectionStateFailed) {
  1373. if ([peerConnection.roomType isEqualToString:kRoomTypeScreen]) {
  1374. [self stopScreenshare];
  1375. return;
  1376. }
  1377. // If publisher peer failed then reconnect
  1378. if (peerConnection.isMCUPublisherPeer) {
  1379. [NCUtils log:@"Publisher peer connection failed"];
  1380. [self forceReconnect];
  1381. // If another peer failed using MCU then request a new offer
  1382. } else if ([_externalSignalingController hasMCU]) {
  1383. NSString *sessionId = [peerConnection.peerId copy];
  1384. NSString *roomType = [peerConnection.roomType copy];
  1385. // Close failed peer connection
  1386. [self cleanPeerConnectionForSessionId:sessionId ofType:roomType forOwnScreenshare:NO];
  1387. // Request new offer
  1388. [self requestOfferWithRepetitionForSessionId:sessionId andRoomType:roomType];
  1389. }
  1390. }
  1391. if (newState == RTCIceConnectionStateConnected) {
  1392. [self startSendingCurrentState];
  1393. if (self.externalSignalingController && peerConnection.isMCUPublisherPeer) {
  1394. [NCUtils log:@"Publisher peer changed to connected"];
  1395. }
  1396. if (self.externalSignalingController && self.screensharingActive) {
  1397. if (peerConnection.isMCUPublisherPeer) {
  1398. // This is our screensharing publisher peer which connected just now, so ask everyone to request our peer now
  1399. for (NCPeerConnection *peer in [self->_connectionsDict allValues]) {
  1400. if ([peer.peerId isEqualToString:_screenPublisherPeerConnection.peerId]) {
  1401. continue;
  1402. }
  1403. [_externalSignalingController sendSendOfferMessageWithSessionId:peer.peerId andRoomType:kRoomTypeScreen];
  1404. }
  1405. } else {
  1406. // Another new peer joined, tell the peer that we are screensharing and it needs to request the screen peer
  1407. [self.externalSignalingController sendSendOfferMessageWithSessionId:peerConnection.peerId andRoomType:kRoomTypeScreen];
  1408. }
  1409. }
  1410. }
  1411. if (!peerConnection.isMCUPublisherPeer) {
  1412. [self.delegate callController:self iceStatusChanged:newState ofPeer:peerConnection];
  1413. }
  1414. }
  1415. - (void)peerConnection:(NCPeerConnection *)peerConnection didGenerateIceCandidate:(RTCIceCandidate *)candidate
  1416. {
  1417. NCICECandidateMessage *message = [[NCICECandidateMessage alloc] initWithCandidate:candidate
  1418. from:[self signalingSessionId]
  1419. to:peerConnection.peerId
  1420. sid:peerConnection.sid
  1421. roomType:peerConnection.roomType
  1422. broadcaster:peerConnection.isOwnScreensharePeer ? [self signalingSessionId] : nil];
  1423. if (_externalSignalingController) {
  1424. [_externalSignalingController sendCallMessage:message];
  1425. } else {
  1426. [_signalingController sendSignalingMessage:message];
  1427. }
  1428. }
  1429. - (void)peerConnection:(NCPeerConnection *)peerConnection needsToSendSessionDescription:(RTCSessionDescription *)sessionDescription
  1430. {
  1431. NCSessionDescriptionMessage *message = [[NCSessionDescriptionMessage alloc]
  1432. initWithSessionDescription:sessionDescription
  1433. from:[self signalingSessionId]
  1434. to:peerConnection.peerId
  1435. sid:peerConnection.sid
  1436. roomType:peerConnection.roomType
  1437. broadcaster:peerConnection.isOwnScreensharePeer ? [self signalingSessionId] : nil
  1438. nick:_userDisplayName];
  1439. if (_externalSignalingController) {
  1440. [_externalSignalingController sendCallMessage:message];
  1441. } else {
  1442. [_signalingController sendSignalingMessage:message];
  1443. }
  1444. }
  1445. - (void)peerConnection:(NCPeerConnection *)peerConnection didReceiveStatusDataChannelMessage:(NSString *)type
  1446. {
  1447. [self.delegate callController:self didReceiveDataChannelMessage:type fromPeer:peerConnection];
  1448. }
  1449. - (void)peerConnection:(NCPeerConnection *)peerConnection didReceivePeerNick:(NSString *)nick
  1450. {
  1451. [self.delegate callController:self didReceiveNick:nick fromPeer:peerConnection];
  1452. }
  1453. @end