blob: eba3ae9db8b2f128678bb94faa585dfe159b9276 [file] [log] [blame]
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00001/*
2 * libjingle
3 * Copyright 2014, Google Inc.
4 *
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
28#import "ARDAppClient.h"
29
30#import <AVFoundation/AVFoundation.h>
31
32#import "ARDMessageResponse.h"
33#import "ARDRegisterResponse.h"
34#import "ARDSignalingMessage.h"
35#import "ARDUtilities.h"
36#import "ARDWebSocketChannel.h"
37#import "RTCICECandidate+JSON.h"
38#import "RTCICEServer+JSON.h"
39#import "RTCMediaConstraints.h"
40#import "RTCMediaStream.h"
41#import "RTCPair.h"
42#import "RTCPeerConnection.h"
43#import "RTCPeerConnectionDelegate.h"
44#import "RTCPeerConnectionFactory.h"
45#import "RTCSessionDescription+JSON.h"
46#import "RTCSessionDescriptionDelegate.h"
47#import "RTCVideoCapturer.h"
48#import "RTCVideoTrack.h"
49
50// TODO(tkchin): move these to a configuration object.
51static NSString *kARDRoomServerHostUrl =
52 @"https://3-dot-apprtc.appspot.com";
53static NSString *kARDRoomServerRegisterFormat =
54 @"https://3-dot-apprtc.appspot.com/register/%@";
55static NSString *kARDRoomServerMessageFormat =
56 @"https://3-dot-apprtc.appspot.com/message/%@/%@";
57static NSString *kARDRoomServerByeFormat =
58 @"https://3-dot-apprtc.appspot.com/bye/%@/%@";
59
60static NSString *kARDDefaultSTUNServerUrl =
61 @"stun:stun.l.google.com:19302";
62// TODO(tkchin): figure out a better username for CEOD statistics.
63static NSString *kARDTurnRequestUrl =
64 @"https://computeengineondemand.appspot.com"
65 @"/turn?username=iapprtc&key=4080218913";
66
67static NSString *kARDAppClientErrorDomain = @"ARDAppClient";
68static NSInteger kARDAppClientErrorUnknown = -1;
69static NSInteger kARDAppClientErrorRoomFull = -2;
70static NSInteger kARDAppClientErrorCreateSDP = -3;
71static NSInteger kARDAppClientErrorSetSDP = -4;
72static NSInteger kARDAppClientErrorNetwork = -5;
73static NSInteger kARDAppClientErrorInvalidClient = -6;
74static NSInteger kARDAppClientErrorInvalidRoom = -7;
75
76@interface ARDAppClient () <ARDWebSocketChannelDelegate,
77 RTCPeerConnectionDelegate, RTCSessionDescriptionDelegate>
78@property(nonatomic, strong) ARDWebSocketChannel *channel;
79@property(nonatomic, strong) RTCPeerConnection *peerConnection;
80@property(nonatomic, strong) RTCPeerConnectionFactory *factory;
81@property(nonatomic, strong) NSMutableArray *messageQueue;
82
83@property(nonatomic, assign) BOOL isTurnComplete;
84@property(nonatomic, assign) BOOL hasReceivedSdp;
85@property(nonatomic, readonly) BOOL isRegisteredWithRoomServer;
86
87@property(nonatomic, strong) NSString *roomId;
88@property(nonatomic, strong) NSString *clientId;
89@property(nonatomic, assign) BOOL isInitiator;
90@property(nonatomic, strong) NSMutableArray *iceServers;
91@property(nonatomic, strong) NSURL *webSocketURL;
92@property(nonatomic, strong) NSURL *webSocketRestURL;
93@end
94
95@implementation ARDAppClient
96
97@synthesize delegate = _delegate;
98@synthesize state = _state;
99@synthesize channel = _channel;
100@synthesize peerConnection = _peerConnection;
101@synthesize factory = _factory;
102@synthesize messageQueue = _messageQueue;
103@synthesize isTurnComplete = _isTurnComplete;
104@synthesize hasReceivedSdp = _hasReceivedSdp;
105@synthesize roomId = _roomId;
106@synthesize clientId = _clientId;
107@synthesize isInitiator = _isInitiator;
108@synthesize iceServers = _iceServers;
109@synthesize webSocketURL = _websocketURL;
110@synthesize webSocketRestURL = _websocketRestURL;
111
112- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
113 if (self = [super init]) {
114 _delegate = delegate;
115 _factory = [[RTCPeerConnectionFactory alloc] init];
116 _messageQueue = [NSMutableArray array];
117 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
118 }
119 return self;
120}
121
122- (void)dealloc {
123 [self disconnect];
124}
125
126- (void)setState:(ARDAppClientState)state {
127 if (_state == state) {
128 return;
129 }
130 _state = state;
131 [_delegate appClient:self didChangeState:_state];
132}
133
134- (void)connectToRoomWithId:(NSString *)roomId
135 options:(NSDictionary *)options {
136 NSParameterAssert(roomId.length);
137 NSParameterAssert(_state == kARDAppClientStateDisconnected);
138 self.state = kARDAppClientStateConnecting;
139
140 // Request TURN.
141 __weak ARDAppClient *weakSelf = self;
142 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
143 [self requestTURNServersWithURL:turnRequestURL
144 completionHandler:^(NSArray *turnServers) {
145 ARDAppClient *strongSelf = weakSelf;
146 [strongSelf.iceServers addObjectsFromArray:turnServers];
147 strongSelf.isTurnComplete = YES;
148 [strongSelf startSignalingIfReady];
149 }];
150
151 // Register with room server.
152 [self registerWithRoomServerForRoomId:roomId
153 completionHandler:^(ARDRegisterResponse *response) {
154 ARDAppClient *strongSelf = weakSelf;
155 if (!response || response.result != kARDRegisterResultTypeSuccess) {
156 NSLog(@"Failed to register with room server. Result:%d",
157 (int)response.result);
158 [strongSelf disconnect];
159 NSDictionary *userInfo = @{
160 NSLocalizedDescriptionKey: @"Room is full.",
161 };
162 NSError *error =
163 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
164 code:kARDAppClientErrorRoomFull
165 userInfo:userInfo];
166 [strongSelf.delegate appClient:strongSelf didError:error];
167 return;
168 }
169 NSLog(@"Registered with room server.");
170 strongSelf.roomId = response.roomId;
171 strongSelf.clientId = response.clientId;
172 strongSelf.isInitiator = response.isInitiator;
173 for (ARDSignalingMessage *message in response.messages) {
174 if (message.type == kARDSignalingMessageTypeOffer ||
175 message.type == kARDSignalingMessageTypeAnswer) {
176 strongSelf.hasReceivedSdp = YES;
177 [strongSelf.messageQueue insertObject:message atIndex:0];
178 } else {
179 [strongSelf.messageQueue addObject:message];
180 }
181 }
182 strongSelf.webSocketURL = response.webSocketURL;
183 strongSelf.webSocketRestURL = response.webSocketRestURL;
184 [strongSelf registerWithColliderIfReady];
185 [strongSelf startSignalingIfReady];
186 }];
187}
188
189- (void)disconnect {
190 if (_state == kARDAppClientStateDisconnected) {
191 return;
192 }
193 if (self.isRegisteredWithRoomServer) {
194 [self unregisterWithRoomServer];
195 }
196 if (_channel) {
197 if (_channel.state == kARDWebSocketChannelStateRegistered) {
198 // Tell the other client we're hanging up.
199 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
200 NSData *byeData = [byeMessage JSONData];
201 [_channel sendData:byeData];
202 }
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
215#pragma mark - ARDWebSocketChannelDelegate
216
217- (void)channel:(ARDWebSocketChannel *)channel
218 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
235- (void)channel:(ARDWebSocketChannel *)channel
236 didChangeState:(ARDWebSocketChannelState)state {
237 switch (state) {
238 case kARDWebSocketChannelStateOpen:
239 break;
240 case kARDWebSocketChannelStateRegistered:
241 break;
242 case kARDWebSocketChannelStateClosed:
243 case kARDWebSocketChannelStateError:
244 // 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);
284}
285
286- (void)peerConnection:(RTCPeerConnection *)peerConnection
287 iceGatheringChanged:(RTCICEGatheringState)newState {
288 NSLog(@"ICE gathering state changed: %d", newState);
289}
290
291- (void)peerConnection:(RTCPeerConnection *)peerConnection
292 gotICECandidate:(RTCICECandidate *)candidate {
293 dispatch_async(dispatch_get_main_queue(), ^{
294 ARDICECandidateMessage *message =
295 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
296 [self sendSignalingMessage:message];
297 });
298}
299
300- (void)peerConnection:(RTCPeerConnection*)peerConnection
301 didOpenDataChannel:(RTCDataChannel*)dataChannel {
302}
303
304#pragma mark - RTCSessionDescriptionDelegate
305
306- (void)peerConnection:(RTCPeerConnection *)peerConnection
307 didCreateSessionDescription:(RTCSessionDescription *)sdp
308 error:(NSError *)error {
309 dispatch_async(dispatch_get_main_queue(), ^{
310 if (error) {
311 NSLog(@"Failed to create session description. Error: %@", error);
312 [self disconnect];
313 NSDictionary *userInfo = @{
314 NSLocalizedDescriptionKey: @"Failed to create session description.",
315 };
316 NSError *sdpError =
317 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
318 code:kARDAppClientErrorCreateSDP
319 userInfo:userInfo];
320 [_delegate appClient:self didError:sdpError];
321 return;
322 }
323 [_peerConnection setLocalDescriptionWithDelegate:self
324 sessionDescription:sdp];
325 ARDSessionDescriptionMessage *message =
326 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
327 [self sendSignalingMessage:message];
328 });
329}
330
331- (void)peerConnection:(RTCPeerConnection *)peerConnection
332 didSetSessionDescriptionWithError:(NSError *)error {
333 dispatch_async(dispatch_get_main_queue(), ^{
334 if (error) {
335 NSLog(@"Failed to set session description. Error: %@", error);
336 [self disconnect];
337 NSDictionary *userInfo = @{
338 NSLocalizedDescriptionKey: @"Failed to set session description.",
339 };
340 NSError *sdpError =
341 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
342 code:kARDAppClientErrorSetSDP
343 userInfo:userInfo];
344 [_delegate appClient:self didError:sdpError];
345 return;
346 }
347 // If we're answering and we've just set the remote offer we need to create
348 // an answer and set the local description.
349 if (!_isInitiator && !_peerConnection.localDescription) {
350 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
351 [_peerConnection createAnswerWithDelegate:self
352 constraints:constraints];
353
354 }
355 });
356}
357
358#pragma mark - Private
359
360- (BOOL)isRegisteredWithRoomServer {
361 return _clientId.length;
362}
363
364- (void)startSignalingIfReady {
365 if (!_isTurnComplete || !self.isRegisteredWithRoomServer) {
366 return;
367 }
368 self.state = kARDAppClientStateConnected;
369
370 // Create peer connection.
371 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
372 _peerConnection = [_factory peerConnectionWithICEServers:_iceServers
373 constraints:constraints
374 delegate:self];
375 RTCMediaStream *localStream = [self createLocalMediaStream];
376 [_peerConnection addStream:localStream];
377 if (_isInitiator) {
378 [self sendOffer];
379 } else {
380 [self waitForAnswer];
381 }
382}
383
384- (void)sendOffer {
385 [_peerConnection createOfferWithDelegate:self
386 constraints:[self defaultOfferConstraints]];
387}
388
389- (void)waitForAnswer {
390 [self drainMessageQueueIfReady];
391}
392
393- (void)drainMessageQueueIfReady {
394 if (!_peerConnection || !_hasReceivedSdp) {
395 return;
396 }
397 for (ARDSignalingMessage *message in _messageQueue) {
398 [self processSignalingMessage:message];
399 }
400 [_messageQueue removeAllObjects];
401}
402
403- (void)processSignalingMessage:(ARDSignalingMessage *)message {
404 NSParameterAssert(_peerConnection ||
405 message.type == kARDSignalingMessageTypeBye);
406 switch (message.type) {
407 case kARDSignalingMessageTypeOffer:
408 case kARDSignalingMessageTypeAnswer: {
409 ARDSessionDescriptionMessage *sdpMessage =
410 (ARDSessionDescriptionMessage *)message;
411 RTCSessionDescription *description = sdpMessage.sessionDescription;
412 [_peerConnection setRemoteDescriptionWithDelegate:self
413 sessionDescription:description];
414 break;
415 }
416 case kARDSignalingMessageTypeCandidate: {
417 ARDICECandidateMessage *candidateMessage =
418 (ARDICECandidateMessage *)message;
419 [_peerConnection addICECandidate:candidateMessage.candidate];
420 break;
421 }
422 case kARDSignalingMessageTypeBye:
423 // Other client disconnected.
424 // TODO(tkchin): support waiting in room for next client. For now just
425 // disconnect.
426 [self disconnect];
427 break;
428 }
429}
430
431- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
432 if (_isInitiator) {
433 [self sendSignalingMessageToRoomServer:message completionHandler:nil];
434 } else {
435 [self sendSignalingMessageToCollider:message];
436 }
437}
438
439- (RTCMediaStream *)createLocalMediaStream {
440 RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
441 RTCVideoTrack* localVideoTrack = nil;
442
443 // The iOS simulator doesn't provide any sort of camera capture
444 // support or emulation (http://goo.gl/rHAnC1) so don't bother
445 // trying to open a local stream.
446 // TODO(tkchin): local video capture for OSX. See
447 // https://code.google.com/p/webrtc/issues/detail?id=3417.
448#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
449 NSString *cameraID = nil;
450 for (AVCaptureDevice *captureDevice in
451 [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
452 if (captureDevice.position == AVCaptureDevicePositionFront) {
453 cameraID = [captureDevice localizedName];
454 break;
455 }
456 }
457 NSAssert(cameraID, @"Unable to get the front camera id");
458
459 RTCVideoCapturer *capturer =
460 [RTCVideoCapturer capturerWithDeviceName:cameraID];
461 RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
462 RTCVideoSource *videoSource =
463 [_factory videoSourceWithCapturer:capturer
464 constraints:mediaConstraints];
465 localVideoTrack =
466 [_factory videoTrackWithID:@"ARDAMSv0" source:videoSource];
467 if (localVideoTrack) {
468 [localStream addVideoTrack:localVideoTrack];
469 }
470 [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
471#endif
472 [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
473 return localStream;
474}
475
476- (void)requestTURNServersWithURL:(NSURL *)requestURL
477 completionHandler:(void (^)(NSArray *turnServers))completionHandler {
478 NSParameterAssert([requestURL absoluteString].length);
479 NSMutableURLRequest *request =
480 [NSMutableURLRequest requestWithURL:requestURL];
481 // We need to set origin because TURN provider whitelists requests based on
482 // origin.
483 [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"];
484 [request addValue:kARDRoomServerHostUrl forHTTPHeaderField:@"origin"];
485 [NSURLConnection sendAsyncRequest:request
486 completionHandler:^(NSURLResponse *response,
487 NSData *data,
488 NSError *error) {
489 NSArray *turnServers = [NSArray array];
490 if (error) {
491 NSLog(@"Unable to get TURN server.");
492 completionHandler(turnServers);
493 return;
494 }
495 NSDictionary *dict = [NSDictionary dictionaryWithJSONData:data];
496 turnServers = [RTCICEServer serversFromCEODJSONDictionary:dict];
497 completionHandler(turnServers);
498 }];
499}
500
501#pragma mark - Room server methods
502
503- (void)registerWithRoomServerForRoomId:(NSString *)roomId
504 completionHandler:(void (^)(ARDRegisterResponse *))completionHandler {
505 NSString *urlString =
506 [NSString stringWithFormat:kARDRoomServerRegisterFormat, roomId];
507 NSURL *roomURL = [NSURL URLWithString:urlString];
508 NSLog(@"Registering with room server.");
509 __weak ARDAppClient *weakSelf = self;
510 [NSURLConnection sendAsyncPostToURL:roomURL
511 withData:nil
512 completionHandler:^(BOOL succeeded, NSData *data) {
513 ARDAppClient *strongSelf = weakSelf;
514 if (!succeeded) {
515 NSError *error = [self roomServerNetworkError];
516 [strongSelf.delegate appClient:strongSelf didError:error];
517 completionHandler(nil);
518 return;
519 }
520 ARDRegisterResponse *response =
521 [ARDRegisterResponse responseFromJSONData:data];
522 completionHandler(response);
523 }];
524}
525
526- (void)sendSignalingMessageToRoomServer:(ARDSignalingMessage *)message
527 completionHandler:(void (^)(ARDMessageResponse *))completionHandler {
528 NSData *data = [message JSONData];
529 NSString *urlString =
530 [NSString stringWithFormat:
531 kARDRoomServerMessageFormat, _roomId, _clientId];
532 NSURL *url = [NSURL URLWithString:urlString];
533 NSLog(@"C->RS POST: %@", message);
534 __weak ARDAppClient *weakSelf = self;
535 [NSURLConnection sendAsyncPostToURL:url
536 withData:data
537 completionHandler:^(BOOL succeeded, NSData *data) {
538 ARDAppClient *strongSelf = weakSelf;
539 if (!succeeded) {
540 NSError *error = [self roomServerNetworkError];
541 [strongSelf.delegate appClient:strongSelf didError:error];
542 return;
543 }
544 ARDMessageResponse *response =
545 [ARDMessageResponse responseFromJSONData:data];
546 NSError *error = nil;
547 switch (response.result) {
548 case kARDMessageResultTypeSuccess:
549 break;
550 case kARDMessageResultTypeUnknown:
551 error =
552 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
553 code:kARDAppClientErrorUnknown
554 userInfo:@{
555 NSLocalizedDescriptionKey: @"Unknown error.",
556 }];
557 case kARDMessageResultTypeInvalidClient:
558 error =
559 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
560 code:kARDAppClientErrorInvalidClient
561 userInfo:@{
562 NSLocalizedDescriptionKey: @"Invalid client.",
563 }];
564 break;
565 case kARDMessageResultTypeInvalidRoom:
566 error =
567 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
568 code:kARDAppClientErrorInvalidRoom
569 userInfo:@{
570 NSLocalizedDescriptionKey: @"Invalid room.",
571 }];
572 break;
573 };
574 if (error) {
575 [strongSelf.delegate appClient:strongSelf didError:error];
576 }
577 if (completionHandler) {
578 completionHandler(response);
579 }
580 }];
581}
582
583- (void)unregisterWithRoomServer {
584 NSString *urlString =
585 [NSString stringWithFormat:kARDRoomServerByeFormat, _roomId, _clientId];
586 NSURL *url = [NSURL URLWithString:urlString];
587 NSURLRequest *request = [NSURLRequest requestWithURL:url];
588 NSURLResponse *response = nil;
589 // We want a synchronous request so that we know that we're unregistered from
590 // room server before we do any further unregistration.
591 NSLog(@"C->RS: BYE");
592 NSError *error = nil;
593 [NSURLConnection sendSynchronousRequest:request
594 returningResponse:&response
595 error:&error];
596 if (error) {
597 NSLog(@"Error unregistering from room server: %@", error);
598 }
599 NSLog(@"Unregistered from room server.");
600}
601
602- (NSError *)roomServerNetworkError {
603 NSError *error =
604 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
605 code:kARDAppClientErrorNetwork
606 userInfo:@{
607 NSLocalizedDescriptionKey: @"Room server network error",
608 }];
609 return error;
610}
611
612#pragma mark - Collider methods
613
614- (void)registerWithColliderIfReady {
615 if (!self.isRegisteredWithRoomServer) {
616 return;
617 }
618 // Open WebSocket connection.
619 _channel =
620 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
621 restURL:_websocketRestURL
622 delegate:self];
623 [_channel registerForRoomId:_roomId clientId:_clientId];
624}
625
626- (void)sendSignalingMessageToCollider:(ARDSignalingMessage *)message {
627 NSData *data = [message JSONData];
628 [_channel sendData:data];
629}
630
631#pragma mark - Defaults
632
633- (RTCMediaConstraints *)defaultMediaStreamConstraints {
634 RTCMediaConstraints* constraints =
635 [[RTCMediaConstraints alloc]
636 initWithMandatoryConstraints:nil
637 optionalConstraints:nil];
638 return constraints;
639}
640
641- (RTCMediaConstraints *)defaultAnswerConstraints {
642 return [self defaultOfferConstraints];
643}
644
645- (RTCMediaConstraints *)defaultOfferConstraints {
646 NSArray *mandatoryConstraints = @[
647 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
648 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
649 ];
650 RTCMediaConstraints* constraints =
651 [[RTCMediaConstraints alloc]
652 initWithMandatoryConstraints:mandatoryConstraints
653 optionalConstraints:nil];
654 return constraints;
655}
656
657- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
658 NSArray *optionalConstraints = @[
659 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
660 ];
661 RTCMediaConstraints* constraints =
662 [[RTCMediaConstraints alloc]
663 initWithMandatoryConstraints:nil
664 optionalConstraints:optionalConstraints];
665 return constraints;
666}
667
668- (RTCICEServer *)defaultSTUNServer {
669 NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
670 return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
671 username:@""
672 password:@""];
673}
674
675@end