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