blob: 5539adbb4da337c5333316ce08161b659cba7d6d [file] [log] [blame]
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00001/*
2 * libjingle
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 * Copyright 2014 Google Inc.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000028#import "ARDAppClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000029
30#import <AVFoundation/AVFoundation.h>
31
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000032#import "ARDAppEngineClient.h"
33#import "ARDCEODTURNClient.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000034#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000035#import "ARDMessageResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000036#import "ARDSignalingMessage.h"
37#import "ARDUtilities.h"
38#import "ARDWebSocketChannel.h"
39#import "RTCICECandidate+JSON.h"
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000040#import "RTCICEServer.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000041#import "RTCMediaConstraints.h"
42#import "RTCMediaStream.h"
43#import "RTCPair.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000044#import "RTCSessionDescription+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000045#import "RTCVideoCapturer.h"
46#import "RTCVideoTrack.h"
47
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000048static NSString *kARDDefaultSTUNServerUrl =
49 @"stun:stun.l.google.com:19302";
50// TODO(tkchin): figure out a better username for CEOD statistics.
51static NSString *kARDTurnRequestUrl =
52 @"https://computeengineondemand.appspot.com"
53 @"/turn?username=iapprtc&key=4080218913";
54
55static NSString *kARDAppClientErrorDomain = @"ARDAppClient";
56static NSInteger kARDAppClientErrorUnknown = -1;
57static NSInteger kARDAppClientErrorRoomFull = -2;
58static NSInteger kARDAppClientErrorCreateSDP = -3;
59static NSInteger kARDAppClientErrorSetSDP = -4;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000060static NSInteger kARDAppClientErrorInvalidClient = -5;
61static NSInteger kARDAppClientErrorInvalidRoom = -6;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000062
63@implementation ARDAppClient
64
65@synthesize delegate = _delegate;
66@synthesize state = _state;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000067@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000068@synthesize channel = _channel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000069@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000070@synthesize peerConnection = _peerConnection;
71@synthesize factory = _factory;
72@synthesize messageQueue = _messageQueue;
73@synthesize isTurnComplete = _isTurnComplete;
74@synthesize hasReceivedSdp = _hasReceivedSdp;
75@synthesize roomId = _roomId;
76@synthesize clientId = _clientId;
77@synthesize isInitiator = _isInitiator;
78@synthesize iceServers = _iceServers;
79@synthesize webSocketURL = _websocketURL;
80@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000081@synthesize defaultPeerConnectionConstraints =
82 _defaultPeerConnectionConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000083
84- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
85 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000086 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000087 _delegate = delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000088 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
89 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
90 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000091 }
92 return self;
93}
94
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000095// TODO(tkchin): Provide signaling channel factory interface so we can recreate
96// channel if we need to on network failure. Also, make this the default public
97// constructor.
98- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
99 signalingChannel:(id<ARDSignalingChannel>)channel
100 turnClient:(id<ARDTURNClient>)turnClient
101 delegate:(id<ARDAppClientDelegate>)delegate {
102 NSParameterAssert(rsClient);
103 NSParameterAssert(channel);
104 NSParameterAssert(turnClient);
105 if (self = [super init]) {
106 _roomServerClient = rsClient;
107 _channel = channel;
108 _turnClient = turnClient;
109 _delegate = delegate;
110 [self configure];
111 }
112 return self;
113}
114
115- (void)configure {
116 _factory = [[RTCPeerConnectionFactory alloc] init];
117 _messageQueue = [NSMutableArray array];
118 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
119}
120
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000121- (void)dealloc {
122 [self disconnect];
123}
124
125- (void)setState:(ARDAppClientState)state {
126 if (_state == state) {
127 return;
128 }
129 _state = state;
130 [_delegate appClient:self didChangeState:_state];
131}
132
133- (void)connectToRoomWithId:(NSString *)roomId
134 options:(NSDictionary *)options {
135 NSParameterAssert(roomId.length);
136 NSParameterAssert(_state == kARDAppClientStateDisconnected);
137 self.state = kARDAppClientStateConnecting;
138
139 // Request TURN.
140 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000141 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
142 NSError *error) {
143 if (error) {
144 NSLog(@"Error retrieving TURN servers: %@", error);
145 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000146 ARDAppClient *strongSelf = weakSelf;
147 [strongSelf.iceServers addObjectsFromArray:turnServers];
148 strongSelf.isTurnComplete = YES;
149 [strongSelf startSignalingIfReady];
150 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000151
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000152 // Join room on room server.
153 [_roomServerClient joinRoomWithRoomId:roomId
154 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000155 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000156 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000157 [strongSelf.delegate appClient:strongSelf didError:error];
158 return;
159 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000160 NSError *joinError =
161 [[strongSelf class] errorForJoinResultType:response.result];
162 if (joinError) {
163 NSLog(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000164 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000165 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000166 return;
167 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000168 NSLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000169 strongSelf.roomId = response.roomId;
170 strongSelf.clientId = response.clientId;
171 strongSelf.isInitiator = response.isInitiator;
172 for (ARDSignalingMessage *message in response.messages) {
173 if (message.type == kARDSignalingMessageTypeOffer ||
174 message.type == kARDSignalingMessageTypeAnswer) {
175 strongSelf.hasReceivedSdp = YES;
176 [strongSelf.messageQueue insertObject:message atIndex:0];
177 } else {
178 [strongSelf.messageQueue addObject:message];
179 }
180 }
181 strongSelf.webSocketURL = response.webSocketURL;
182 strongSelf.webSocketRestURL = response.webSocketRestURL;
183 [strongSelf registerWithColliderIfReady];
184 [strongSelf startSignalingIfReady];
185 }];
186}
187
188- (void)disconnect {
189 if (_state == kARDAppClientStateDisconnected) {
190 return;
191 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000192 if (self.hasJoinedRoomServerRoom) {
193 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000194 clientId:_clientId
195 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000196 }
197 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000198 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000199 // Tell the other client we're hanging up.
200 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000201 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000202 }
203 // Disconnect from collider.
204 _channel = nil;
205 }
206 _clientId = nil;
207 _roomId = nil;
208 _isInitiator = NO;
209 _hasReceivedSdp = NO;
210 _messageQueue = [NSMutableArray array];
211 _peerConnection = nil;
212 self.state = kARDAppClientStateDisconnected;
213}
214
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000215#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000216
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000217- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000218 didReceiveMessage:(ARDSignalingMessage *)message {
219 switch (message.type) {
220 case kARDSignalingMessageTypeOffer:
221 case kARDSignalingMessageTypeAnswer:
222 _hasReceivedSdp = YES;
223 [_messageQueue insertObject:message atIndex:0];
224 break;
225 case kARDSignalingMessageTypeCandidate:
226 [_messageQueue addObject:message];
227 break;
228 case kARDSignalingMessageTypeBye:
229 [self processSignalingMessage:message];
230 return;
231 }
232 [self drainMessageQueueIfReady];
233}
234
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000235- (void)channel:(id<ARDSignalingChannel>)channel
236 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000237 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000238 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000239 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000240 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000241 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000242 case kARDSignalingChannelStateClosed:
243 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000244 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
245 // completely if the websocket connection fails.
246 [self disconnect];
247 break;
248 }
249}
250
251#pragma mark - RTCPeerConnectionDelegate
252
253- (void)peerConnection:(RTCPeerConnection *)peerConnection
254 signalingStateChanged:(RTCSignalingState)stateChanged {
255 NSLog(@"Signaling state changed: %d", stateChanged);
256}
257
258- (void)peerConnection:(RTCPeerConnection *)peerConnection
259 addedStream:(RTCMediaStream *)stream {
260 dispatch_async(dispatch_get_main_queue(), ^{
261 NSLog(@"Received %lu video tracks and %lu audio tracks",
262 (unsigned long)stream.videoTracks.count,
263 (unsigned long)stream.audioTracks.count);
264 if (stream.videoTracks.count) {
265 RTCVideoTrack *videoTrack = stream.videoTracks[0];
266 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
267 }
268 });
269}
270
271- (void)peerConnection:(RTCPeerConnection *)peerConnection
272 removedStream:(RTCMediaStream *)stream {
273 NSLog(@"Stream was removed.");
274}
275
276- (void)peerConnectionOnRenegotiationNeeded:
277 (RTCPeerConnection *)peerConnection {
278 NSLog(@"WARNING: Renegotiation needed but unimplemented.");
279}
280
281- (void)peerConnection:(RTCPeerConnection *)peerConnection
282 iceConnectionChanged:(RTCICEConnectionState)newState {
283 NSLog(@"ICE state changed: %d", newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000284 dispatch_async(dispatch_get_main_queue(), ^{
285 [_delegate appClient:self didChangeConnectionState:newState];
286 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000287}
288
289- (void)peerConnection:(RTCPeerConnection *)peerConnection
290 iceGatheringChanged:(RTCICEGatheringState)newState {
291 NSLog(@"ICE gathering state changed: %d", newState);
292}
293
294- (void)peerConnection:(RTCPeerConnection *)peerConnection
295 gotICECandidate:(RTCICECandidate *)candidate {
296 dispatch_async(dispatch_get_main_queue(), ^{
297 ARDICECandidateMessage *message =
298 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
299 [self sendSignalingMessage:message];
300 });
301}
302
303- (void)peerConnection:(RTCPeerConnection*)peerConnection
304 didOpenDataChannel:(RTCDataChannel*)dataChannel {
305}
306
307#pragma mark - RTCSessionDescriptionDelegate
308
309- (void)peerConnection:(RTCPeerConnection *)peerConnection
310 didCreateSessionDescription:(RTCSessionDescription *)sdp
311 error:(NSError *)error {
312 dispatch_async(dispatch_get_main_queue(), ^{
313 if (error) {
314 NSLog(@"Failed to create session description. Error: %@", error);
315 [self disconnect];
316 NSDictionary *userInfo = @{
317 NSLocalizedDescriptionKey: @"Failed to create session description.",
318 };
319 NSError *sdpError =
320 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
321 code:kARDAppClientErrorCreateSDP
322 userInfo:userInfo];
323 [_delegate appClient:self didError:sdpError];
324 return;
325 }
326 [_peerConnection setLocalDescriptionWithDelegate:self
327 sessionDescription:sdp];
328 ARDSessionDescriptionMessage *message =
329 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
330 [self sendSignalingMessage:message];
331 });
332}
333
334- (void)peerConnection:(RTCPeerConnection *)peerConnection
335 didSetSessionDescriptionWithError:(NSError *)error {
336 dispatch_async(dispatch_get_main_queue(), ^{
337 if (error) {
338 NSLog(@"Failed to set session description. Error: %@", error);
339 [self disconnect];
340 NSDictionary *userInfo = @{
341 NSLocalizedDescriptionKey: @"Failed to set session description.",
342 };
343 NSError *sdpError =
344 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
345 code:kARDAppClientErrorSetSDP
346 userInfo:userInfo];
347 [_delegate appClient:self didError:sdpError];
348 return;
349 }
350 // If we're answering and we've just set the remote offer we need to create
351 // an answer and set the local description.
352 if (!_isInitiator && !_peerConnection.localDescription) {
353 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
354 [_peerConnection createAnswerWithDelegate:self
355 constraints:constraints];
356
357 }
358 });
359}
360
361#pragma mark - Private
362
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000363- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000364 return _clientId.length;
365}
366
367- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000368 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000369 return;
370 }
371 self.state = kARDAppClientStateConnected;
372
373 // Create peer connection.
374 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
375 _peerConnection = [_factory peerConnectionWithICEServers:_iceServers
376 constraints:constraints
377 delegate:self];
378 RTCMediaStream *localStream = [self createLocalMediaStream];
379 [_peerConnection addStream:localStream];
380 if (_isInitiator) {
381 [self sendOffer];
382 } else {
383 [self waitForAnswer];
384 }
385}
386
387- (void)sendOffer {
388 [_peerConnection createOfferWithDelegate:self
389 constraints:[self defaultOfferConstraints]];
390}
391
392- (void)waitForAnswer {
393 [self drainMessageQueueIfReady];
394}
395
396- (void)drainMessageQueueIfReady {
397 if (!_peerConnection || !_hasReceivedSdp) {
398 return;
399 }
400 for (ARDSignalingMessage *message in _messageQueue) {
401 [self processSignalingMessage:message];
402 }
403 [_messageQueue removeAllObjects];
404}
405
406- (void)processSignalingMessage:(ARDSignalingMessage *)message {
407 NSParameterAssert(_peerConnection ||
408 message.type == kARDSignalingMessageTypeBye);
409 switch (message.type) {
410 case kARDSignalingMessageTypeOffer:
411 case kARDSignalingMessageTypeAnswer: {
412 ARDSessionDescriptionMessage *sdpMessage =
413 (ARDSessionDescriptionMessage *)message;
414 RTCSessionDescription *description = sdpMessage.sessionDescription;
415 [_peerConnection setRemoteDescriptionWithDelegate:self
416 sessionDescription:description];
417 break;
418 }
419 case kARDSignalingMessageTypeCandidate: {
420 ARDICECandidateMessage *candidateMessage =
421 (ARDICECandidateMessage *)message;
422 [_peerConnection addICECandidate:candidateMessage.candidate];
423 break;
424 }
425 case kARDSignalingMessageTypeBye:
426 // Other client disconnected.
427 // TODO(tkchin): support waiting in room for next client. For now just
428 // disconnect.
429 [self disconnect];
430 break;
431 }
432}
433
434- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
435 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000436 __weak ARDAppClient *weakSelf = self;
437 [_roomServerClient sendMessage:message
438 forRoomId:_roomId
439 clientId:_clientId
440 completionHandler:^(ARDMessageResponse *response,
441 NSError *error) {
442 ARDAppClient *strongSelf = weakSelf;
443 if (error) {
444 [strongSelf.delegate appClient:strongSelf didError:error];
445 return;
446 }
447 NSError *messageError =
448 [[strongSelf class] errorForMessageResultType:response.result];
449 if (messageError) {
450 [strongSelf.delegate appClient:strongSelf didError:messageError];
451 return;
452 }
453 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000454 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000455 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000456 }
457}
458
459- (RTCMediaStream *)createLocalMediaStream {
460 RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
461 RTCVideoTrack* localVideoTrack = nil;
462
463 // The iOS simulator doesn't provide any sort of camera capture
464 // support or emulation (http://goo.gl/rHAnC1) so don't bother
465 // trying to open a local stream.
466 // TODO(tkchin): local video capture for OSX. See
467 // https://code.google.com/p/webrtc/issues/detail?id=3417.
468#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
469 NSString *cameraID = nil;
470 for (AVCaptureDevice *captureDevice in
471 [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
472 if (captureDevice.position == AVCaptureDevicePositionFront) {
473 cameraID = [captureDevice localizedName];
474 break;
475 }
476 }
477 NSAssert(cameraID, @"Unable to get the front camera id");
478
479 RTCVideoCapturer *capturer =
480 [RTCVideoCapturer capturerWithDeviceName:cameraID];
481 RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
482 RTCVideoSource *videoSource =
483 [_factory videoSourceWithCapturer:capturer
484 constraints:mediaConstraints];
485 localVideoTrack =
486 [_factory videoTrackWithID:@"ARDAMSv0" source:videoSource];
487 if (localVideoTrack) {
488 [localStream addVideoTrack:localVideoTrack];
489 }
490 [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
491#endif
492 [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
493 return localStream;
494}
495
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000496#pragma mark - Collider methods
497
498- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000499 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000500 return;
501 }
502 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000503 if (!_channel) {
504 _channel =
505 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
506 restURL:_websocketRestURL
507 delegate:self];
508 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000509 [_channel registerForRoomId:_roomId clientId:_clientId];
510}
511
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000512#pragma mark - Defaults
513
514- (RTCMediaConstraints *)defaultMediaStreamConstraints {
515 RTCMediaConstraints* constraints =
516 [[RTCMediaConstraints alloc]
517 initWithMandatoryConstraints:nil
518 optionalConstraints:nil];
519 return constraints;
520}
521
522- (RTCMediaConstraints *)defaultAnswerConstraints {
523 return [self defaultOfferConstraints];
524}
525
526- (RTCMediaConstraints *)defaultOfferConstraints {
527 NSArray *mandatoryConstraints = @[
528 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
529 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
530 ];
531 RTCMediaConstraints* constraints =
532 [[RTCMediaConstraints alloc]
533 initWithMandatoryConstraints:mandatoryConstraints
534 optionalConstraints:nil];
535 return constraints;
536}
537
538- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000539 if (_defaultPeerConnectionConstraints) {
540 return _defaultPeerConnectionConstraints;
541 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000542 NSArray *optionalConstraints = @[
543 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
544 ];
545 RTCMediaConstraints* constraints =
546 [[RTCMediaConstraints alloc]
547 initWithMandatoryConstraints:nil
548 optionalConstraints:optionalConstraints];
549 return constraints;
550}
551
552- (RTCICEServer *)defaultSTUNServer {
553 NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
554 return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
555 username:@""
556 password:@""];
557}
558
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000559#pragma mark - Errors
560
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000561+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000562 NSError *error = nil;
563 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000564 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000565 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000566 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000567 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
568 code:kARDAppClientErrorUnknown
569 userInfo:@{
570 NSLocalizedDescriptionKey: @"Unknown error.",
571 }];
572 break;
573 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000574 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000575 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
576 code:kARDAppClientErrorRoomFull
577 userInfo:@{
578 NSLocalizedDescriptionKey: @"Room is full.",
579 }];
580 break;
581 }
582 }
583 return error;
584}
585
586+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
587 NSError *error = nil;
588 switch (resultType) {
589 case kARDMessageResultTypeSuccess:
590 break;
591 case kARDMessageResultTypeUnknown:
592 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
593 code:kARDAppClientErrorUnknown
594 userInfo:@{
595 NSLocalizedDescriptionKey: @"Unknown error.",
596 }];
597 break;
598 case kARDMessageResultTypeInvalidClient:
599 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
600 code:kARDAppClientErrorInvalidClient
601 userInfo:@{
602 NSLocalizedDescriptionKey: @"Invalid client.",
603 }];
604 break;
605 case kARDMessageResultTypeInvalidRoom:
606 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
607 code:kARDAppClientErrorInvalidRoom
608 userInfo:@{
609 NSLocalizedDescriptionKey: @"Invalid room.",
610 }];
611 break;
612 }
613 return error;
614}
615
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000616@end