blob: 0f3c423c42cf9100681b2a9eb6faa34187b8258a [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"
Zeke Chinbc7dd7e2015-05-29 14:59:14 -070037#import "RTCPeerConnectionInterface.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070038#import "RTCVideoCapturer.h"
39#import "RTCAVFoundationVideoSource.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000040
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000041#import "ARDAppEngineClient.h"
42#import "ARDCEODTURNClient.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000043#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000044#import "ARDMessageResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000045#import "ARDSignalingMessage.h"
46#import "ARDUtilities.h"
47#import "ARDWebSocketChannel.h"
48#import "RTCICECandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000049#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070050
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000051
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000052static NSString * const kARDDefaultSTUNServerUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000053 @"stun:stun.l.google.com:19302";
54// TODO(tkchin): figure out a better username for CEOD statistics.
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000055static NSString * const kARDTurnRequestUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000056 @"https://computeengineondemand.appspot.com"
57 @"/turn?username=iapprtc&key=4080218913";
58
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000059static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
60static NSInteger const kARDAppClientErrorUnknown = -1;
61static NSInteger const kARDAppClientErrorRoomFull = -2;
62static NSInteger const kARDAppClientErrorCreateSDP = -3;
63static NSInteger const kARDAppClientErrorSetSDP = -4;
64static NSInteger const kARDAppClientErrorInvalidClient = -5;
65static NSInteger const kARDAppClientErrorInvalidRoom = -6;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000066
67@implementation ARDAppClient
68
69@synthesize delegate = _delegate;
70@synthesize state = _state;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000071@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000072@synthesize channel = _channel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000073@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000074@synthesize peerConnection = _peerConnection;
75@synthesize factory = _factory;
76@synthesize messageQueue = _messageQueue;
77@synthesize isTurnComplete = _isTurnComplete;
78@synthesize hasReceivedSdp = _hasReceivedSdp;
79@synthesize roomId = _roomId;
80@synthesize clientId = _clientId;
81@synthesize isInitiator = _isInitiator;
82@synthesize iceServers = _iceServers;
83@synthesize webSocketURL = _websocketURL;
84@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000085@synthesize defaultPeerConnectionConstraints =
86 _defaultPeerConnectionConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000087
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000088- (instancetype)init {
89 if (self = [super init]) {
90 _roomServerClient = [[ARDAppEngineClient alloc] init];
91 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
92 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
93 [self configure];
94 }
95 return self;
96}
97
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000098- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
99 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000100 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000101 _delegate = delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000102 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
103 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
104 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000105 }
106 return self;
107}
108
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000109// TODO(tkchin): Provide signaling channel factory interface so we can recreate
110// channel if we need to on network failure. Also, make this the default public
111// constructor.
112- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
113 signalingChannel:(id<ARDSignalingChannel>)channel
114 turnClient:(id<ARDTURNClient>)turnClient
115 delegate:(id<ARDAppClientDelegate>)delegate {
116 NSParameterAssert(rsClient);
117 NSParameterAssert(channel);
118 NSParameterAssert(turnClient);
119 if (self = [super init]) {
120 _roomServerClient = rsClient;
121 _channel = channel;
122 _turnClient = turnClient;
123 _delegate = delegate;
124 [self configure];
125 }
126 return self;
127}
128
129- (void)configure {
130 _factory = [[RTCPeerConnectionFactory alloc] init];
131 _messageQueue = [NSMutableArray array];
132 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
133}
134
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000135- (void)dealloc {
136 [self disconnect];
137}
138
139- (void)setState:(ARDAppClientState)state {
140 if (_state == state) {
141 return;
142 }
143 _state = state;
144 [_delegate appClient:self didChangeState:_state];
145}
146
147- (void)connectToRoomWithId:(NSString *)roomId
148 options:(NSDictionary *)options {
149 NSParameterAssert(roomId.length);
150 NSParameterAssert(_state == kARDAppClientStateDisconnected);
151 self.state = kARDAppClientStateConnecting;
152
153 // Request TURN.
154 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000155 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
156 NSError *error) {
157 if (error) {
158 NSLog(@"Error retrieving TURN servers: %@", error);
159 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000160 ARDAppClient *strongSelf = weakSelf;
161 [strongSelf.iceServers addObjectsFromArray:turnServers];
162 strongSelf.isTurnComplete = YES;
163 [strongSelf startSignalingIfReady];
164 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000165
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000166 // Join room on room server.
167 [_roomServerClient joinRoomWithRoomId:roomId
168 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000169 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000170 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000171 [strongSelf.delegate appClient:strongSelf didError:error];
172 return;
173 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000174 NSError *joinError =
175 [[strongSelf class] errorForJoinResultType:response.result];
176 if (joinError) {
177 NSLog(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000178 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000179 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000180 return;
181 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000182 NSLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000183 strongSelf.roomId = response.roomId;
184 strongSelf.clientId = response.clientId;
185 strongSelf.isInitiator = response.isInitiator;
186 for (ARDSignalingMessage *message in response.messages) {
187 if (message.type == kARDSignalingMessageTypeOffer ||
188 message.type == kARDSignalingMessageTypeAnswer) {
189 strongSelf.hasReceivedSdp = YES;
190 [strongSelf.messageQueue insertObject:message atIndex:0];
191 } else {
192 [strongSelf.messageQueue addObject:message];
193 }
194 }
195 strongSelf.webSocketURL = response.webSocketURL;
196 strongSelf.webSocketRestURL = response.webSocketRestURL;
197 [strongSelf registerWithColliderIfReady];
198 [strongSelf startSignalingIfReady];
199 }];
200}
201
202- (void)disconnect {
203 if (_state == kARDAppClientStateDisconnected) {
204 return;
205 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000206 if (self.hasJoinedRoomServerRoom) {
207 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000208 clientId:_clientId
209 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000210 }
211 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000212 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000213 // Tell the other client we're hanging up.
214 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000215 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000216 }
217 // Disconnect from collider.
218 _channel = nil;
219 }
220 _clientId = nil;
221 _roomId = nil;
222 _isInitiator = NO;
223 _hasReceivedSdp = NO;
224 _messageQueue = [NSMutableArray array];
225 _peerConnection = nil;
226 self.state = kARDAppClientStateDisconnected;
227}
228
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000229#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000230
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000231- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000232 didReceiveMessage:(ARDSignalingMessage *)message {
233 switch (message.type) {
234 case kARDSignalingMessageTypeOffer:
235 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000236 // Offers and answers must be processed before any other message, so we
237 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000238 _hasReceivedSdp = YES;
239 [_messageQueue insertObject:message atIndex:0];
240 break;
241 case kARDSignalingMessageTypeCandidate:
242 [_messageQueue addObject:message];
243 break;
244 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000245 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000246 [self processSignalingMessage:message];
247 return;
248 }
249 [self drainMessageQueueIfReady];
250}
251
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000252- (void)channel:(id<ARDSignalingChannel>)channel
253 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000254 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000255 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000256 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000257 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000258 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000259 case kARDSignalingChannelStateClosed:
260 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000261 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
262 // completely if the websocket connection fails.
263 [self disconnect];
264 break;
265 }
266}
267
268#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000269// Callbacks for this delegate occur on non-main thread and need to be
270// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000271
272- (void)peerConnection:(RTCPeerConnection *)peerConnection
273 signalingStateChanged:(RTCSignalingState)stateChanged {
274 NSLog(@"Signaling state changed: %d", stateChanged);
275}
276
277- (void)peerConnection:(RTCPeerConnection *)peerConnection
278 addedStream:(RTCMediaStream *)stream {
279 dispatch_async(dispatch_get_main_queue(), ^{
280 NSLog(@"Received %lu video tracks and %lu audio tracks",
281 (unsigned long)stream.videoTracks.count,
282 (unsigned long)stream.audioTracks.count);
283 if (stream.videoTracks.count) {
284 RTCVideoTrack *videoTrack = stream.videoTracks[0];
285 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
286 }
287 });
288}
289
290- (void)peerConnection:(RTCPeerConnection *)peerConnection
291 removedStream:(RTCMediaStream *)stream {
292 NSLog(@"Stream was removed.");
293}
294
295- (void)peerConnectionOnRenegotiationNeeded:
296 (RTCPeerConnection *)peerConnection {
297 NSLog(@"WARNING: Renegotiation needed but unimplemented.");
298}
299
300- (void)peerConnection:(RTCPeerConnection *)peerConnection
301 iceConnectionChanged:(RTCICEConnectionState)newState {
302 NSLog(@"ICE state changed: %d", newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000303 dispatch_async(dispatch_get_main_queue(), ^{
304 [_delegate appClient:self didChangeConnectionState:newState];
305 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000306}
307
308- (void)peerConnection:(RTCPeerConnection *)peerConnection
309 iceGatheringChanged:(RTCICEGatheringState)newState {
310 NSLog(@"ICE gathering state changed: %d", newState);
311}
312
313- (void)peerConnection:(RTCPeerConnection *)peerConnection
314 gotICECandidate:(RTCICECandidate *)candidate {
315 dispatch_async(dispatch_get_main_queue(), ^{
316 ARDICECandidateMessage *message =
317 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
318 [self sendSignalingMessage:message];
319 });
320}
321
322- (void)peerConnection:(RTCPeerConnection*)peerConnection
323 didOpenDataChannel:(RTCDataChannel*)dataChannel {
324}
325
326#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000327// Callbacks for this delegate occur on non-main thread and need to be
328// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000329
330- (void)peerConnection:(RTCPeerConnection *)peerConnection
331 didCreateSessionDescription:(RTCSessionDescription *)sdp
332 error:(NSError *)error {
333 dispatch_async(dispatch_get_main_queue(), ^{
334 if (error) {
335 NSLog(@"Failed to create session description. Error: %@", error);
336 [self disconnect];
337 NSDictionary *userInfo = @{
338 NSLocalizedDescriptionKey: @"Failed to create session description.",
339 };
340 NSError *sdpError =
341 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
342 code:kARDAppClientErrorCreateSDP
343 userInfo:userInfo];
344 [_delegate appClient:self didError:sdpError];
345 return;
346 }
347 [_peerConnection setLocalDescriptionWithDelegate:self
348 sessionDescription:sdp];
349 ARDSessionDescriptionMessage *message =
350 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
351 [self sendSignalingMessage:message];
352 });
353}
354
355- (void)peerConnection:(RTCPeerConnection *)peerConnection
356 didSetSessionDescriptionWithError:(NSError *)error {
357 dispatch_async(dispatch_get_main_queue(), ^{
358 if (error) {
359 NSLog(@"Failed to set session description. Error: %@", error);
360 [self disconnect];
361 NSDictionary *userInfo = @{
362 NSLocalizedDescriptionKey: @"Failed to set session description.",
363 };
364 NSError *sdpError =
365 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
366 code:kARDAppClientErrorSetSDP
367 userInfo:userInfo];
368 [_delegate appClient:self didError:sdpError];
369 return;
370 }
371 // If we're answering and we've just set the remote offer we need to create
372 // an answer and set the local description.
373 if (!_isInitiator && !_peerConnection.localDescription) {
374 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
375 [_peerConnection createAnswerWithDelegate:self
376 constraints:constraints];
377
378 }
379 });
380}
381
382#pragma mark - Private
383
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000384- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000385 return _clientId.length;
386}
387
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000388// Begins the peer connection connection process if we have both joined a room
389// on the room server and tried to obtain a TURN server. Otherwise does nothing.
390// A peer connection object will be created with a stream that contains local
391// audio and video capture. If this client is the caller, an offer is created as
392// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000393- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000394 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000395 return;
396 }
397 self.state = kARDAppClientStateConnected;
398
399 // Create peer connection.
400 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700401 RTCConfiguration *config = [[RTCConfiguration alloc] init];
402 config.iceServers = _iceServers;
403 _peerConnection = [_factory peerConnectionWithConfiguration:config
404 constraints:constraints
405 delegate:self];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000406 // Create AV media stream and add it to the peer connection.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000407 RTCMediaStream *localStream = [self createLocalMediaStream];
408 [_peerConnection addStream:localStream];
409 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000410 // Send offer.
411 [_peerConnection createOfferWithDelegate:self
412 constraints:[self defaultOfferConstraints]];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000413 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000414 // Check if we've received an offer.
415 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000416 }
417}
418
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000419// Processes the messages that we've received from the room server and the
420// signaling channel. The offer or answer message must be processed before other
421// signaling messages, however they can arrive out of order. Hence, this method
422// only processes pending messages if there is a peer connection object and
423// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000424- (void)drainMessageQueueIfReady {
425 if (!_peerConnection || !_hasReceivedSdp) {
426 return;
427 }
428 for (ARDSignalingMessage *message in _messageQueue) {
429 [self processSignalingMessage:message];
430 }
431 [_messageQueue removeAllObjects];
432}
433
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000434// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000435- (void)processSignalingMessage:(ARDSignalingMessage *)message {
436 NSParameterAssert(_peerConnection ||
437 message.type == kARDSignalingMessageTypeBye);
438 switch (message.type) {
439 case kARDSignalingMessageTypeOffer:
440 case kARDSignalingMessageTypeAnswer: {
441 ARDSessionDescriptionMessage *sdpMessage =
442 (ARDSessionDescriptionMessage *)message;
443 RTCSessionDescription *description = sdpMessage.sessionDescription;
444 [_peerConnection setRemoteDescriptionWithDelegate:self
445 sessionDescription:description];
446 break;
447 }
448 case kARDSignalingMessageTypeCandidate: {
449 ARDICECandidateMessage *candidateMessage =
450 (ARDICECandidateMessage *)message;
451 [_peerConnection addICECandidate:candidateMessage.candidate];
452 break;
453 }
454 case kARDSignalingMessageTypeBye:
455 // Other client disconnected.
456 // TODO(tkchin): support waiting in room for next client. For now just
457 // disconnect.
458 [self disconnect];
459 break;
460 }
461}
462
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000463// Sends a signaling message to the other client. The caller will send messages
464// through the room server, whereas the callee will send messages over the
465// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000466- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
467 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000468 __weak ARDAppClient *weakSelf = self;
469 [_roomServerClient sendMessage:message
470 forRoomId:_roomId
471 clientId:_clientId
472 completionHandler:^(ARDMessageResponse *response,
473 NSError *error) {
474 ARDAppClient *strongSelf = weakSelf;
475 if (error) {
476 [strongSelf.delegate appClient:strongSelf didError:error];
477 return;
478 }
479 NSError *messageError =
480 [[strongSelf class] errorForMessageResultType:response.result];
481 if (messageError) {
482 [strongSelf.delegate appClient:strongSelf didError:messageError];
483 return;
484 }
485 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000486 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000487 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000488 }
489}
490
491- (RTCMediaStream *)createLocalMediaStream {
492 RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700493 RTCVideoTrack* localVideoTrack = [self createLocalVideoTrack];
494 if (localVideoTrack) {
495 [localStream addVideoTrack:localVideoTrack];
496 [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
497 }
498 [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
499 return localStream;
500}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000501
Zeke Chin57cc74e2015-05-05 07:52:31 -0700502- (RTCVideoTrack *)createLocalVideoTrack {
503 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000504 // The iOS simulator doesn't provide any sort of camera capture
505 // support or emulation (http://goo.gl/rHAnC1) so don't bother
506 // trying to open a local stream.
507 // TODO(tkchin): local video capture for OSX. See
508 // https://code.google.com/p/webrtc/issues/detail?id=3417.
509#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000510 RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700511 RTCAVFoundationVideoSource *source =
512 [[RTCAVFoundationVideoSource alloc] initWithFactory:_factory
513 constraints:mediaConstraints];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000514 localVideoTrack =
Zeke Chin57cc74e2015-05-05 07:52:31 -0700515 [[RTCVideoTrack alloc] initWithFactory:_factory
516 source:source
517 trackId:@"ARDAMSv0"];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000518#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700519 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000520}
521
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000522#pragma mark - Collider methods
523
524- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000525 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000526 return;
527 }
528 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000529 if (!_channel) {
530 _channel =
531 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
532 restURL:_websocketRestURL
533 delegate:self];
534 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000535 [_channel registerForRoomId:_roomId clientId:_clientId];
536}
537
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000538#pragma mark - Defaults
539
540- (RTCMediaConstraints *)defaultMediaStreamConstraints {
541 RTCMediaConstraints* constraints =
542 [[RTCMediaConstraints alloc]
543 initWithMandatoryConstraints:nil
544 optionalConstraints:nil];
545 return constraints;
546}
547
548- (RTCMediaConstraints *)defaultAnswerConstraints {
549 return [self defaultOfferConstraints];
550}
551
552- (RTCMediaConstraints *)defaultOfferConstraints {
553 NSArray *mandatoryConstraints = @[
554 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
555 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
556 ];
557 RTCMediaConstraints* constraints =
558 [[RTCMediaConstraints alloc]
559 initWithMandatoryConstraints:mandatoryConstraints
560 optionalConstraints:nil];
561 return constraints;
562}
563
564- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000565 if (_defaultPeerConnectionConstraints) {
566 return _defaultPeerConnectionConstraints;
567 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000568 NSArray *optionalConstraints = @[
569 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
570 ];
571 RTCMediaConstraints* constraints =
572 [[RTCMediaConstraints alloc]
573 initWithMandatoryConstraints:nil
574 optionalConstraints:optionalConstraints];
575 return constraints;
576}
577
578- (RTCICEServer *)defaultSTUNServer {
579 NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
580 return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
581 username:@""
582 password:@""];
583}
584
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000585#pragma mark - Errors
586
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000587+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000588 NSError *error = nil;
589 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000590 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000591 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000592 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000593 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
594 code:kARDAppClientErrorUnknown
595 userInfo:@{
596 NSLocalizedDescriptionKey: @"Unknown error.",
597 }];
598 break;
599 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000600 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000601 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
602 code:kARDAppClientErrorRoomFull
603 userInfo:@{
604 NSLocalizedDescriptionKey: @"Room is full.",
605 }];
606 break;
607 }
608 }
609 return error;
610}
611
612+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
613 NSError *error = nil;
614 switch (resultType) {
615 case kARDMessageResultTypeSuccess:
616 break;
617 case kARDMessageResultTypeUnknown:
618 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
619 code:kARDAppClientErrorUnknown
620 userInfo:@{
621 NSLocalizedDescriptionKey: @"Unknown error.",
622 }];
623 break;
624 case kARDMessageResultTypeInvalidClient:
625 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
626 code:kARDAppClientErrorInvalidClient
627 userInfo:@{
628 NSLocalizedDescriptionKey: @"Invalid client.",
629 }];
630 break;
631 case kARDMessageResultTypeInvalidRoom:
632 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
633 code:kARDAppClientErrorInvalidRoom
634 userInfo:@{
635 NSLocalizedDescriptionKey: @"Invalid room.",
636 }];
637 break;
638 }
639 return error;
640}
641
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000642@end