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