blob: 40a2be7cbf8cc3ec5d2c96f10d7693ba2504dcd7 [file] [log] [blame]
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00001/*
Donald E Curtisa8736442015-08-05 15:48:13 -07002 * Copyright 2014 The WebRTC Project Authors. All rights reserved.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00003 *
Donald E Curtisa8736442015-08-05 15:48:13 -07004 * 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.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00009 */
10
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000011#import "ARDAppClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000012
tkchin9eeb6242016-04-27 01:54:20 -070013#import "WebRTC/RTCAVFoundationVideoSource.h"
tkchin9eeb6242016-04-27 01:54:20 -070014#import "WebRTC/RTCAudioTrack.h"
15#import "WebRTC/RTCConfiguration.h"
16#import "WebRTC/RTCFileLogger.h"
17#import "WebRTC/RTCIceServer.h"
18#import "WebRTC/RTCLogging.h"
19#import "WebRTC/RTCMediaConstraints.h"
20#import "WebRTC/RTCMediaStream.h"
21#import "WebRTC/RTCPeerConnectionFactory.h"
skvladf3569c82016-04-29 15:30:16 -070022#import "WebRTC/RTCRtpSender.h"
tkchin204177f2016-06-14 15:03:11 -070023#import "WebRTC/RTCTracing.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000024
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000025#import "ARDAppEngineClient.h"
26#import "ARDCEODTURNClient.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000027#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000028#import "ARDMessageResponse.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070029#import "ARDSDPUtils.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000030#import "ARDSignalingMessage.h"
31#import "ARDUtilities.h"
32#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070033#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000034#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070035
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000036static NSString * const kARDDefaultSTUNServerUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000037 @"stun:stun.l.google.com:19302";
38// TODO(tkchin): figure out a better username for CEOD statistics.
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000039static NSString * const kARDTurnRequestUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000040 @"https://computeengineondemand.appspot.com"
41 @"/turn?username=iapprtc&key=4080218913";
42
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000043static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
44static NSInteger const kARDAppClientErrorUnknown = -1;
45static NSInteger const kARDAppClientErrorRoomFull = -2;
46static NSInteger const kARDAppClientErrorCreateSDP = -3;
47static NSInteger const kARDAppClientErrorSetSDP = -4;
48static NSInteger const kARDAppClientErrorInvalidClient = -5;
49static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070050static NSString * const kARDMediaStreamId = @"ARDAMS";
51static NSString * const kARDAudioTrackId = @"ARDAMSa0";
52static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080053static NSString * const kARDVideoTrackKind = @"video";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000054
tkchin204177f2016-06-14 15:03:11 -070055// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080056static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070057static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070058static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070059static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
denicija9af2b602016-11-17 00:43:43 -080060static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080061
Zeke Chind3325802015-08-14 11:00:02 -070062// We need a proxy to NSTimer because it causes a strong retain cycle. When
63// using the proxy, |invalidate| must be called before it properly deallocs.
64@interface ARDTimerProxy : NSObject
65
66- (instancetype)initWithInterval:(NSTimeInterval)interval
67 repeats:(BOOL)repeats
68 timerHandler:(void (^)(void))timerHandler;
69- (void)invalidate;
70
71@end
72
73@implementation ARDTimerProxy {
74 NSTimer *_timer;
75 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070076}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000077
Zeke Chind3325802015-08-14 11:00:02 -070078- (instancetype)initWithInterval:(NSTimeInterval)interval
79 repeats:(BOOL)repeats
80 timerHandler:(void (^)(void))timerHandler {
81 NSParameterAssert(timerHandler);
82 if (self = [super init]) {
83 _timerHandler = timerHandler;
84 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
85 target:self
86 selector:@selector(timerDidFire:)
87 userInfo:nil
88 repeats:repeats];
89 }
90 return self;
91}
92
93- (void)invalidate {
94 [_timer invalidate];
95}
96
97- (void)timerDidFire:(NSTimer *)timer {
98 _timerHandler();
99}
100
101@end
102
103@implementation ARDAppClient {
104 RTCFileLogger *_fileLogger;
105 ARDTimerProxy *_statsTimer;
denicijad17d5362016-11-02 02:56:09 -0700106 RTCMediaConstraints *_cameraConstraints;
denicija8c375de2016-11-08 06:28:17 -0800107 NSNumber *_maxBitrate;
Zeke Chind3325802015-08-14 11:00:02 -0700108}
109
110@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000111@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700112@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000113@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000114@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700115@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000116@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000117@synthesize peerConnection = _peerConnection;
118@synthesize factory = _factory;
119@synthesize messageQueue = _messageQueue;
120@synthesize isTurnComplete = _isTurnComplete;
121@synthesize hasReceivedSdp = _hasReceivedSdp;
122@synthesize roomId = _roomId;
123@synthesize clientId = _clientId;
124@synthesize isInitiator = _isInitiator;
125@synthesize iceServers = _iceServers;
126@synthesize webSocketURL = _websocketURL;
127@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000128@synthesize defaultPeerConnectionConstraints =
129 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700130@synthesize isLoopback = _isLoopback;
131@synthesize isAudioOnly = _isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700132@synthesize shouldMakeAecDump = _shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700133@synthesize shouldUseLevelControl = _shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000134
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000135- (instancetype)init {
136 if (self = [super init]) {
137 _roomServerClient = [[ARDAppEngineClient alloc] init];
138 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
139 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
140 [self configure];
141 }
142 return self;
143}
144
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000145- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
146 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000147 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000148 _delegate = delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000149 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
150 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
151 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000152 }
153 return self;
154}
155
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000156// TODO(tkchin): Provide signaling channel factory interface so we can recreate
157// channel if we need to on network failure. Also, make this the default public
158// constructor.
159- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
160 signalingChannel:(id<ARDSignalingChannel>)channel
161 turnClient:(id<ARDTURNClient>)turnClient
162 delegate:(id<ARDAppClientDelegate>)delegate {
163 NSParameterAssert(rsClient);
164 NSParameterAssert(channel);
165 NSParameterAssert(turnClient);
166 if (self = [super init]) {
167 _roomServerClient = rsClient;
168 _channel = channel;
169 _turnClient = turnClient;
170 _delegate = delegate;
171 [self configure];
172 }
173 return self;
174}
175
176- (void)configure {
177 _factory = [[RTCPeerConnectionFactory alloc] init];
178 _messageQueue = [NSMutableArray array];
179 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700180 _fileLogger = [[RTCFileLogger alloc] init];
181 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000182}
183
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000184- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700185 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000186 [self disconnect];
187}
188
Zeke Chind3325802015-08-14 11:00:02 -0700189- (void)setShouldGetStats:(BOOL)shouldGetStats {
190 if (_shouldGetStats == shouldGetStats) {
191 return;
192 }
193 if (shouldGetStats) {
194 __weak ARDAppClient *weakSelf = self;
195 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
196 repeats:YES
197 timerHandler:^{
198 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700199 [strongSelf.peerConnection statsForTrack:nil
200 statsOutputLevel:RTCStatsOutputLevelDebug
201 completionHandler:^(NSArray *stats) {
202 dispatch_async(dispatch_get_main_queue(), ^{
203 ARDAppClient *strongSelf = weakSelf;
204 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
205 });
206 }];
Zeke Chind3325802015-08-14 11:00:02 -0700207 }];
208 } else {
209 [_statsTimer invalidate];
210 _statsTimer = nil;
211 }
212 _shouldGetStats = shouldGetStats;
213}
214
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000215- (void)setState:(ARDAppClientState)state {
216 if (_state == state) {
217 return;
218 }
219 _state = state;
220 [_delegate appClient:self didChangeState:_state];
221}
222
223- (void)connectToRoomWithId:(NSString *)roomId
haysc913e6452015-10-02 11:44:03 -0700224 isLoopback:(BOOL)isLoopback
peah5085b0c2016-08-25 22:15:14 -0700225 isAudioOnly:(BOOL)isAudioOnly
tkchinab1293a2016-08-30 12:35:05 -0700226 shouldMakeAecDump:(BOOL)shouldMakeAecDump
227 shouldUseLevelControl:(BOOL)shouldUseLevelControl {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000228 NSParameterAssert(roomId.length);
229 NSParameterAssert(_state == kARDAppClientStateDisconnected);
haysc913e6452015-10-02 11:44:03 -0700230 _isLoopback = isLoopback;
231 _isAudioOnly = isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700232 _shouldMakeAecDump = shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700233 _shouldUseLevelControl = shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000234 self.state = kARDAppClientStateConnecting;
235
tkchind1fb26d2016-02-03 01:51:18 -0800236#if defined(WEBRTC_IOS)
237 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700238 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800239 RTCStartInternalCapture(filePath);
240 }
241#endif
242
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000243 // Request TURN.
244 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000245 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
246 NSError *error) {
247 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700248 RTCLogError("Error retrieving TURN servers: %@",
249 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000250 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000251 ARDAppClient *strongSelf = weakSelf;
252 [strongSelf.iceServers addObjectsFromArray:turnServers];
253 strongSelf.isTurnComplete = YES;
254 [strongSelf startSignalingIfReady];
255 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000256
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000257 // Join room on room server.
258 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700259 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000260 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000261 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000262 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000263 [strongSelf.delegate appClient:strongSelf didError:error];
264 return;
265 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000266 NSError *joinError =
267 [[strongSelf class] errorForJoinResultType:response.result];
268 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700269 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000270 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000271 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000272 return;
273 }
tkchinc3f46a92015-07-23 12:50:55 -0700274 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000275 strongSelf.roomId = response.roomId;
276 strongSelf.clientId = response.clientId;
277 strongSelf.isInitiator = response.isInitiator;
278 for (ARDSignalingMessage *message in response.messages) {
279 if (message.type == kARDSignalingMessageTypeOffer ||
280 message.type == kARDSignalingMessageTypeAnswer) {
281 strongSelf.hasReceivedSdp = YES;
282 [strongSelf.messageQueue insertObject:message atIndex:0];
283 } else {
284 [strongSelf.messageQueue addObject:message];
285 }
286 }
287 strongSelf.webSocketURL = response.webSocketURL;
288 strongSelf.webSocketRestURL = response.webSocketRestURL;
289 [strongSelf registerWithColliderIfReady];
290 [strongSelf startSignalingIfReady];
291 }];
292}
293
294- (void)disconnect {
295 if (_state == kARDAppClientStateDisconnected) {
296 return;
297 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000298 if (self.hasJoinedRoomServerRoom) {
299 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000300 clientId:_clientId
301 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000302 }
303 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000304 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000305 // Tell the other client we're hanging up.
306 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000307 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000308 }
309 // Disconnect from collider.
310 _channel = nil;
311 }
312 _clientId = nil;
313 _roomId = nil;
314 _isInitiator = NO;
315 _hasReceivedSdp = NO;
316 _messageQueue = [NSMutableArray array];
ivoc14d5dbe2016-07-04 07:06:55 -0700317#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700318 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700319 [_peerConnection stopRtcEventLog];
320#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000321 _peerConnection = nil;
322 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800323#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800324 if (kARDAppClientEnableTracing) {
325 RTCStopInternalCapture();
326 }
tkchind1fb26d2016-02-03 01:51:18 -0800327#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000328}
329
denicijad17d5362016-11-02 02:56:09 -0700330- (void)setCameraConstraints:(RTCMediaConstraints *)mediaConstraints {
331 _cameraConstraints = mediaConstraints;
332}
333
denicija8c375de2016-11-08 06:28:17 -0800334- (void)setMaxBitrate:(NSNumber *)maxBitrate {
335 _maxBitrate = maxBitrate;
336}
337
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000338#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000339
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000340- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000341 didReceiveMessage:(ARDSignalingMessage *)message {
342 switch (message.type) {
343 case kARDSignalingMessageTypeOffer:
344 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000345 // Offers and answers must be processed before any other message, so we
346 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000347 _hasReceivedSdp = YES;
348 [_messageQueue insertObject:message atIndex:0];
349 break;
350 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700351 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000352 [_messageQueue addObject:message];
353 break;
354 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000355 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000356 [self processSignalingMessage:message];
357 return;
358 }
359 [self drainMessageQueueIfReady];
360}
361
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000362- (void)channel:(id<ARDSignalingChannel>)channel
363 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000364 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000365 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000366 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000367 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000368 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000369 case kARDSignalingChannelStateClosed:
370 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000371 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
372 // completely if the websocket connection fails.
373 [self disconnect];
374 break;
375 }
376}
377
378#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000379// Callbacks for this delegate occur on non-main thread and need to be
380// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000381
382- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700383 didChangeSignalingState:(RTCSignalingState)stateChanged {
384 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000385}
386
387- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700388 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000389 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700390 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000391 (unsigned long)stream.videoTracks.count,
392 (unsigned long)stream.audioTracks.count);
393 if (stream.videoTracks.count) {
394 RTCVideoTrack *videoTrack = stream.videoTracks[0];
395 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
396 }
397 });
398}
399
400- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700401 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700402 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000403}
404
hjon79858f82016-03-13 22:08:26 -0700405- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700406 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000407}
408
409- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700410 didChangeIceConnectionState:(RTCIceConnectionState)newState {
411 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000412 dispatch_async(dispatch_get_main_queue(), ^{
413 [_delegate appClient:self didChangeConnectionState:newState];
414 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000415}
416
417- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700418 didChangeIceGatheringState:(RTCIceGatheringState)newState {
419 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000420}
421
422- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700423 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000424 dispatch_async(dispatch_get_main_queue(), ^{
425 ARDICECandidateMessage *message =
426 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
427 [self sendSignalingMessage:message];
428 });
429}
430
Zeke Chind3325802015-08-14 11:00:02 -0700431- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700432 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
433 dispatch_async(dispatch_get_main_queue(), ^{
434 ARDICECandidateRemovalMessage *message =
435 [[ARDICECandidateRemovalMessage alloc]
436 initWithRemovedCandidates:candidates];
437 [self sendSignalingMessage:message];
438 });
439}
440
441- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700442 didOpenDataChannel:(RTCDataChannel *)dataChannel {
443}
444
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000445#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000446// Callbacks for this delegate occur on non-main thread and need to be
447// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000448
449- (void)peerConnection:(RTCPeerConnection *)peerConnection
450 didCreateSessionDescription:(RTCSessionDescription *)sdp
451 error:(NSError *)error {
452 dispatch_async(dispatch_get_main_queue(), ^{
453 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700454 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000455 [self disconnect];
456 NSDictionary *userInfo = @{
457 NSLocalizedDescriptionKey: @"Failed to create session description.",
458 };
459 NSError *sdpError =
460 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
461 code:kARDAppClientErrorCreateSDP
462 userInfo:userInfo];
463 [_delegate appClient:self didError:sdpError];
464 return;
465 }
Zeke Chin71f6f442015-06-29 14:34:58 -0700466 // Prefer H264 if available.
467 RTCSessionDescription *sdpPreferringH264 =
468 [ARDSDPUtils descriptionForDescription:sdp
469 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700470 __weak ARDAppClient *weakSelf = self;
471 [_peerConnection setLocalDescription:sdpPreferringH264
472 completionHandler:^(NSError *error) {
473 ARDAppClient *strongSelf = weakSelf;
474 [strongSelf peerConnection:strongSelf.peerConnection
475 didSetSessionDescriptionWithError:error];
476 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000477 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700478 [[ARDSessionDescriptionMessage alloc]
479 initWithDescription:sdpPreferringH264];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000480 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800481 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000482 });
483}
484
485- (void)peerConnection:(RTCPeerConnection *)peerConnection
486 didSetSessionDescriptionWithError:(NSError *)error {
487 dispatch_async(dispatch_get_main_queue(), ^{
488 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700489 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000490 [self disconnect];
491 NSDictionary *userInfo = @{
492 NSLocalizedDescriptionKey: @"Failed to set session description.",
493 };
494 NSError *sdpError =
495 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
496 code:kARDAppClientErrorSetSDP
497 userInfo:userInfo];
498 [_delegate appClient:self didError:sdpError];
499 return;
500 }
501 // If we're answering and we've just set the remote offer we need to create
502 // an answer and set the local description.
503 if (!_isInitiator && !_peerConnection.localDescription) {
504 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700505 __weak ARDAppClient *weakSelf = self;
506 [_peerConnection answerForConstraints:constraints
507 completionHandler:^(RTCSessionDescription *sdp,
508 NSError *error) {
509 ARDAppClient *strongSelf = weakSelf;
510 [strongSelf peerConnection:strongSelf.peerConnection
511 didCreateSessionDescription:sdp
512 error:error];
513 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000514 }
515 });
516}
517
518#pragma mark - Private
519
tkchin204177f2016-06-14 15:03:11 -0700520#if defined(WEBRTC_IOS)
521
522- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
523 NSParameterAssert(fileName.length);
524 NSArray *paths = NSSearchPathForDirectoriesInDomains(
525 NSDocumentDirectory, NSUserDomainMask, YES);
526 NSString *documentsDirPath = paths.firstObject;
527 NSString *filePath =
528 [documentsDirPath stringByAppendingPathComponent:fileName];
529 return filePath;
530}
531
532#endif
533
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000534- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000535 return _clientId.length;
536}
537
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000538// Begins the peer connection connection process if we have both joined a room
539// on the room server and tried to obtain a TURN server. Otherwise does nothing.
540// A peer connection object will be created with a stream that contains local
541// audio and video capture. If this client is the caller, an offer is created as
542// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000543- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000544 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000545 return;
546 }
547 self.state = kARDAppClientStateConnected;
548
549 // Create peer connection.
550 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700551 RTCConfiguration *config = [[RTCConfiguration alloc] init];
552 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700553 _peerConnection = [_factory peerConnectionWithConfiguration:config
554 constraints:constraints
555 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700556 // Create AV senders.
557 [self createAudioSender];
558 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000559 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000560 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700561 __weak ARDAppClient *weakSelf = self;
562 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
563 completionHandler:^(RTCSessionDescription *sdp,
564 NSError *error) {
565 ARDAppClient *strongSelf = weakSelf;
566 [strongSelf peerConnection:strongSelf.peerConnection
567 didCreateSessionDescription:sdp
568 error:error];
569 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000570 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000571 // Check if we've received an offer.
572 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000573 }
ivoc14d5dbe2016-07-04 07:06:55 -0700574#if defined(WEBRTC_IOS)
575 // Start event log.
576 if (kARDAppClientEnableRtcEventLog) {
577 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
578 if (![_peerConnection startRtcEventLogWithFilePath:filePath
579 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
580 RTCLogError(@"Failed to start event logging.");
581 }
582 }
peah5085b0c2016-08-25 22:15:14 -0700583
584 // Start aecdump diagnostic recording.
585 if (_shouldMakeAecDump) {
tkchinfce0e2c2016-08-30 12:58:11 -0700586 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
587 if (![_factory startAecDumpWithFilePath:filePath
588 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
589 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700590 }
591 }
ivoc14d5dbe2016-07-04 07:06:55 -0700592#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000593}
594
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000595// Processes the messages that we've received from the room server and the
596// signaling channel. The offer or answer message must be processed before other
597// signaling messages, however they can arrive out of order. Hence, this method
598// only processes pending messages if there is a peer connection object and
599// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000600- (void)drainMessageQueueIfReady {
601 if (!_peerConnection || !_hasReceivedSdp) {
602 return;
603 }
604 for (ARDSignalingMessage *message in _messageQueue) {
605 [self processSignalingMessage:message];
606 }
607 [_messageQueue removeAllObjects];
608}
609
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000610// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000611- (void)processSignalingMessage:(ARDSignalingMessage *)message {
612 NSParameterAssert(_peerConnection ||
613 message.type == kARDSignalingMessageTypeBye);
614 switch (message.type) {
615 case kARDSignalingMessageTypeOffer:
616 case kARDSignalingMessageTypeAnswer: {
617 ARDSessionDescriptionMessage *sdpMessage =
618 (ARDSessionDescriptionMessage *)message;
619 RTCSessionDescription *description = sdpMessage.sessionDescription;
Zeke Chin71f6f442015-06-29 14:34:58 -0700620 // Prefer H264 if available.
621 RTCSessionDescription *sdpPreferringH264 =
622 [ARDSDPUtils descriptionForDescription:description
623 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700624 __weak ARDAppClient *weakSelf = self;
625 [_peerConnection setRemoteDescription:sdpPreferringH264
626 completionHandler:^(NSError *error) {
627 ARDAppClient *strongSelf = weakSelf;
628 [strongSelf peerConnection:strongSelf.peerConnection
629 didSetSessionDescriptionWithError:error];
630 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000631 break;
632 }
633 case kARDSignalingMessageTypeCandidate: {
634 ARDICECandidateMessage *candidateMessage =
635 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700636 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000637 break;
638 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700639 case kARDSignalingMessageTypeCandidateRemoval: {
640 ARDICECandidateRemovalMessage *candidateMessage =
641 (ARDICECandidateRemovalMessage *)message;
642 [_peerConnection removeIceCandidates:candidateMessage.candidates];
643 break;
644 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000645 case kARDSignalingMessageTypeBye:
646 // Other client disconnected.
647 // TODO(tkchin): support waiting in room for next client. For now just
648 // disconnect.
649 [self disconnect];
650 break;
651 }
652}
653
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000654// Sends a signaling message to the other client. The caller will send messages
655// through the room server, whereas the callee will send messages over the
656// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000657- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
658 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000659 __weak ARDAppClient *weakSelf = self;
660 [_roomServerClient sendMessage:message
661 forRoomId:_roomId
662 clientId:_clientId
663 completionHandler:^(ARDMessageResponse *response,
664 NSError *error) {
665 ARDAppClient *strongSelf = weakSelf;
666 if (error) {
667 [strongSelf.delegate appClient:strongSelf didError:error];
668 return;
669 }
670 NSError *messageError =
671 [[strongSelf class] errorForMessageResultType:response.result];
672 if (messageError) {
673 [strongSelf.delegate appClient:strongSelf didError:messageError];
674 return;
675 }
676 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000677 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000678 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000679 }
680}
681
skvladf3569c82016-04-29 15:30:16 -0700682- (RTCRtpSender *)createVideoSender {
683 RTCRtpSender *sender =
684 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
685 streamId:kARDMediaStreamId];
686 RTCVideoTrack *track = [self createLocalVideoTrack];
687 if (track) {
688 sender.track = track;
689 [_delegate appClient:self didReceiveLocalVideoTrack:track];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700690 }
denicija9af2b602016-11-17 00:43:43 -0800691
skvladf3569c82016-04-29 15:30:16 -0700692 return sender;
693}
694
denicija9af2b602016-11-17 00:43:43 -0800695- (void)setMaxBitrateForPeerConnectionVideoSender {
696 for (RTCRtpSender *sender in _peerConnection.senders) {
697 if (sender.track != nil) {
698 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
699 [self setMaxBitrate:_maxBitrate forVideoSender:sender];
700 }
701 }
denicija8c375de2016-11-08 06:28:17 -0800702 }
703}
704
denicija9af2b602016-11-17 00:43:43 -0800705- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
706 if (maxBitrate.intValue <= 0) {
707 return;
708 }
709
710 RTCRtpParameters *parametersToModify = sender.parameters;
711 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
712 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
713 }
714 [sender setParameters:parametersToModify];
715}
716
skvladf3569c82016-04-29 15:30:16 -0700717- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700718 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
719 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
720 RTCAudioTrack *track = [_factory audioTrackWithSource:source
721 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700722 RTCRtpSender *sender =
723 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
724 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700725 sender.track = track;
726 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700727}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000728
Zeke Chin57cc74e2015-05-05 07:52:31 -0700729- (RTCVideoTrack *)createLocalVideoTrack {
730 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000731 // The iOS simulator doesn't provide any sort of camera capture
732 // support or emulation (http://goo.gl/rHAnC1) so don't bother
733 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700734#if !TARGET_IPHONE_SIMULATOR
haysc913e6452015-10-02 11:44:03 -0700735 if (!_isAudioOnly) {
denicijad17d5362016-11-02 02:56:09 -0700736 RTCMediaConstraints *cameraConstraints =
737 [self cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700738 RTCAVFoundationVideoSource *source =
denicijad17d5362016-11-02 02:56:09 -0700739 [_factory avFoundationVideoSourceWithConstraints:cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700740 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700741 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700742 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700743 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000744#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700745 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000746}
747
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000748#pragma mark - Collider methods
749
750- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000751 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000752 return;
753 }
754 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000755 if (!_channel) {
756 _channel =
757 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
758 restURL:_websocketRestURL
759 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700760 if (_isLoopback) {
761 _loopbackChannel =
762 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
763 restURL:_websocketRestURL];
764 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000765 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000766 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700767 if (_isLoopback) {
768 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
769 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000770}
771
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000772#pragma mark - Defaults
773
tkchinab1293a2016-08-30 12:35:05 -0700774 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
775 NSString *valueLevelControl = _shouldUseLevelControl ?
776 kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
777 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700778 RTCMediaConstraints *constraints =
779 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
780 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700781 return constraints;
782}
783
denicijad17d5362016-11-02 02:56:09 -0700784- (RTCMediaConstraints *)cameraConstraints {
785 return _cameraConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000786}
787
788- (RTCMediaConstraints *)defaultAnswerConstraints {
789 return [self defaultOfferConstraints];
790}
791
792- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700793 NSDictionary *mandatoryConstraints = @{
794 @"OfferToReceiveAudio" : @"true",
795 @"OfferToReceiveVideo" : @"true"
796 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000797 RTCMediaConstraints* constraints =
798 [[RTCMediaConstraints alloc]
799 initWithMandatoryConstraints:mandatoryConstraints
800 optionalConstraints:nil];
801 return constraints;
802}
803
804- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000805 if (_defaultPeerConnectionConstraints) {
806 return _defaultPeerConnectionConstraints;
807 }
haysc913e6452015-10-02 11:44:03 -0700808 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700809 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000810 RTCMediaConstraints* constraints =
811 [[RTCMediaConstraints alloc]
812 initWithMandatoryConstraints:nil
813 optionalConstraints:optionalConstraints];
814 return constraints;
815}
816
hjon79858f82016-03-13 22:08:26 -0700817- (RTCIceServer *)defaultSTUNServer {
818 return [[RTCIceServer alloc] initWithURLStrings:@[kARDDefaultSTUNServerUrl]
819 username:@""
820 credential:@""];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000821}
822
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000823#pragma mark - Errors
824
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000825+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000826 NSError *error = nil;
827 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000828 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000829 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000830 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000831 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
832 code:kARDAppClientErrorUnknown
833 userInfo:@{
834 NSLocalizedDescriptionKey: @"Unknown error.",
835 }];
836 break;
837 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000838 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000839 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
840 code:kARDAppClientErrorRoomFull
841 userInfo:@{
842 NSLocalizedDescriptionKey: @"Room is full.",
843 }];
844 break;
845 }
846 }
847 return error;
848}
849
850+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
851 NSError *error = nil;
852 switch (resultType) {
853 case kARDMessageResultTypeSuccess:
854 break;
855 case kARDMessageResultTypeUnknown:
856 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
857 code:kARDAppClientErrorUnknown
858 userInfo:@{
859 NSLocalizedDescriptionKey: @"Unknown error.",
860 }];
861 break;
862 case kARDMessageResultTypeInvalidClient:
863 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
864 code:kARDAppClientErrorInvalidClient
865 userInfo:@{
866 NSLocalizedDescriptionKey: @"Invalid client.",
867 }];
868 break;
869 case kARDMessageResultTypeInvalidRoom:
870 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
871 code:kARDAppClientErrorInvalidRoom
872 userInfo:@{
873 NSLocalizedDescriptionKey: @"Invalid room.",
874 }];
875 break;
876 }
877 return error;
878}
879
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000880@end