ARDAppClient.m (34457B)
1 /* 2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #import "ARDAppClient+Internal.h" 12 13 #import "sdk/objc/api/peerconnection/RTCAudioTrack.h" 14 #import "sdk/objc/api/peerconnection/RTCConfiguration.h" 15 #import "sdk/objc/api/peerconnection/RTCFileLogger.h" 16 #import "sdk/objc/api/peerconnection/RTCIceCandidateErrorEvent.h" 17 #import "sdk/objc/api/peerconnection/RTCIceServer.h" 18 #import "sdk/objc/api/peerconnection/RTCMediaConstraints.h" 19 #import "sdk/objc/api/peerconnection/RTCMediaStream.h" 20 #import "sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h" 21 #import "sdk/objc/api/peerconnection/RTCRtpSender.h" 22 #import "sdk/objc/api/peerconnection/RTCRtpTransceiver.h" 23 #import "sdk/objc/api/peerconnection/RTCTracing.h" 24 #import "sdk/objc/api/peerconnection/RTCVideoSource.h" 25 #import "sdk/objc/api/peerconnection/RTCVideoTrack.h" 26 #import "sdk/objc/base/RTCLogging.h" 27 #import "sdk/objc/components/capturer/RTCCameraVideoCapturer.h" 28 #import "sdk/objc/components/capturer/RTCFileVideoCapturer.h" 29 #import "sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.h" 30 #import "sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.h" 31 32 #import "ARDAppEngineClient.h" 33 #import "ARDExternalSampleCapturer.h" 34 #import "ARDJoinResponse.h" 35 #import "ARDMessageResponse.h" 36 #import "ARDSettingsModel.h" 37 #import "ARDSignalingMessage.h" 38 #import "ARDTURNClient+Internal.h" 39 #import "ARDUtilities.h" 40 #import "ARDWebSocketChannel.h" 41 #import "RTCIceCandidate+JSON.h" 42 #import "RTCSessionDescription+JSON.h" 43 44 static NSString *const kARDIceServerRequestUrl = @"https://appr.tc/params"; 45 46 static NSString *const kARDAppClientErrorDomain = @"ARDAppClient"; 47 static NSInteger const kARDAppClientErrorUnknown = -1; 48 static NSInteger const kARDAppClientErrorRoomFull = -2; 49 static NSInteger const kARDAppClientErrorCreateSDP = -3; 50 static NSInteger const kARDAppClientErrorSetSDP = -4; 51 static NSInteger const kARDAppClientErrorInvalidClient = -5; 52 static NSInteger const kARDAppClientErrorInvalidRoom = -6; 53 static NSString *const kARDMediaStreamId = @"ARDAMS"; 54 static NSString *const kARDAudioTrackId = @"ARDAMSa0"; 55 static NSString *const kARDVideoTrackId = @"ARDAMSv0"; 56 static NSString *const kARDVideoTrackKind = @"video"; 57 58 // TODO(tkchin): Add these as UI options. 59 #if defined(WEBRTC_IOS) 60 static BOOL const kARDAppClientEnableTracing = NO; 61 static BOOL const kARDAppClientEnableRtcEventLog = YES; 62 static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB. 63 static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB. 64 #endif 65 static int const kKbpsMultiplier = 1000; 66 67 // We need a proxy to NSTimer because it causes a strong retain cycle. When 68 // using the proxy, `invalidate` must be called before it properly deallocs. 69 @interface ARDTimerProxy : NSObject 70 71 - (instancetype)initWithInterval:(NSTimeInterval)interval 72 repeats:(BOOL)repeats 73 timerHandler:(void (^)(void))timerHandler; 74 - (void)invalidate; 75 76 @end 77 78 @implementation ARDTimerProxy { 79 NSTimer *_timer; 80 void (^_timerHandler)(void); 81 } 82 83 - (instancetype)initWithInterval:(NSTimeInterval)interval 84 repeats:(BOOL)repeats 85 timerHandler:(void (^)(void))timerHandler { 86 NSParameterAssert(timerHandler); 87 self = [super init]; 88 if (self) { 89 _timerHandler = timerHandler; 90 _timer = [NSTimer scheduledTimerWithTimeInterval:interval 91 target:self 92 selector:@selector(timerDidFire:) 93 userInfo:nil 94 repeats:repeats]; 95 } 96 return self; 97 } 98 99 - (void)invalidate { 100 [_timer invalidate]; 101 } 102 103 - (void)timerDidFire:(NSTimer *)timer { 104 _timerHandler(); 105 } 106 107 @end 108 109 @implementation ARDAppClient { 110 RTC_OBJC_TYPE(RTCFileLogger) * _fileLogger; 111 ARDTimerProxy *_statsTimer; 112 ARDSettingsModel *_settings; 113 RTC_OBJC_TYPE(RTCVideoTrack) * _localVideoTrack; 114 } 115 116 @synthesize shouldGetStats = _shouldGetStats; 117 @synthesize state = _state; 118 @synthesize delegate = _delegate; 119 @synthesize roomServerClient = _roomServerClient; 120 @synthesize channel = _channel; 121 @synthesize loopbackChannel = _loopbackChannel; 122 @synthesize turnClient = _turnClient; 123 @synthesize peerConnection = _peerConnection; 124 @synthesize factory = _factory; 125 @synthesize messageQueue = _messageQueue; 126 @synthesize isTurnComplete = _isTurnComplete; 127 @synthesize hasReceivedSdp = _hasReceivedSdp; 128 @synthesize roomId = _roomId; 129 @synthesize clientId = _clientId; 130 @synthesize isInitiator = _isInitiator; 131 @synthesize iceServers = _iceServers; 132 @synthesize webSocketURL = _websocketURL; 133 @synthesize webSocketRestURL = _websocketRestURL; 134 @synthesize defaultPeerConnectionConstraints = 135 _defaultPeerConnectionConstraints; 136 @synthesize isLoopback = _isLoopback; 137 @synthesize broadcast = _broadcast; 138 139 - (instancetype)init { 140 return [self initWithDelegate:nil]; 141 } 142 143 - (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate { 144 self = [super init]; 145 if (self) { 146 _roomServerClient = [[ARDAppEngineClient alloc] init]; 147 _delegate = delegate; 148 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl]; 149 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL]; 150 [self configure]; 151 } 152 return self; 153 } 154 155 // TODO(tkchin): Provide signaling channel factory interface so we can recreate 156 // channel if we need to on network failure. Also, make this the default public 157 // constructor. 158 - (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient 159 signalingChannel:(id<ARDSignalingChannel>)channel 160 turnClient:(id<ARDTURNClient>)turnClient 161 delegate:(id<ARDAppClientDelegate>)delegate { 162 NSParameterAssert(rsClient); 163 NSParameterAssert(channel); 164 NSParameterAssert(turnClient); 165 self = [super init]; 166 if (self) { 167 _roomServerClient = rsClient; 168 _channel = channel; 169 _turnClient = turnClient; 170 _delegate = delegate; 171 [self configure]; 172 } 173 return self; 174 } 175 176 - (void)configure { 177 _messageQueue = [NSMutableArray array]; 178 _iceServers = [NSMutableArray array]; 179 _fileLogger = [[RTC_OBJC_TYPE(RTCFileLogger) alloc] init]; 180 [_fileLogger start]; 181 } 182 183 - (void)dealloc { 184 self.shouldGetStats = NO; 185 [self disconnect]; 186 } 187 188 - (void)setShouldGetStats:(BOOL)shouldGetStats { 189 if (_shouldGetStats == shouldGetStats) { 190 return; 191 } 192 if (shouldGetStats) { 193 __weak ARDAppClient *weakSelf = self; 194 _statsTimer = [[ARDTimerProxy alloc] 195 initWithInterval:1 196 repeats:YES 197 timerHandler:^{ 198 ARDAppClient *strongSelf = weakSelf; 199 [strongSelf.peerConnection 200 statisticsWithCompletionHandler:^( 201 RTC_OBJC_TYPE(RTCStatisticsReport) * stats) { 202 dispatch_async(dispatch_get_main_queue(), ^{ 203 ARDAppClient *strongerSelf = weakSelf; 204 [strongerSelf.delegate appClient:strongerSelf 205 didGetStats:stats]; 206 }); 207 }]; 208 }]; 209 } else { 210 [_statsTimer invalidate]; 211 _statsTimer = nil; 212 } 213 _shouldGetStats = shouldGetStats; 214 } 215 216 - (void)setState:(ARDAppClientState)state { 217 if (_state == state) { 218 return; 219 } 220 _state = state; 221 [_delegate appClient:self didChangeState:_state]; 222 } 223 224 - (void)connectToRoomWithId:(NSString *)roomId 225 settings:(ARDSettingsModel *)settings 226 isLoopback:(BOOL)isLoopback { 227 NSParameterAssert(roomId.length); 228 NSParameterAssert(_state == kARDAppClientStateDisconnected); 229 _settings = settings; 230 _isLoopback = isLoopback; 231 self.state = kARDAppClientStateConnecting; 232 233 RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) *decoderFactory = 234 [[RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) alloc] init]; 235 RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) *encoderFactory = 236 [[RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) alloc] init]; 237 encoderFactory.preferredCodec = [settings currentVideoCodecSettingFromStore]; 238 _factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] 239 initWithEncoderFactory:encoderFactory 240 decoderFactory:decoderFactory]; 241 242 #if defined(WEBRTC_IOS) 243 if (kARDAppClientEnableTracing) { 244 NSString *filePath = 245 [self documentsFilePathForFileName:@"webrtc-trace.txt"]; 246 RTCStartInternalCapture(filePath); 247 } 248 #endif 249 250 // Request TURN. 251 __weak ARDAppClient *weakSelf = self; 252 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers, 253 NSError *error) { 254 if (error) { 255 RTCLogError(@"Error retrieving TURN servers: %@", 256 error.localizedDescription); 257 } 258 ARDAppClient *strongSelf = weakSelf; 259 [strongSelf.iceServers addObjectsFromArray:turnServers]; 260 strongSelf.isTurnComplete = YES; 261 [strongSelf startSignalingIfReady]; 262 }]; 263 264 // Join room on room server. 265 [_roomServerClient 266 joinRoomWithRoomId:roomId 267 isLoopback:isLoopback 268 completionHandler:^(ARDJoinResponse *response, NSError *error) { 269 ARDAppClient *strongSelf = weakSelf; 270 if (error) { 271 [strongSelf.delegate appClient:strongSelf didError:error]; 272 return; 273 } 274 NSError *joinError = 275 [[strongSelf class] errorForJoinResultType:response.result]; 276 if (joinError) { 277 RTCLogError(@"Failed to join room:%@ on room server.", roomId); 278 [strongSelf disconnect]; 279 [strongSelf.delegate appClient:strongSelf didError:joinError]; 280 return; 281 } 282 RTCLog(@"Joined room:%@ on room server.", roomId); 283 strongSelf.roomId = response.roomId; 284 strongSelf.clientId = response.clientId; 285 strongSelf.isInitiator = response.isInitiator; 286 for (ARDSignalingMessage *message in response.messages) { 287 if (message.type == kARDSignalingMessageTypeOffer || 288 message.type == kARDSignalingMessageTypeAnswer) { 289 strongSelf.hasReceivedSdp = YES; 290 [strongSelf.messageQueue insertObject:message atIndex:0]; 291 } else { 292 [strongSelf.messageQueue addObject:message]; 293 } 294 } 295 strongSelf.webSocketURL = response.webSocketURL; 296 strongSelf.webSocketRestURL = response.webSocketRestURL; 297 [strongSelf registerWithColliderIfReady]; 298 [strongSelf startSignalingIfReady]; 299 }]; 300 } 301 302 - (void)disconnect { 303 if (_state == kARDAppClientStateDisconnected) { 304 return; 305 } 306 if (self.hasJoinedRoomServerRoom) { 307 [_roomServerClient leaveRoomWithRoomId:_roomId 308 clientId:_clientId 309 completionHandler:nil]; 310 } 311 if (_channel) { 312 if (_channel.state == kARDSignalingChannelStateRegistered) { 313 // Tell the other client we're hanging up. 314 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init]; 315 [_channel sendMessage:byeMessage]; 316 } 317 // Disconnect from collider. 318 _channel = nil; 319 } 320 _clientId = nil; 321 _roomId = nil; 322 _isInitiator = NO; 323 _hasReceivedSdp = NO; 324 _messageQueue = [NSMutableArray array]; 325 _localVideoTrack = nil; 326 #if defined(WEBRTC_IOS) 327 [_factory stopAecDump]; 328 [_peerConnection stopRtcEventLog]; 329 #endif 330 [_peerConnection close]; 331 _peerConnection = nil; 332 self.state = kARDAppClientStateDisconnected; 333 #if defined(WEBRTC_IOS) 334 if (kARDAppClientEnableTracing) { 335 RTCStopInternalCapture(); 336 } 337 #endif 338 } 339 340 #pragma mark - ARDSignalingChannelDelegate 341 342 - (void)channel:(id<ARDSignalingChannel>)channel 343 didReceiveMessage:(ARDSignalingMessage *)message { 344 switch (message.type) { 345 case kARDSignalingMessageTypeOffer: 346 case kARDSignalingMessageTypeAnswer: 347 // Offers and answers must be processed before any other message, so we 348 // place them at the front of the queue. 349 _hasReceivedSdp = YES; 350 [_messageQueue insertObject:message atIndex:0]; 351 break; 352 case kARDSignalingMessageTypeCandidate: 353 case kARDSignalingMessageTypeCandidateRemoval: 354 [_messageQueue addObject:message]; 355 break; 356 case kARDSignalingMessageTypeBye: 357 // Disconnects can be processed immediately. 358 [self processSignalingMessage:message]; 359 return; 360 } 361 [self drainMessageQueueIfReady]; 362 } 363 364 - (void)channel:(id<ARDSignalingChannel>)channel 365 didChangeState:(ARDSignalingChannelState)state { 366 switch (state) { 367 case kARDSignalingChannelStateOpen: 368 break; 369 case kARDSignalingChannelStateRegistered: 370 break; 371 case kARDSignalingChannelStateClosed: 372 case kARDSignalingChannelStateError: 373 // TODO(tkchin): reconnection scenarios. Right now we just disconnect 374 // completely if the websocket connection fails. 375 [self disconnect]; 376 break; 377 } 378 } 379 380 #pragma mark - RTC_OBJC_TYPE(RTCPeerConnectionDelegate) 381 // Callbacks for this delegate occur on non-main thread and need to be 382 // dispatched back to main queue as needed. 383 384 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 385 didChangeSignalingState:(RTCSignalingState)stateChanged { 386 RTCLog(@"Signaling state changed: %ld", (long)stateChanged); 387 } 388 389 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 390 didAddStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream { 391 RTCLog(@"Stream with %lu video tracks and %lu audio tracks was added.", 392 (unsigned long)stream.videoTracks.count, 393 (unsigned long)stream.audioTracks.count); 394 } 395 396 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 397 didStartReceivingOnTransceiver: 398 (RTC_OBJC_TYPE(RTCRtpTransceiver) *)transceiver { 399 RTC_OBJC_TYPE(RTCMediaStreamTrack) *track = transceiver.receiver.track; 400 RTCLog(@"Now receiving %@ on track %@.", track.kind, track.trackId); 401 } 402 403 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 404 didRemoveStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream { 405 RTCLog(@"Stream was removed."); 406 } 407 408 - (void)peerConnectionShouldNegotiate: 409 (RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection { 410 RTCLog(@"WARNING: Renegotiation needed but unimplemented."); 411 } 412 413 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 414 didChangeIceConnectionState:(RTCIceConnectionState)newState { 415 RTCLog(@"ICE state changed: %ld", (long)newState); 416 dispatch_async(dispatch_get_main_queue(), ^{ 417 [self.delegate appClient:self didChangeConnectionState:newState]; 418 }); 419 } 420 421 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 422 didChangeConnectionState:(RTCPeerConnectionState)newState { 423 RTCLog(@"ICE+DTLS state changed: %ld", (long)newState); 424 } 425 426 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 427 didChangeIceGatheringState:(RTCIceGatheringState)newState { 428 RTCLog(@"ICE gathering state changed: %ld", (long)newState); 429 } 430 431 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 432 didGenerateIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate { 433 dispatch_async(dispatch_get_main_queue(), ^{ 434 ARDICECandidateMessage *message = 435 [[ARDICECandidateMessage alloc] initWithCandidate:candidate]; 436 [self sendSignalingMessage:message]; 437 }); 438 } 439 440 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 441 didFailToGatherIceCandidate: 442 (RTC_OBJC_TYPE(RTCIceCandidateErrorEvent) *)event { 443 RTCLog(@"Failed to gather ICE candidate. address: %@, port: %d, url: %@, " 444 @"errorCode: %d, " 445 @"errorText: %@", 446 event.address, 447 event.port, 448 event.url, 449 event.errorCode, 450 event.errorText); 451 } 452 453 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 454 didRemoveIceCandidates: 455 (NSArray<RTC_OBJC_TYPE(RTCIceCandidate) *> *)candidates { 456 dispatch_async(dispatch_get_main_queue(), ^{ 457 ARDICECandidateRemovalMessage *message = 458 [[ARDICECandidateRemovalMessage alloc] 459 initWithRemovedCandidates:candidates]; 460 [self sendSignalingMessage:message]; 461 }); 462 } 463 464 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 465 didChangeLocalCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)local 466 didChangeRemoteCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)remote 467 lastReceivedMs:(int)lastDataReceivedMs 468 didHaveReason:(NSString *)reason { 469 RTCLog(@"ICE candidate pair changed because: %@", reason); 470 } 471 472 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 473 didOpenDataChannel:(RTC_OBJC_TYPE(RTCDataChannel) *)dataChannel { 474 } 475 476 #pragma mark - RTCSessionDescriptionDelegate 477 // Callbacks for this delegate occur on non-main thread and need to be 478 // dispatched back to main queue as needed. 479 480 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 481 didCreateSessionDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp 482 error:(NSError *)error { 483 dispatch_async(dispatch_get_main_queue(), ^{ 484 if (error) { 485 RTCLogError(@"Failed to create session description. Error: %@", error); 486 [self disconnect]; 487 NSDictionary *userInfo = @{ 488 NSLocalizedDescriptionKey : @"Failed to create session description.", 489 }; 490 NSError *sdpError = 491 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain 492 code:kARDAppClientErrorCreateSDP 493 userInfo:userInfo]; 494 [self.delegate appClient:self didError:sdpError]; 495 return; 496 } 497 __weak ARDAppClient *weakSelf = self; 498 [self.peerConnection 499 setLocalDescription:sdp 500 completionHandler:^(NSError *sldError) { 501 ARDAppClient *strongSelf = weakSelf; 502 [strongSelf peerConnection:strongSelf.peerConnection 503 didSetSessionDescriptionWithError:sldError]; 504 }]; 505 ARDSessionDescriptionMessage *message = 506 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp]; 507 [self sendSignalingMessage:message]; 508 [self setMaxBitrateForPeerConnectionVideoSender]; 509 }); 510 } 511 512 - (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection 513 didSetSessionDescriptionWithError:(NSError *)error { 514 dispatch_async(dispatch_get_main_queue(), ^{ 515 if (error) { 516 RTCLogError(@"Failed to set session description. Error: %@", error); 517 [self disconnect]; 518 NSDictionary *userInfo = @{ 519 NSLocalizedDescriptionKey : @"Failed to set session description.", 520 }; 521 NSError *sdpError = 522 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain 523 code:kARDAppClientErrorSetSDP 524 userInfo:userInfo]; 525 [self.delegate appClient:self didError:sdpError]; 526 return; 527 } 528 // If we're answering and we've just set the remote offer we need to create 529 // an answer and set the local description. 530 if (!self.isInitiator && !self.peerConnection.localDescription) { 531 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = 532 [self defaultAnswerConstraints]; 533 __weak ARDAppClient *weakSelf = self; 534 [self.peerConnection 535 answerForConstraints:constraints 536 completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * sdp, 537 NSError * answerError) { 538 ARDAppClient *strongSelf = weakSelf; 539 [strongSelf peerConnection:strongSelf.peerConnection 540 didCreateSessionDescription:sdp 541 error:answerError]; 542 }]; 543 } 544 }); 545 } 546 547 #pragma mark - Private 548 549 #if defined(WEBRTC_IOS) 550 551 - (NSString *)documentsFilePathForFileName:(NSString *)fileName { 552 NSParameterAssert(fileName.length); 553 NSArray *paths = NSSearchPathForDirectoriesInDomains( 554 NSDocumentDirectory, NSUserDomainMask, YES); 555 NSString *documentsDirPath = paths.firstObject; 556 NSString *filePath = 557 [documentsDirPath stringByAppendingPathComponent:fileName]; 558 return filePath; 559 } 560 561 #endif 562 563 - (BOOL)hasJoinedRoomServerRoom { 564 return _clientId.length; 565 } 566 567 // Begins the peer connection connection process if we have both joined a room 568 // on the room server and tried to obtain a TURN server. Otherwise does nothing. 569 // A peer connection object will be created with a stream that contains local 570 // audio and video capture. If this client is the caller, an offer is created as 571 // well, otherwise the client will wait for an offer to arrive. 572 - (void)startSignalingIfReady { 573 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) { 574 return; 575 } 576 self.state = kARDAppClientStateConnected; 577 578 // Create peer connection. 579 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = 580 [self defaultPeerConnectionConstraints]; 581 RTC_OBJC_TYPE(RTCConfiguration) *config = 582 [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init]; 583 RTC_OBJC_TYPE(RTCCertificate) *pcert = 584 [RTC_OBJC_TYPE(RTCCertificate) generateCertificateWithParams:@{ 585 @"expires" : @100000, 586 @"name" : @"RSASSA-PKCS1-v1_5" 587 }]; 588 config.iceServers = _iceServers; 589 config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; 590 config.certificate = pcert; 591 592 _peerConnection = [_factory peerConnectionWithConfiguration:config 593 constraints:constraints 594 delegate:self]; 595 // Create AV senders. 596 [self createMediaSenders]; 597 if (_isInitiator) { 598 // Send offer. 599 __weak ARDAppClient *weakSelf = self; 600 [_peerConnection 601 offerForConstraints:[self defaultOfferConstraints] 602 completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * sdp, 603 NSError * error) { 604 ARDAppClient *strongSelf = weakSelf; 605 [strongSelf peerConnection:strongSelf.peerConnection 606 didCreateSessionDescription:sdp 607 error:error]; 608 }]; 609 } else { 610 // Check if we've received an offer. 611 [self drainMessageQueueIfReady]; 612 } 613 #if defined(WEBRTC_IOS) 614 // Start event log. 615 if (kARDAppClientEnableRtcEventLog) { 616 NSString *filePath = 617 [self documentsFilePathForFileName:@"webrtc-rtceventlog"]; 618 if (![_peerConnection 619 startRtcEventLogWithFilePath:filePath 620 maxSizeInBytes: 621 kARDAppClientRtcEventLogMaxSizeInBytes]) { 622 RTCLogError(@"Failed to start event logging."); 623 } 624 } 625 626 // Start aecdump diagnostic recording. 627 if ([_settings currentCreateAecDumpSettingFromStore]) { 628 NSString *filePath = 629 [self documentsFilePathForFileName:@"webrtc-audio.aecdump"]; 630 if (![_factory 631 startAecDumpWithFilePath:filePath 632 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) { 633 RTCLogError(@"Failed to start aec dump."); 634 } 635 } 636 #endif 637 } 638 639 // Processes the messages that we've received from the room server and the 640 // signaling channel. The offer or answer message must be processed before other 641 // signaling messages, however they can arrive out of order. Hence, this method 642 // only processes pending messages if there is a peer connection object and 643 // if we have received either an offer or answer. 644 - (void)drainMessageQueueIfReady { 645 if (!_peerConnection || !_hasReceivedSdp) { 646 return; 647 } 648 for (ARDSignalingMessage *message in _messageQueue) { 649 [self processSignalingMessage:message]; 650 } 651 [_messageQueue removeAllObjects]; 652 } 653 654 // Processes the given signaling message based on its type. 655 - (void)processSignalingMessage:(ARDSignalingMessage *)message { 656 NSParameterAssert(_peerConnection || 657 message.type == kARDSignalingMessageTypeBye); 658 switch (message.type) { 659 case kARDSignalingMessageTypeOffer: 660 case kARDSignalingMessageTypeAnswer: { 661 ARDSessionDescriptionMessage *sdpMessage = 662 (ARDSessionDescriptionMessage *)message; 663 RTC_OBJC_TYPE(RTCSessionDescription) *description = 664 sdpMessage.sessionDescription; 665 __weak ARDAppClient *weakSelf = self; 666 [_peerConnection setRemoteDescription:description 667 completionHandler:^(NSError *error) { 668 ARDAppClient *strongSelf = weakSelf; 669 [strongSelf peerConnection:strongSelf.peerConnection 670 didSetSessionDescriptionWithError:error]; 671 }]; 672 break; 673 } 674 case kARDSignalingMessageTypeCandidate: { 675 ARDICECandidateMessage *candidateMessage = 676 (ARDICECandidateMessage *)message; 677 __weak ARDAppClient *weakSelf = self; 678 [_peerConnection addIceCandidate:candidateMessage.candidate 679 completionHandler:^(NSError *error) { 680 ARDAppClient *strongSelf = weakSelf; 681 if (error) { 682 [strongSelf.delegate appClient:strongSelf 683 didError:error]; 684 } 685 }]; 686 break; 687 } 688 case kARDSignalingMessageTypeCandidateRemoval: { 689 ARDICECandidateRemovalMessage *candidateMessage = 690 (ARDICECandidateRemovalMessage *)message; 691 [_peerConnection removeIceCandidates:candidateMessage.candidates]; 692 break; 693 } 694 case kARDSignalingMessageTypeBye: 695 // Other client disconnected. 696 // TODO(tkchin): support waiting in room for next client. For now just 697 // disconnect. 698 [self disconnect]; 699 break; 700 } 701 } 702 703 // Sends a signaling message to the other client. The caller will send messages 704 // through the room server, whereas the callee will send messages over the 705 // signaling channel. 706 - (void)sendSignalingMessage:(ARDSignalingMessage *)message { 707 if (_isInitiator) { 708 __weak ARDAppClient *weakSelf = self; 709 [_roomServerClient 710 sendMessage:message 711 forRoomId:_roomId 712 clientId:_clientId 713 completionHandler:^(ARDMessageResponse *response, NSError *error) { 714 ARDAppClient *strongSelf = weakSelf; 715 if (error) { 716 [strongSelf.delegate appClient:strongSelf didError:error]; 717 return; 718 } 719 NSError *messageError = 720 [[strongSelf class] errorForMessageResultType:response.result]; 721 if (messageError) { 722 [strongSelf.delegate appClient:strongSelf didError:messageError]; 723 return; 724 } 725 }]; 726 } else { 727 [_channel sendMessage:message]; 728 } 729 } 730 731 - (void)setMaxBitrateForPeerConnectionVideoSender { 732 for (RTC_OBJC_TYPE(RTCRtpSender) * sender in _peerConnection.senders) { 733 if (sender.track != nil) { 734 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) { 735 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] 736 forVideoSender:sender]; 737 } 738 } 739 } 740 } 741 742 - (void)setMaxBitrate:(NSNumber *)maxBitrate 743 forVideoSender:(RTC_OBJC_TYPE(RTCRtpSender) *)sender { 744 if (maxBitrate.intValue <= 0) { 745 return; 746 } 747 748 RTC_OBJC_TYPE(RTCRtpParameters) *parametersToModify = sender.parameters; 749 for (RTC_OBJC_TYPE(RTCRtpEncodingParameters) * 750 encoding in parametersToModify.encodings) { 751 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier); 752 } 753 [sender setParameters:parametersToModify]; 754 } 755 756 - (RTC_OBJC_TYPE(RTCRtpTransceiver) *)videoTransceiver { 757 for (RTC_OBJC_TYPE(RTCRtpTransceiver) * 758 transceiver in _peerConnection.transceivers) { 759 if (transceiver.mediaType == RTCRtpMediaTypeVideo) { 760 return transceiver; 761 } 762 } 763 return nil; 764 } 765 766 - (void)createMediaSenders { 767 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = 768 [self defaultMediaAudioConstraints]; 769 RTC_OBJC_TYPE(RTCAudioSource) *source = 770 [_factory audioSourceWithConstraints:constraints]; 771 RTC_OBJC_TYPE(RTCAudioTrack) *audioTrack = 772 [_factory audioTrackWithSource:source trackId:kARDAudioTrackId]; 773 [_peerConnection addTrack:audioTrack streamIds:@[ kARDMediaStreamId ]]; 774 _localVideoTrack = [self createLocalVideoTrack]; 775 if (_localVideoTrack) { 776 [_peerConnection addTrack:_localVideoTrack 777 streamIds:@[ kARDMediaStreamId ]]; 778 [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack]; 779 // We can set up rendering for the remote track right away since the 780 // transceiver already has an RTC_OBJC_TYPE(RTCRtpReceiver) with a track. 781 // The track will automatically get unmuted and produce frames once RTP is 782 // received. 783 RTC_OBJC_TYPE(RTCVideoTrack) *videoTrack = (RTC_OBJC_TYPE(RTCVideoTrack) *)( 784 [self videoTransceiver].receiver.track); 785 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack]; 786 } 787 } 788 789 - (RTC_OBJC_TYPE(RTCVideoTrack) *)createLocalVideoTrack { 790 if ([_settings currentAudioOnlySettingFromStore]) { 791 return nil; 792 } 793 794 RTC_OBJC_TYPE(RTCVideoSource) *source = [_factory videoSource]; 795 796 #if !TARGET_IPHONE_SIMULATOR 797 if (self.isBroadcast) { 798 ARDExternalSampleCapturer *capturer = 799 [[ARDExternalSampleCapturer alloc] initWithDelegate:source]; 800 [_delegate appClient:self didCreateLocalExternalSampleCapturer:capturer]; 801 } else { 802 RTC_OBJC_TYPE(RTCCameraVideoCapturer) *capturer = 803 [[RTC_OBJC_TYPE(RTCCameraVideoCapturer) alloc] initWithDelegate:source]; 804 [_delegate appClient:self didCreateLocalCapturer:capturer]; 805 } 806 #else 807 #if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) 808 if (@available(iOS 10, *)) { 809 RTC_OBJC_TYPE(RTCFileVideoCapturer) *fileCapturer = 810 [[RTC_OBJC_TYPE(RTCFileVideoCapturer) alloc] initWithDelegate:source]; 811 [_delegate appClient:self didCreateLocalFileCapturer:fileCapturer]; 812 } 813 #endif 814 #endif 815 816 return [_factory videoTrackWithSource:source trackId:kARDVideoTrackId]; 817 } 818 819 #pragma mark - Collider methods 820 821 - (void)registerWithColliderIfReady { 822 if (!self.hasJoinedRoomServerRoom) { 823 return; 824 } 825 // Open WebSocket connection. 826 if (!_channel) { 827 _channel = [[ARDWebSocketChannel alloc] initWithURL:_websocketURL 828 restURL:_websocketRestURL 829 delegate:self]; 830 if (_isLoopback) { 831 _loopbackChannel = 832 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL 833 restURL:_websocketRestURL]; 834 } 835 } 836 [_channel registerForRoomId:_roomId clientId:_clientId]; 837 if (_isLoopback) { 838 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"]; 839 } 840 } 841 842 #pragma mark - Defaults 843 844 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultMediaAudioConstraints { 845 NSDictionary *mandatoryConstraints = @{}; 846 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = 847 [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] 848 initWithMandatoryConstraints:mandatoryConstraints 849 optionalConstraints:nil]; 850 return constraints; 851 } 852 853 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultAnswerConstraints { 854 return [self defaultOfferConstraints]; 855 } 856 857 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultOfferConstraints { 858 NSDictionary *mandatoryConstraints = 859 @{@"OfferToReceiveAudio" : @"true", @"OfferToReceiveVideo" : @"true"}; 860 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = 861 [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] 862 initWithMandatoryConstraints:mandatoryConstraints 863 optionalConstraints:nil]; 864 return constraints; 865 } 866 867 - (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultPeerConnectionConstraints { 868 if (_defaultPeerConnectionConstraints) { 869 return _defaultPeerConnectionConstraints; 870 } 871 NSString *value = _isLoopback ? @"false" : @"true"; 872 NSDictionary *optionalConstraints = @{@"DtlsSrtpKeyAgreement" : value}; 873 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = 874 [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] 875 initWithMandatoryConstraints:nil 876 optionalConstraints:optionalConstraints]; 877 return constraints; 878 } 879 880 #pragma mark - Errors 881 882 + (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType { 883 NSError *error = nil; 884 switch (resultType) { 885 case kARDJoinResultTypeSuccess: 886 break; 887 case kARDJoinResultTypeUnknown: { 888 error = [[NSError alloc] 889 initWithDomain:kARDAppClientErrorDomain 890 code:kARDAppClientErrorUnknown 891 userInfo:@{ 892 NSLocalizedDescriptionKey : @"Unknown error.", 893 }]; 894 break; 895 } 896 case kARDJoinResultTypeFull: { 897 error = 898 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain 899 code:kARDAppClientErrorRoomFull 900 userInfo:@{ 901 NSLocalizedDescriptionKey : @"Room is full.", 902 }]; 903 break; 904 } 905 } 906 return error; 907 } 908 909 + (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType { 910 NSError *error = nil; 911 switch (resultType) { 912 case kARDMessageResultTypeSuccess: 913 break; 914 case kARDMessageResultTypeUnknown: 915 error = [[NSError alloc] 916 initWithDomain:kARDAppClientErrorDomain 917 code:kARDAppClientErrorUnknown 918 userInfo:@{ 919 NSLocalizedDescriptionKey : @"Unknown error.", 920 }]; 921 break; 922 case kARDMessageResultTypeInvalidClient: 923 error = [[NSError alloc] 924 initWithDomain:kARDAppClientErrorDomain 925 code:kARDAppClientErrorInvalidClient 926 userInfo:@{ 927 NSLocalizedDescriptionKey : @"Invalid client.", 928 }]; 929 break; 930 case kARDMessageResultTypeInvalidRoom: 931 error = 932 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain 933 code:kARDAppClientErrorInvalidRoom 934 userInfo:@{ 935 NSLocalizedDescriptionKey : @"Invalid room.", 936 }]; 937 break; 938 } 939 return error; 940 } 941 942 @end