blob: ac99ca29971d1b8f3c028786bc5c872fbc6549c2 [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"
Zeke Chin71f6f442015-06-29 14:34:58 -070045#import "ARDSDPUtils.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000046#import "ARDSignalingMessage.h"
47#import "ARDUtilities.h"
48#import "ARDWebSocketChannel.h"
49#import "RTCICECandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000050#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070051
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000052
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000053static NSString * const kARDDefaultSTUNServerUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000054 @"stun:stun.l.google.com:19302";
55// TODO(tkchin): figure out a better username for CEOD statistics.
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000056static NSString * const kARDTurnRequestUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000057 @"https://computeengineondemand.appspot.com"
58 @"/turn?username=iapprtc&key=4080218913";
59
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000060static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
61static NSInteger const kARDAppClientErrorUnknown = -1;
62static NSInteger const kARDAppClientErrorRoomFull = -2;
63static NSInteger const kARDAppClientErrorCreateSDP = -3;
64static NSInteger const kARDAppClientErrorSetSDP = -4;
65static NSInteger const kARDAppClientErrorInvalidClient = -5;
66static NSInteger const kARDAppClientErrorInvalidRoom = -6;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000067
68@implementation ARDAppClient
69
70@synthesize delegate = _delegate;
71@synthesize state = _state;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000072@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000073@synthesize channel = _channel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000074@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000075@synthesize peerConnection = _peerConnection;
76@synthesize factory = _factory;
77@synthesize messageQueue = _messageQueue;
78@synthesize isTurnComplete = _isTurnComplete;
79@synthesize hasReceivedSdp = _hasReceivedSdp;
80@synthesize roomId = _roomId;
81@synthesize clientId = _clientId;
82@synthesize isInitiator = _isInitiator;
83@synthesize iceServers = _iceServers;
84@synthesize webSocketURL = _websocketURL;
85@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000086@synthesize defaultPeerConnectionConstraints =
87 _defaultPeerConnectionConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000088
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000089- (instancetype)init {
90 if (self = [super init]) {
91 _roomServerClient = [[ARDAppEngineClient alloc] init];
92 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
93 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
94 [self configure];
95 }
96 return self;
97}
98
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000099- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
100 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000101 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000102 _delegate = delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000103 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
104 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
105 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000106 }
107 return self;
108}
109
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000110// TODO(tkchin): Provide signaling channel factory interface so we can recreate
111// channel if we need to on network failure. Also, make this the default public
112// constructor.
113- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
114 signalingChannel:(id<ARDSignalingChannel>)channel
115 turnClient:(id<ARDTURNClient>)turnClient
116 delegate:(id<ARDAppClientDelegate>)delegate {
117 NSParameterAssert(rsClient);
118 NSParameterAssert(channel);
119 NSParameterAssert(turnClient);
120 if (self = [super init]) {
121 _roomServerClient = rsClient;
122 _channel = channel;
123 _turnClient = turnClient;
124 _delegate = delegate;
125 [self configure];
126 }
127 return self;
128}
129
130- (void)configure {
131 _factory = [[RTCPeerConnectionFactory alloc] init];
132 _messageQueue = [NSMutableArray array];
133 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
134}
135
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000136- (void)dealloc {
137 [self disconnect];
138}
139
140- (void)setState:(ARDAppClientState)state {
141 if (_state == state) {
142 return;
143 }
144 _state = state;
145 [_delegate appClient:self didChangeState:_state];
146}
147
148- (void)connectToRoomWithId:(NSString *)roomId
149 options:(NSDictionary *)options {
150 NSParameterAssert(roomId.length);
151 NSParameterAssert(_state == kARDAppClientStateDisconnected);
152 self.state = kARDAppClientStateConnecting;
153
154 // Request TURN.
155 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000156 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
157 NSError *error) {
158 if (error) {
159 NSLog(@"Error retrieving TURN servers: %@", error);
160 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000161 ARDAppClient *strongSelf = weakSelf;
162 [strongSelf.iceServers addObjectsFromArray:turnServers];
163 strongSelf.isTurnComplete = YES;
164 [strongSelf startSignalingIfReady];
165 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000166
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000167 // Join room on room server.
168 [_roomServerClient joinRoomWithRoomId:roomId
169 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000170 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000171 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000172 [strongSelf.delegate appClient:strongSelf didError:error];
173 return;
174 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000175 NSError *joinError =
176 [[strongSelf class] errorForJoinResultType:response.result];
177 if (joinError) {
178 NSLog(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000179 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000180 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000181 return;
182 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000183 NSLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000184 strongSelf.roomId = response.roomId;
185 strongSelf.clientId = response.clientId;
186 strongSelf.isInitiator = response.isInitiator;
187 for (ARDSignalingMessage *message in response.messages) {
188 if (message.type == kARDSignalingMessageTypeOffer ||
189 message.type == kARDSignalingMessageTypeAnswer) {
190 strongSelf.hasReceivedSdp = YES;
191 [strongSelf.messageQueue insertObject:message atIndex:0];
192 } else {
193 [strongSelf.messageQueue addObject:message];
194 }
195 }
196 strongSelf.webSocketURL = response.webSocketURL;
197 strongSelf.webSocketRestURL = response.webSocketRestURL;
198 [strongSelf registerWithColliderIfReady];
199 [strongSelf startSignalingIfReady];
200 }];
201}
202
203- (void)disconnect {
204 if (_state == kARDAppClientStateDisconnected) {
205 return;
206 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000207 if (self.hasJoinedRoomServerRoom) {
208 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000209 clientId:_clientId
210 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000211 }
212 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000213 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000214 // Tell the other client we're hanging up.
215 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000216 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000217 }
218 // Disconnect from collider.
219 _channel = nil;
220 }
221 _clientId = nil;
222 _roomId = nil;
223 _isInitiator = NO;
224 _hasReceivedSdp = NO;
225 _messageQueue = [NSMutableArray array];
226 _peerConnection = nil;
227 self.state = kARDAppClientStateDisconnected;
228}
229
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000230#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000231
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000232- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000233 didReceiveMessage:(ARDSignalingMessage *)message {
234 switch (message.type) {
235 case kARDSignalingMessageTypeOffer:
236 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000237 // Offers and answers must be processed before any other message, so we
238 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000239 _hasReceivedSdp = YES;
240 [_messageQueue insertObject:message atIndex:0];
241 break;
242 case kARDSignalingMessageTypeCandidate:
243 [_messageQueue addObject:message];
244 break;
245 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000246 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000247 [self processSignalingMessage:message];
248 return;
249 }
250 [self drainMessageQueueIfReady];
251}
252
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000253- (void)channel:(id<ARDSignalingChannel>)channel
254 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000255 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000256 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000257 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000258 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000259 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000260 case kARDSignalingChannelStateClosed:
261 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000262 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
263 // completely if the websocket connection fails.
264 [self disconnect];
265 break;
266 }
267}
268
269#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000270// Callbacks for this delegate occur on non-main thread and need to be
271// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000272
273- (void)peerConnection:(RTCPeerConnection *)peerConnection
274 signalingStateChanged:(RTCSignalingState)stateChanged {
275 NSLog(@"Signaling state changed: %d", stateChanged);
276}
277
278- (void)peerConnection:(RTCPeerConnection *)peerConnection
279 addedStream:(RTCMediaStream *)stream {
280 dispatch_async(dispatch_get_main_queue(), ^{
281 NSLog(@"Received %lu video tracks and %lu audio tracks",
282 (unsigned long)stream.videoTracks.count,
283 (unsigned long)stream.audioTracks.count);
284 if (stream.videoTracks.count) {
285 RTCVideoTrack *videoTrack = stream.videoTracks[0];
286 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
287 }
288 });
289}
290
291- (void)peerConnection:(RTCPeerConnection *)peerConnection
292 removedStream:(RTCMediaStream *)stream {
293 NSLog(@"Stream was removed.");
294}
295
296- (void)peerConnectionOnRenegotiationNeeded:
297 (RTCPeerConnection *)peerConnection {
298 NSLog(@"WARNING: Renegotiation needed but unimplemented.");
299}
300
301- (void)peerConnection:(RTCPeerConnection *)peerConnection
302 iceConnectionChanged:(RTCICEConnectionState)newState {
303 NSLog(@"ICE state changed: %d", newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000304 dispatch_async(dispatch_get_main_queue(), ^{
305 [_delegate appClient:self didChangeConnectionState:newState];
306 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000307}
308
309- (void)peerConnection:(RTCPeerConnection *)peerConnection
310 iceGatheringChanged:(RTCICEGatheringState)newState {
311 NSLog(@"ICE gathering state changed: %d", newState);
312}
313
314- (void)peerConnection:(RTCPeerConnection *)peerConnection
315 gotICECandidate:(RTCICECandidate *)candidate {
316 dispatch_async(dispatch_get_main_queue(), ^{
317 ARDICECandidateMessage *message =
318 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
319 [self sendSignalingMessage:message];
320 });
321}
322
323- (void)peerConnection:(RTCPeerConnection*)peerConnection
324 didOpenDataChannel:(RTCDataChannel*)dataChannel {
325}
326
327#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000328// Callbacks for this delegate occur on non-main thread and need to be
329// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000330
331- (void)peerConnection:(RTCPeerConnection *)peerConnection
332 didCreateSessionDescription:(RTCSessionDescription *)sdp
333 error:(NSError *)error {
334 dispatch_async(dispatch_get_main_queue(), ^{
335 if (error) {
336 NSLog(@"Failed to create session description. Error: %@", error);
337 [self disconnect];
338 NSDictionary *userInfo = @{
339 NSLocalizedDescriptionKey: @"Failed to create session description.",
340 };
341 NSError *sdpError =
342 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
343 code:kARDAppClientErrorCreateSDP
344 userInfo:userInfo];
345 [_delegate appClient:self didError:sdpError];
346 return;
347 }
Zeke Chin71f6f442015-06-29 14:34:58 -0700348 // Prefer H264 if available.
349 RTCSessionDescription *sdpPreferringH264 =
350 [ARDSDPUtils descriptionForDescription:sdp
351 preferredVideoCodec:@"H264"];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000352 [_peerConnection setLocalDescriptionWithDelegate:self
Zeke Chin71f6f442015-06-29 14:34:58 -0700353 sessionDescription:sdpPreferringH264];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000354 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700355 [[ARDSessionDescriptionMessage alloc]
356 initWithDescription:sdpPreferringH264];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000357 [self sendSignalingMessage:message];
358 });
359}
360
361- (void)peerConnection:(RTCPeerConnection *)peerConnection
362 didSetSessionDescriptionWithError:(NSError *)error {
363 dispatch_async(dispatch_get_main_queue(), ^{
364 if (error) {
365 NSLog(@"Failed to set session description. Error: %@", error);
366 [self disconnect];
367 NSDictionary *userInfo = @{
368 NSLocalizedDescriptionKey: @"Failed to set session description.",
369 };
370 NSError *sdpError =
371 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
372 code:kARDAppClientErrorSetSDP
373 userInfo:userInfo];
374 [_delegate appClient:self didError:sdpError];
375 return;
376 }
377 // If we're answering and we've just set the remote offer we need to create
378 // an answer and set the local description.
379 if (!_isInitiator && !_peerConnection.localDescription) {
380 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
381 [_peerConnection createAnswerWithDelegate:self
382 constraints:constraints];
383
384 }
385 });
386}
387
388#pragma mark - Private
389
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000390- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000391 return _clientId.length;
392}
393
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000394// Begins the peer connection connection process if we have both joined a room
395// on the room server and tried to obtain a TURN server. Otherwise does nothing.
396// A peer connection object will be created with a stream that contains local
397// audio and video capture. If this client is the caller, an offer is created as
398// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000399- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000400 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000401 return;
402 }
403 self.state = kARDAppClientStateConnected;
404
405 // Create peer connection.
406 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700407 RTCConfiguration *config = [[RTCConfiguration alloc] init];
408 config.iceServers = _iceServers;
409 _peerConnection = [_factory peerConnectionWithConfiguration:config
410 constraints:constraints
411 delegate:self];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000412 // Create AV media stream and add it to the peer connection.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000413 RTCMediaStream *localStream = [self createLocalMediaStream];
414 [_peerConnection addStream:localStream];
415 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000416 // Send offer.
417 [_peerConnection createOfferWithDelegate:self
418 constraints:[self defaultOfferConstraints]];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000419 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000420 // Check if we've received an offer.
421 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000422 }
423}
424
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000425// Processes the messages that we've received from the room server and the
426// signaling channel. The offer or answer message must be processed before other
427// signaling messages, however they can arrive out of order. Hence, this method
428// only processes pending messages if there is a peer connection object and
429// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000430- (void)drainMessageQueueIfReady {
431 if (!_peerConnection || !_hasReceivedSdp) {
432 return;
433 }
434 for (ARDSignalingMessage *message in _messageQueue) {
435 [self processSignalingMessage:message];
436 }
437 [_messageQueue removeAllObjects];
438}
439
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000440// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000441- (void)processSignalingMessage:(ARDSignalingMessage *)message {
442 NSParameterAssert(_peerConnection ||
443 message.type == kARDSignalingMessageTypeBye);
444 switch (message.type) {
445 case kARDSignalingMessageTypeOffer:
446 case kARDSignalingMessageTypeAnswer: {
447 ARDSessionDescriptionMessage *sdpMessage =
448 (ARDSessionDescriptionMessage *)message;
449 RTCSessionDescription *description = sdpMessage.sessionDescription;
Zeke Chin71f6f442015-06-29 14:34:58 -0700450 // Prefer H264 if available.
451 RTCSessionDescription *sdpPreferringH264 =
452 [ARDSDPUtils descriptionForDescription:description
453 preferredVideoCodec:@"H264"];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000454 [_peerConnection setRemoteDescriptionWithDelegate:self
Zeke Chin71f6f442015-06-29 14:34:58 -0700455 sessionDescription:sdpPreferringH264];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000456 break;
457 }
458 case kARDSignalingMessageTypeCandidate: {
459 ARDICECandidateMessage *candidateMessage =
460 (ARDICECandidateMessage *)message;
461 [_peerConnection addICECandidate:candidateMessage.candidate];
462 break;
463 }
464 case kARDSignalingMessageTypeBye:
465 // Other client disconnected.
466 // TODO(tkchin): support waiting in room for next client. For now just
467 // disconnect.
468 [self disconnect];
469 break;
470 }
471}
472
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000473// Sends a signaling message to the other client. The caller will send messages
474// through the room server, whereas the callee will send messages over the
475// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000476- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
477 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000478 __weak ARDAppClient *weakSelf = self;
479 [_roomServerClient sendMessage:message
480 forRoomId:_roomId
481 clientId:_clientId
482 completionHandler:^(ARDMessageResponse *response,
483 NSError *error) {
484 ARDAppClient *strongSelf = weakSelf;
485 if (error) {
486 [strongSelf.delegate appClient:strongSelf didError:error];
487 return;
488 }
489 NSError *messageError =
490 [[strongSelf class] errorForMessageResultType:response.result];
491 if (messageError) {
492 [strongSelf.delegate appClient:strongSelf didError:messageError];
493 return;
494 }
495 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000496 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000497 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000498 }
499}
500
501- (RTCMediaStream *)createLocalMediaStream {
502 RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700503 RTCVideoTrack* localVideoTrack = [self createLocalVideoTrack];
504 if (localVideoTrack) {
505 [localStream addVideoTrack:localVideoTrack];
506 [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
507 }
508 [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
509 return localStream;
510}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000511
Zeke Chin57cc74e2015-05-05 07:52:31 -0700512- (RTCVideoTrack *)createLocalVideoTrack {
513 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000514 // The iOS simulator doesn't provide any sort of camera capture
515 // support or emulation (http://goo.gl/rHAnC1) so don't bother
516 // trying to open a local stream.
517 // TODO(tkchin): local video capture for OSX. See
518 // https://code.google.com/p/webrtc/issues/detail?id=3417.
519#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000520 RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700521 RTCAVFoundationVideoSource *source =
522 [[RTCAVFoundationVideoSource alloc] initWithFactory:_factory
523 constraints:mediaConstraints];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000524 localVideoTrack =
Zeke Chin57cc74e2015-05-05 07:52:31 -0700525 [[RTCVideoTrack alloc] initWithFactory:_factory
526 source:source
527 trackId:@"ARDAMSv0"];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000528#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700529 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000530}
531
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000532#pragma mark - Collider methods
533
534- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000535 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000536 return;
537 }
538 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000539 if (!_channel) {
540 _channel =
541 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
542 restURL:_websocketRestURL
543 delegate:self];
544 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000545 [_channel registerForRoomId:_roomId clientId:_clientId];
546}
547
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000548#pragma mark - Defaults
549
550- (RTCMediaConstraints *)defaultMediaStreamConstraints {
551 RTCMediaConstraints* constraints =
552 [[RTCMediaConstraints alloc]
553 initWithMandatoryConstraints:nil
554 optionalConstraints:nil];
555 return constraints;
556}
557
558- (RTCMediaConstraints *)defaultAnswerConstraints {
559 return [self defaultOfferConstraints];
560}
561
562- (RTCMediaConstraints *)defaultOfferConstraints {
563 NSArray *mandatoryConstraints = @[
564 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
565 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
566 ];
567 RTCMediaConstraints* constraints =
568 [[RTCMediaConstraints alloc]
569 initWithMandatoryConstraints:mandatoryConstraints
570 optionalConstraints:nil];
571 return constraints;
572}
573
574- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000575 if (_defaultPeerConnectionConstraints) {
576 return _defaultPeerConnectionConstraints;
577 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000578 NSArray *optionalConstraints = @[
579 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
580 ];
581 RTCMediaConstraints* constraints =
582 [[RTCMediaConstraints alloc]
583 initWithMandatoryConstraints:nil
584 optionalConstraints:optionalConstraints];
585 return constraints;
586}
587
588- (RTCICEServer *)defaultSTUNServer {
589 NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
590 return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
591 username:@""
592 password:@""];
593}
594
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000595#pragma mark - Errors
596
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000597+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000598 NSError *error = nil;
599 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000600 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000601 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000602 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000603 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
604 code:kARDAppClientErrorUnknown
605 userInfo:@{
606 NSLocalizedDescriptionKey: @"Unknown error.",
607 }];
608 break;
609 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000610 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000611 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
612 code:kARDAppClientErrorRoomFull
613 userInfo:@{
614 NSLocalizedDescriptionKey: @"Room is full.",
615 }];
616 break;
617 }
618 }
619 return error;
620}
621
622+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
623 NSError *error = nil;
624 switch (resultType) {
625 case kARDMessageResultTypeSuccess:
626 break;
627 case kARDMessageResultTypeUnknown:
628 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
629 code:kARDAppClientErrorUnknown
630 userInfo:@{
631 NSLocalizedDescriptionKey: @"Unknown error.",
632 }];
633 break;
634 case kARDMessageResultTypeInvalidClient:
635 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
636 code:kARDAppClientErrorInvalidClient
637 userInfo:@{
638 NSLocalizedDescriptionKey: @"Invalid client.",
639 }];
640 break;
641 case kARDMessageResultTypeInvalidRoom:
642 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
643 code:kARDAppClientErrorInvalidRoom
644 userInfo:@{
645 NSLocalizedDescriptionKey: @"Invalid room.",
646 }];
647 break;
648 }
649 return error;
650}
651
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000652@end