blob: e4c2f81b481ef16b8d2338e7c7294c538f2dcd18 [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
Zeke Chin57cc74e2015-05-05 07:52:31 -070030#if defined(WEBRTC_IOS)
31#import "RTCAVFoundationVideoSource.h"
32#endif
33#import "RTCICEServer.h"
34#import "RTCMediaConstraints.h"
35#import "RTCMediaStream.h"
36#import "RTCPair.h"
37#import "RTCVideoCapturer.h"
38#import "RTCAVFoundationVideoSource.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000039
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000040#import "ARDAppEngineClient.h"
41#import "ARDCEODTURNClient.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000042#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000043#import "ARDMessageResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000044#import "ARDSignalingMessage.h"
45#import "ARDUtilities.h"
46#import "ARDWebSocketChannel.h"
47#import "RTCICECandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000048#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070049
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000050
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000051static NSString * const kARDDefaultSTUNServerUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000052 @"stun:stun.l.google.com:19302";
53// TODO(tkchin): figure out a better username for CEOD statistics.
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000054static NSString * const kARDTurnRequestUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000055 @"https://computeengineondemand.appspot.com"
56 @"/turn?username=iapprtc&key=4080218913";
57
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000058static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
59static NSInteger const kARDAppClientErrorUnknown = -1;
60static NSInteger const kARDAppClientErrorRoomFull = -2;
61static NSInteger const kARDAppClientErrorCreateSDP = -3;
62static NSInteger const kARDAppClientErrorSetSDP = -4;
63static NSInteger const kARDAppClientErrorInvalidClient = -5;
64static NSInteger const kARDAppClientErrorInvalidRoom = -6;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000065
66@implementation ARDAppClient
67
68@synthesize delegate = _delegate;
69@synthesize state = _state;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000070@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000071@synthesize channel = _channel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000072@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000073@synthesize peerConnection = _peerConnection;
74@synthesize factory = _factory;
75@synthesize messageQueue = _messageQueue;
76@synthesize isTurnComplete = _isTurnComplete;
77@synthesize hasReceivedSdp = _hasReceivedSdp;
78@synthesize roomId = _roomId;
79@synthesize clientId = _clientId;
80@synthesize isInitiator = _isInitiator;
81@synthesize iceServers = _iceServers;
82@synthesize webSocketURL = _websocketURL;
83@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000084@synthesize defaultPeerConnectionConstraints =
85 _defaultPeerConnectionConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000086
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000087- (instancetype)init {
88 if (self = [super init]) {
89 _roomServerClient = [[ARDAppEngineClient alloc] init];
90 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
91 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
92 [self configure];
93 }
94 return self;
95}
96
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000097- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
98 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000099 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000100 _delegate = delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000101 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
102 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
103 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000104 }
105 return self;
106}
107
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000108// TODO(tkchin): Provide signaling channel factory interface so we can recreate
109// channel if we need to on network failure. Also, make this the default public
110// constructor.
111- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
112 signalingChannel:(id<ARDSignalingChannel>)channel
113 turnClient:(id<ARDTURNClient>)turnClient
114 delegate:(id<ARDAppClientDelegate>)delegate {
115 NSParameterAssert(rsClient);
116 NSParameterAssert(channel);
117 NSParameterAssert(turnClient);
118 if (self = [super init]) {
119 _roomServerClient = rsClient;
120 _channel = channel;
121 _turnClient = turnClient;
122 _delegate = delegate;
123 [self configure];
124 }
125 return self;
126}
127
128- (void)configure {
129 _factory = [[RTCPeerConnectionFactory alloc] init];
130 _messageQueue = [NSMutableArray array];
131 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
132}
133
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000134- (void)dealloc {
135 [self disconnect];
136}
137
138- (void)setState:(ARDAppClientState)state {
139 if (_state == state) {
140 return;
141 }
142 _state = state;
143 [_delegate appClient:self didChangeState:_state];
144}
145
146- (void)connectToRoomWithId:(NSString *)roomId
147 options:(NSDictionary *)options {
148 NSParameterAssert(roomId.length);
149 NSParameterAssert(_state == kARDAppClientStateDisconnected);
150 self.state = kARDAppClientStateConnecting;
151
152 // Request TURN.
153 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000154 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
155 NSError *error) {
156 if (error) {
157 NSLog(@"Error retrieving TURN servers: %@", error);
158 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000159 ARDAppClient *strongSelf = weakSelf;
160 [strongSelf.iceServers addObjectsFromArray:turnServers];
161 strongSelf.isTurnComplete = YES;
162 [strongSelf startSignalingIfReady];
163 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000164
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000165 // Join room on room server.
166 [_roomServerClient joinRoomWithRoomId:roomId
167 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000168 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000169 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000170 [strongSelf.delegate appClient:strongSelf didError:error];
171 return;
172 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000173 NSError *joinError =
174 [[strongSelf class] errorForJoinResultType:response.result];
175 if (joinError) {
176 NSLog(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000177 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000178 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000179 return;
180 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000181 NSLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000182 strongSelf.roomId = response.roomId;
183 strongSelf.clientId = response.clientId;
184 strongSelf.isInitiator = response.isInitiator;
185 for (ARDSignalingMessage *message in response.messages) {
186 if (message.type == kARDSignalingMessageTypeOffer ||
187 message.type == kARDSignalingMessageTypeAnswer) {
188 strongSelf.hasReceivedSdp = YES;
189 [strongSelf.messageQueue insertObject:message atIndex:0];
190 } else {
191 [strongSelf.messageQueue addObject:message];
192 }
193 }
194 strongSelf.webSocketURL = response.webSocketURL;
195 strongSelf.webSocketRestURL = response.webSocketRestURL;
196 [strongSelf registerWithColliderIfReady];
197 [strongSelf startSignalingIfReady];
198 }];
199}
200
201- (void)disconnect {
202 if (_state == kARDAppClientStateDisconnected) {
203 return;
204 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000205 if (self.hasJoinedRoomServerRoom) {
206 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000207 clientId:_clientId
208 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000209 }
210 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000211 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000212 // Tell the other client we're hanging up.
213 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000214 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000215 }
216 // Disconnect from collider.
217 _channel = nil;
218 }
219 _clientId = nil;
220 _roomId = nil;
221 _isInitiator = NO;
222 _hasReceivedSdp = NO;
223 _messageQueue = [NSMutableArray array];
224 _peerConnection = nil;
225 self.state = kARDAppClientStateDisconnected;
226}
227
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000228#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000229
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000230- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000231 didReceiveMessage:(ARDSignalingMessage *)message {
232 switch (message.type) {
233 case kARDSignalingMessageTypeOffer:
234 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000235 // Offers and answers must be processed before any other message, so we
236 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000237 _hasReceivedSdp = YES;
238 [_messageQueue insertObject:message atIndex:0];
239 break;
240 case kARDSignalingMessageTypeCandidate:
241 [_messageQueue addObject:message];
242 break;
243 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000244 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000245 [self processSignalingMessage:message];
246 return;
247 }
248 [self drainMessageQueueIfReady];
249}
250
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000251- (void)channel:(id<ARDSignalingChannel>)channel
252 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000253 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000254 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000255 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000256 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000257 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000258 case kARDSignalingChannelStateClosed:
259 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000260 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
261 // completely if the websocket connection fails.
262 [self disconnect];
263 break;
264 }
265}
266
267#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000268// Callbacks for this delegate occur on non-main thread and need to be
269// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000270
271- (void)peerConnection:(RTCPeerConnection *)peerConnection
272 signalingStateChanged:(RTCSignalingState)stateChanged {
273 NSLog(@"Signaling state changed: %d", stateChanged);
274}
275
276- (void)peerConnection:(RTCPeerConnection *)peerConnection
277 addedStream:(RTCMediaStream *)stream {
278 dispatch_async(dispatch_get_main_queue(), ^{
279 NSLog(@"Received %lu video tracks and %lu audio tracks",
280 (unsigned long)stream.videoTracks.count,
281 (unsigned long)stream.audioTracks.count);
282 if (stream.videoTracks.count) {
283 RTCVideoTrack *videoTrack = stream.videoTracks[0];
284 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
285 }
286 });
287}
288
289- (void)peerConnection:(RTCPeerConnection *)peerConnection
290 removedStream:(RTCMediaStream *)stream {
291 NSLog(@"Stream was removed.");
292}
293
294- (void)peerConnectionOnRenegotiationNeeded:
295 (RTCPeerConnection *)peerConnection {
296 NSLog(@"WARNING: Renegotiation needed but unimplemented.");
297}
298
299- (void)peerConnection:(RTCPeerConnection *)peerConnection
300 iceConnectionChanged:(RTCICEConnectionState)newState {
301 NSLog(@"ICE state changed: %d", newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000302 dispatch_async(dispatch_get_main_queue(), ^{
303 [_delegate appClient:self didChangeConnectionState:newState];
304 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000305}
306
307- (void)peerConnection:(RTCPeerConnection *)peerConnection
308 iceGatheringChanged:(RTCICEGatheringState)newState {
309 NSLog(@"ICE gathering state changed: %d", newState);
310}
311
312- (void)peerConnection:(RTCPeerConnection *)peerConnection
313 gotICECandidate:(RTCICECandidate *)candidate {
314 dispatch_async(dispatch_get_main_queue(), ^{
315 ARDICECandidateMessage *message =
316 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
317 [self sendSignalingMessage:message];
318 });
319}
320
321- (void)peerConnection:(RTCPeerConnection*)peerConnection
322 didOpenDataChannel:(RTCDataChannel*)dataChannel {
323}
324
325#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000326// Callbacks for this delegate occur on non-main thread and need to be
327// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000328
329- (void)peerConnection:(RTCPeerConnection *)peerConnection
330 didCreateSessionDescription:(RTCSessionDescription *)sdp
331 error:(NSError *)error {
332 dispatch_async(dispatch_get_main_queue(), ^{
333 if (error) {
334 NSLog(@"Failed to create session description. Error: %@", error);
335 [self disconnect];
336 NSDictionary *userInfo = @{
337 NSLocalizedDescriptionKey: @"Failed to create session description.",
338 };
339 NSError *sdpError =
340 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
341 code:kARDAppClientErrorCreateSDP
342 userInfo:userInfo];
343 [_delegate appClient:self didError:sdpError];
344 return;
345 }
346 [_peerConnection setLocalDescriptionWithDelegate:self
347 sessionDescription:sdp];
348 ARDSessionDescriptionMessage *message =
349 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
350 [self sendSignalingMessage:message];
351 });
352}
353
354- (void)peerConnection:(RTCPeerConnection *)peerConnection
355 didSetSessionDescriptionWithError:(NSError *)error {
356 dispatch_async(dispatch_get_main_queue(), ^{
357 if (error) {
358 NSLog(@"Failed to set session description. Error: %@", error);
359 [self disconnect];
360 NSDictionary *userInfo = @{
361 NSLocalizedDescriptionKey: @"Failed to set session description.",
362 };
363 NSError *sdpError =
364 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
365 code:kARDAppClientErrorSetSDP
366 userInfo:userInfo];
367 [_delegate appClient:self didError:sdpError];
368 return;
369 }
370 // If we're answering and we've just set the remote offer we need to create
371 // an answer and set the local description.
372 if (!_isInitiator && !_peerConnection.localDescription) {
373 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
374 [_peerConnection createAnswerWithDelegate:self
375 constraints:constraints];
376
377 }
378 });
379}
380
381#pragma mark - Private
382
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000383- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000384 return _clientId.length;
385}
386
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000387// Begins the peer connection connection process if we have both joined a room
388// on the room server and tried to obtain a TURN server. Otherwise does nothing.
389// A peer connection object will be created with a stream that contains local
390// audio and video capture. If this client is the caller, an offer is created as
391// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000392- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000393 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000394 return;
395 }
396 self.state = kARDAppClientStateConnected;
397
398 // Create peer connection.
399 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
400 _peerConnection = [_factory peerConnectionWithICEServers:_iceServers
401 constraints:constraints
402 delegate:self];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000403 // Create AV media stream and add it to the peer connection.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000404 RTCMediaStream *localStream = [self createLocalMediaStream];
405 [_peerConnection addStream:localStream];
406 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000407 // Send offer.
408 [_peerConnection createOfferWithDelegate:self
409 constraints:[self defaultOfferConstraints]];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000410 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000411 // Check if we've received an offer.
412 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000413 }
414}
415
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000416// Processes the messages that we've received from the room server and the
417// signaling channel. The offer or answer message must be processed before other
418// signaling messages, however they can arrive out of order. Hence, this method
419// only processes pending messages if there is a peer connection object and
420// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000421- (void)drainMessageQueueIfReady {
422 if (!_peerConnection || !_hasReceivedSdp) {
423 return;
424 }
425 for (ARDSignalingMessage *message in _messageQueue) {
426 [self processSignalingMessage:message];
427 }
428 [_messageQueue removeAllObjects];
429}
430
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000431// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000432- (void)processSignalingMessage:(ARDSignalingMessage *)message {
433 NSParameterAssert(_peerConnection ||
434 message.type == kARDSignalingMessageTypeBye);
435 switch (message.type) {
436 case kARDSignalingMessageTypeOffer:
437 case kARDSignalingMessageTypeAnswer: {
438 ARDSessionDescriptionMessage *sdpMessage =
439 (ARDSessionDescriptionMessage *)message;
440 RTCSessionDescription *description = sdpMessage.sessionDescription;
441 [_peerConnection setRemoteDescriptionWithDelegate:self
442 sessionDescription:description];
443 break;
444 }
445 case kARDSignalingMessageTypeCandidate: {
446 ARDICECandidateMessage *candidateMessage =
447 (ARDICECandidateMessage *)message;
448 [_peerConnection addICECandidate:candidateMessage.candidate];
449 break;
450 }
451 case kARDSignalingMessageTypeBye:
452 // Other client disconnected.
453 // TODO(tkchin): support waiting in room for next client. For now just
454 // disconnect.
455 [self disconnect];
456 break;
457 }
458}
459
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000460// Sends a signaling message to the other client. The caller will send messages
461// through the room server, whereas the callee will send messages over the
462// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000463- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
464 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000465 __weak ARDAppClient *weakSelf = self;
466 [_roomServerClient sendMessage:message
467 forRoomId:_roomId
468 clientId:_clientId
469 completionHandler:^(ARDMessageResponse *response,
470 NSError *error) {
471 ARDAppClient *strongSelf = weakSelf;
472 if (error) {
473 [strongSelf.delegate appClient:strongSelf didError:error];
474 return;
475 }
476 NSError *messageError =
477 [[strongSelf class] errorForMessageResultType:response.result];
478 if (messageError) {
479 [strongSelf.delegate appClient:strongSelf didError:messageError];
480 return;
481 }
482 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000483 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000484 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000485 }
486}
487
488- (RTCMediaStream *)createLocalMediaStream {
489 RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700490 RTCVideoTrack* localVideoTrack = [self createLocalVideoTrack];
491 if (localVideoTrack) {
492 [localStream addVideoTrack:localVideoTrack];
493 [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
494 }
495 [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
496 return localStream;
497}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000498
Zeke Chin57cc74e2015-05-05 07:52:31 -0700499- (RTCVideoTrack *)createLocalVideoTrack {
500 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000501 // The iOS simulator doesn't provide any sort of camera capture
502 // support or emulation (http://goo.gl/rHAnC1) so don't bother
503 // trying to open a local stream.
504 // TODO(tkchin): local video capture for OSX. See
505 // https://code.google.com/p/webrtc/issues/detail?id=3417.
506#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000507 RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700508 RTCAVFoundationVideoSource *source =
509 [[RTCAVFoundationVideoSource alloc] initWithFactory:_factory
510 constraints:mediaConstraints];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000511 localVideoTrack =
Zeke Chin57cc74e2015-05-05 07:52:31 -0700512 [[RTCVideoTrack alloc] initWithFactory:_factory
513 source:source
514 trackId:@"ARDAMSv0"];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000515#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700516 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000517}
518
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000519#pragma mark - Collider methods
520
521- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000522 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000523 return;
524 }
525 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000526 if (!_channel) {
527 _channel =
528 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
529 restURL:_websocketRestURL
530 delegate:self];
531 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000532 [_channel registerForRoomId:_roomId clientId:_clientId];
533}
534
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000535#pragma mark - Defaults
536
537- (RTCMediaConstraints *)defaultMediaStreamConstraints {
538 RTCMediaConstraints* constraints =
539 [[RTCMediaConstraints alloc]
540 initWithMandatoryConstraints:nil
541 optionalConstraints:nil];
542 return constraints;
543}
544
545- (RTCMediaConstraints *)defaultAnswerConstraints {
546 return [self defaultOfferConstraints];
547}
548
549- (RTCMediaConstraints *)defaultOfferConstraints {
550 NSArray *mandatoryConstraints = @[
551 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
552 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
553 ];
554 RTCMediaConstraints* constraints =
555 [[RTCMediaConstraints alloc]
556 initWithMandatoryConstraints:mandatoryConstraints
557 optionalConstraints:nil];
558 return constraints;
559}
560
561- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000562 if (_defaultPeerConnectionConstraints) {
563 return _defaultPeerConnectionConstraints;
564 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000565 NSArray *optionalConstraints = @[
566 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
567 ];
568 RTCMediaConstraints* constraints =
569 [[RTCMediaConstraints alloc]
570 initWithMandatoryConstraints:nil
571 optionalConstraints:optionalConstraints];
572 return constraints;
573}
574
575- (RTCICEServer *)defaultSTUNServer {
576 NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
577 return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
578 username:@""
579 password:@""];
580}
581
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000582#pragma mark - Errors
583
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000584+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000585 NSError *error = nil;
586 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000587 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000588 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000589 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000590 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
591 code:kARDAppClientErrorUnknown
592 userInfo:@{
593 NSLocalizedDescriptionKey: @"Unknown error.",
594 }];
595 break;
596 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000597 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000598 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
599 code:kARDAppClientErrorRoomFull
600 userInfo:@{
601 NSLocalizedDescriptionKey: @"Room is full.",
602 }];
603 break;
604 }
605 }
606 return error;
607}
608
609+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
610 NSError *error = nil;
611 switch (resultType) {
612 case kARDMessageResultTypeSuccess:
613 break;
614 case kARDMessageResultTypeUnknown:
615 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
616 code:kARDAppClientErrorUnknown
617 userInfo:@{
618 NSLocalizedDescriptionKey: @"Unknown error.",
619 }];
620 break;
621 case kARDMessageResultTypeInvalidClient:
622 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
623 code:kARDAppClientErrorInvalidClient
624 userInfo:@{
625 NSLocalizedDescriptionKey: @"Invalid client.",
626 }];
627 break;
628 case kARDMessageResultTypeInvalidRoom:
629 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
630 code:kARDAppClientErrorInvalidRoom
631 userInfo:@{
632 NSLocalizedDescriptionKey: @"Invalid room.",
633 }];
634 break;
635 }
636 return error;
637}
638
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000639@end