iAppRTCDemo: WebSocket based signaling.
Updates the iOS code to use the new signaling model. Removes old Channel API
code. Note that this no longer logs messages to UI. UI update forthcoming.
BUG=
R=glaznev@webrtc.org, jiayl@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/35369004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@7852 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/examples/objc/AppRTCDemo/ARDAppClient.m b/talk/examples/objc/AppRTCDemo/ARDAppClient.m
new file mode 100644
index 0000000..eba3ae9
--- /dev/null
+++ b/talk/examples/objc/AppRTCDemo/ARDAppClient.m
@@ -0,0 +1,675 @@
+/*
+ * libjingle
+ * Copyright 2014, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "ARDAppClient.h"
+
+#import <AVFoundation/AVFoundation.h>
+
+#import "ARDMessageResponse.h"
+#import "ARDRegisterResponse.h"
+#import "ARDSignalingMessage.h"
+#import "ARDUtilities.h"
+#import "ARDWebSocketChannel.h"
+#import "RTCICECandidate+JSON.h"
+#import "RTCICEServer+JSON.h"
+#import "RTCMediaConstraints.h"
+#import "RTCMediaStream.h"
+#import "RTCPair.h"
+#import "RTCPeerConnection.h"
+#import "RTCPeerConnectionDelegate.h"
+#import "RTCPeerConnectionFactory.h"
+#import "RTCSessionDescription+JSON.h"
+#import "RTCSessionDescriptionDelegate.h"
+#import "RTCVideoCapturer.h"
+#import "RTCVideoTrack.h"
+
+// TODO(tkchin): move these to a configuration object.
+static NSString *kARDRoomServerHostUrl =
+ @"https://3-dot-apprtc.appspot.com";
+static NSString *kARDRoomServerRegisterFormat =
+ @"https://3-dot-apprtc.appspot.com/register/%@";
+static NSString *kARDRoomServerMessageFormat =
+ @"https://3-dot-apprtc.appspot.com/message/%@/%@";
+static NSString *kARDRoomServerByeFormat =
+ @"https://3-dot-apprtc.appspot.com/bye/%@/%@";
+
+static NSString *kARDDefaultSTUNServerUrl =
+ @"stun:stun.l.google.com:19302";
+// TODO(tkchin): figure out a better username for CEOD statistics.
+static NSString *kARDTurnRequestUrl =
+ @"https://computeengineondemand.appspot.com"
+ @"/turn?username=iapprtc&key=4080218913";
+
+static NSString *kARDAppClientErrorDomain = @"ARDAppClient";
+static NSInteger kARDAppClientErrorUnknown = -1;
+static NSInteger kARDAppClientErrorRoomFull = -2;
+static NSInteger kARDAppClientErrorCreateSDP = -3;
+static NSInteger kARDAppClientErrorSetSDP = -4;
+static NSInteger kARDAppClientErrorNetwork = -5;
+static NSInteger kARDAppClientErrorInvalidClient = -6;
+static NSInteger kARDAppClientErrorInvalidRoom = -7;
+
+@interface ARDAppClient () <ARDWebSocketChannelDelegate,
+ RTCPeerConnectionDelegate, RTCSessionDescriptionDelegate>
+@property(nonatomic, strong) ARDWebSocketChannel *channel;
+@property(nonatomic, strong) RTCPeerConnection *peerConnection;
+@property(nonatomic, strong) RTCPeerConnectionFactory *factory;
+@property(nonatomic, strong) NSMutableArray *messageQueue;
+
+@property(nonatomic, assign) BOOL isTurnComplete;
+@property(nonatomic, assign) BOOL hasReceivedSdp;
+@property(nonatomic, readonly) BOOL isRegisteredWithRoomServer;
+
+@property(nonatomic, strong) NSString *roomId;
+@property(nonatomic, strong) NSString *clientId;
+@property(nonatomic, assign) BOOL isInitiator;
+@property(nonatomic, strong) NSMutableArray *iceServers;
+@property(nonatomic, strong) NSURL *webSocketURL;
+@property(nonatomic, strong) NSURL *webSocketRestURL;
+@end
+
+@implementation ARDAppClient
+
+@synthesize delegate = _delegate;
+@synthesize state = _state;
+@synthesize channel = _channel;
+@synthesize peerConnection = _peerConnection;
+@synthesize factory = _factory;
+@synthesize messageQueue = _messageQueue;
+@synthesize isTurnComplete = _isTurnComplete;
+@synthesize hasReceivedSdp = _hasReceivedSdp;
+@synthesize roomId = _roomId;
+@synthesize clientId = _clientId;
+@synthesize isInitiator = _isInitiator;
+@synthesize iceServers = _iceServers;
+@synthesize webSocketURL = _websocketURL;
+@synthesize webSocketRestURL = _websocketRestURL;
+
+- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
+ if (self = [super init]) {
+ _delegate = delegate;
+ _factory = [[RTCPeerConnectionFactory alloc] init];
+ _messageQueue = [NSMutableArray array];
+ _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [self disconnect];
+}
+
+- (void)setState:(ARDAppClientState)state {
+ if (_state == state) {
+ return;
+ }
+ _state = state;
+ [_delegate appClient:self didChangeState:_state];
+}
+
+- (void)connectToRoomWithId:(NSString *)roomId
+ options:(NSDictionary *)options {
+ NSParameterAssert(roomId.length);
+ NSParameterAssert(_state == kARDAppClientStateDisconnected);
+ self.state = kARDAppClientStateConnecting;
+
+ // Request TURN.
+ __weak ARDAppClient *weakSelf = self;
+ NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
+ [self requestTURNServersWithURL:turnRequestURL
+ completionHandler:^(NSArray *turnServers) {
+ ARDAppClient *strongSelf = weakSelf;
+ [strongSelf.iceServers addObjectsFromArray:turnServers];
+ strongSelf.isTurnComplete = YES;
+ [strongSelf startSignalingIfReady];
+ }];
+
+ // Register with room server.
+ [self registerWithRoomServerForRoomId:roomId
+ completionHandler:^(ARDRegisterResponse *response) {
+ ARDAppClient *strongSelf = weakSelf;
+ if (!response || response.result != kARDRegisterResultTypeSuccess) {
+ NSLog(@"Failed to register with room server. Result:%d",
+ (int)response.result);
+ [strongSelf disconnect];
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: @"Room is full.",
+ };
+ NSError *error =
+ [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+ code:kARDAppClientErrorRoomFull
+ userInfo:userInfo];
+ [strongSelf.delegate appClient:strongSelf didError:error];
+ return;
+ }
+ NSLog(@"Registered with room server.");
+ strongSelf.roomId = response.roomId;
+ strongSelf.clientId = response.clientId;
+ strongSelf.isInitiator = response.isInitiator;
+ for (ARDSignalingMessage *message in response.messages) {
+ if (message.type == kARDSignalingMessageTypeOffer ||
+ message.type == kARDSignalingMessageTypeAnswer) {
+ strongSelf.hasReceivedSdp = YES;
+ [strongSelf.messageQueue insertObject:message atIndex:0];
+ } else {
+ [strongSelf.messageQueue addObject:message];
+ }
+ }
+ strongSelf.webSocketURL = response.webSocketURL;
+ strongSelf.webSocketRestURL = response.webSocketRestURL;
+ [strongSelf registerWithColliderIfReady];
+ [strongSelf startSignalingIfReady];
+ }];
+}
+
+- (void)disconnect {
+ if (_state == kARDAppClientStateDisconnected) {
+ return;
+ }
+ if (self.isRegisteredWithRoomServer) {
+ [self unregisterWithRoomServer];
+ }
+ if (_channel) {
+ if (_channel.state == kARDWebSocketChannelStateRegistered) {
+ // Tell the other client we're hanging up.
+ ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
+ NSData *byeData = [byeMessage JSONData];
+ [_channel sendData:byeData];
+ }
+ // Disconnect from collider.
+ _channel = nil;
+ }
+ _clientId = nil;
+ _roomId = nil;
+ _isInitiator = NO;
+ _hasReceivedSdp = NO;
+ _messageQueue = [NSMutableArray array];
+ _peerConnection = nil;
+ self.state = kARDAppClientStateDisconnected;
+}
+
+#pragma mark - ARDWebSocketChannelDelegate
+
+- (void)channel:(ARDWebSocketChannel *)channel
+ didReceiveMessage:(ARDSignalingMessage *)message {
+ switch (message.type) {
+ case kARDSignalingMessageTypeOffer:
+ case kARDSignalingMessageTypeAnswer:
+ _hasReceivedSdp = YES;
+ [_messageQueue insertObject:message atIndex:0];
+ break;
+ case kARDSignalingMessageTypeCandidate:
+ [_messageQueue addObject:message];
+ break;
+ case kARDSignalingMessageTypeBye:
+ [self processSignalingMessage:message];
+ return;
+ }
+ [self drainMessageQueueIfReady];
+}
+
+- (void)channel:(ARDWebSocketChannel *)channel
+ didChangeState:(ARDWebSocketChannelState)state {
+ switch (state) {
+ case kARDWebSocketChannelStateOpen:
+ break;
+ case kARDWebSocketChannelStateRegistered:
+ break;
+ case kARDWebSocketChannelStateClosed:
+ case kARDWebSocketChannelStateError:
+ // TODO(tkchin): reconnection scenarios. Right now we just disconnect
+ // completely if the websocket connection fails.
+ [self disconnect];
+ break;
+ }
+}
+
+#pragma mark - RTCPeerConnectionDelegate
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ signalingStateChanged:(RTCSignalingState)stateChanged {
+ NSLog(@"Signaling state changed: %d", stateChanged);
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ addedStream:(RTCMediaStream *)stream {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSLog(@"Received %lu video tracks and %lu audio tracks",
+ (unsigned long)stream.videoTracks.count,
+ (unsigned long)stream.audioTracks.count);
+ if (stream.videoTracks.count) {
+ RTCVideoTrack *videoTrack = stream.videoTracks[0];
+ [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
+ }
+ });
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ removedStream:(RTCMediaStream *)stream {
+ NSLog(@"Stream was removed.");
+}
+
+- (void)peerConnectionOnRenegotiationNeeded:
+ (RTCPeerConnection *)peerConnection {
+ NSLog(@"WARNING: Renegotiation needed but unimplemented.");
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ iceConnectionChanged:(RTCICEConnectionState)newState {
+ NSLog(@"ICE state changed: %d", newState);
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ iceGatheringChanged:(RTCICEGatheringState)newState {
+ NSLog(@"ICE gathering state changed: %d", newState);
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ gotICECandidate:(RTCICECandidate *)candidate {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ ARDICECandidateMessage *message =
+ [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
+ [self sendSignalingMessage:message];
+ });
+}
+
+- (void)peerConnection:(RTCPeerConnection*)peerConnection
+ didOpenDataChannel:(RTCDataChannel*)dataChannel {
+}
+
+#pragma mark - RTCSessionDescriptionDelegate
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ didCreateSessionDescription:(RTCSessionDescription *)sdp
+ error:(NSError *)error {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (error) {
+ NSLog(@"Failed to create session description. Error: %@", error);
+ [self disconnect];
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: @"Failed to create session description.",
+ };
+ NSError *sdpError =
+ [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+ code:kARDAppClientErrorCreateSDP
+ userInfo:userInfo];
+ [_delegate appClient:self didError:sdpError];
+ return;
+ }
+ [_peerConnection setLocalDescriptionWithDelegate:self
+ sessionDescription:sdp];
+ ARDSessionDescriptionMessage *message =
+ [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
+ [self sendSignalingMessage:message];
+ });
+}
+
+- (void)peerConnection:(RTCPeerConnection *)peerConnection
+ didSetSessionDescriptionWithError:(NSError *)error {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (error) {
+ NSLog(@"Failed to set session description. Error: %@", error);
+ [self disconnect];
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: @"Failed to set session description.",
+ };
+ NSError *sdpError =
+ [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+ code:kARDAppClientErrorSetSDP
+ userInfo:userInfo];
+ [_delegate appClient:self didError:sdpError];
+ return;
+ }
+ // If we're answering and we've just set the remote offer we need to create
+ // an answer and set the local description.
+ if (!_isInitiator && !_peerConnection.localDescription) {
+ RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
+ [_peerConnection createAnswerWithDelegate:self
+ constraints:constraints];
+
+ }
+ });
+}
+
+#pragma mark - Private
+
+- (BOOL)isRegisteredWithRoomServer {
+ return _clientId.length;
+}
+
+- (void)startSignalingIfReady {
+ if (!_isTurnComplete || !self.isRegisteredWithRoomServer) {
+ return;
+ }
+ self.state = kARDAppClientStateConnected;
+
+ // Create peer connection.
+ RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
+ _peerConnection = [_factory peerConnectionWithICEServers:_iceServers
+ constraints:constraints
+ delegate:self];
+ RTCMediaStream *localStream = [self createLocalMediaStream];
+ [_peerConnection addStream:localStream];
+ if (_isInitiator) {
+ [self sendOffer];
+ } else {
+ [self waitForAnswer];
+ }
+}
+
+- (void)sendOffer {
+ [_peerConnection createOfferWithDelegate:self
+ constraints:[self defaultOfferConstraints]];
+}
+
+- (void)waitForAnswer {
+ [self drainMessageQueueIfReady];
+}
+
+- (void)drainMessageQueueIfReady {
+ if (!_peerConnection || !_hasReceivedSdp) {
+ return;
+ }
+ for (ARDSignalingMessage *message in _messageQueue) {
+ [self processSignalingMessage:message];
+ }
+ [_messageQueue removeAllObjects];
+}
+
+- (void)processSignalingMessage:(ARDSignalingMessage *)message {
+ NSParameterAssert(_peerConnection ||
+ message.type == kARDSignalingMessageTypeBye);
+ switch (message.type) {
+ case kARDSignalingMessageTypeOffer:
+ case kARDSignalingMessageTypeAnswer: {
+ ARDSessionDescriptionMessage *sdpMessage =
+ (ARDSessionDescriptionMessage *)message;
+ RTCSessionDescription *description = sdpMessage.sessionDescription;
+ [_peerConnection setRemoteDescriptionWithDelegate:self
+ sessionDescription:description];
+ break;
+ }
+ case kARDSignalingMessageTypeCandidate: {
+ ARDICECandidateMessage *candidateMessage =
+ (ARDICECandidateMessage *)message;
+ [_peerConnection addICECandidate:candidateMessage.candidate];
+ break;
+ }
+ case kARDSignalingMessageTypeBye:
+ // Other client disconnected.
+ // TODO(tkchin): support waiting in room for next client. For now just
+ // disconnect.
+ [self disconnect];
+ break;
+ }
+}
+
+- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
+ if (_isInitiator) {
+ [self sendSignalingMessageToRoomServer:message completionHandler:nil];
+ } else {
+ [self sendSignalingMessageToCollider:message];
+ }
+}
+
+- (RTCMediaStream *)createLocalMediaStream {
+ RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"];
+ RTCVideoTrack* localVideoTrack = nil;
+
+ // The iOS simulator doesn't provide any sort of camera capture
+ // support or emulation (http://goo.gl/rHAnC1) so don't bother
+ // trying to open a local stream.
+ // TODO(tkchin): local video capture for OSX. See
+ // https://code.google.com/p/webrtc/issues/detail?id=3417.
+#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
+ NSString *cameraID = nil;
+ for (AVCaptureDevice *captureDevice in
+ [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
+ if (captureDevice.position == AVCaptureDevicePositionFront) {
+ cameraID = [captureDevice localizedName];
+ break;
+ }
+ }
+ NSAssert(cameraID, @"Unable to get the front camera id");
+
+ RTCVideoCapturer *capturer =
+ [RTCVideoCapturer capturerWithDeviceName:cameraID];
+ RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints];
+ RTCVideoSource *videoSource =
+ [_factory videoSourceWithCapturer:capturer
+ constraints:mediaConstraints];
+ localVideoTrack =
+ [_factory videoTrackWithID:@"ARDAMSv0" source:videoSource];
+ if (localVideoTrack) {
+ [localStream addVideoTrack:localVideoTrack];
+ }
+ [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack];
+#endif
+ [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]];
+ return localStream;
+}
+
+- (void)requestTURNServersWithURL:(NSURL *)requestURL
+ completionHandler:(void (^)(NSArray *turnServers))completionHandler {
+ NSParameterAssert([requestURL absoluteString].length);
+ NSMutableURLRequest *request =
+ [NSMutableURLRequest requestWithURL:requestURL];
+ // We need to set origin because TURN provider whitelists requests based on
+ // origin.
+ [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"];
+ [request addValue:kARDRoomServerHostUrl forHTTPHeaderField:@"origin"];
+ [NSURLConnection sendAsyncRequest:request
+ completionHandler:^(NSURLResponse *response,
+ NSData *data,
+ NSError *error) {
+ NSArray *turnServers = [NSArray array];
+ if (error) {
+ NSLog(@"Unable to get TURN server.");
+ completionHandler(turnServers);
+ return;
+ }
+ NSDictionary *dict = [NSDictionary dictionaryWithJSONData:data];
+ turnServers = [RTCICEServer serversFromCEODJSONDictionary:dict];
+ completionHandler(turnServers);
+ }];
+}
+
+#pragma mark - Room server methods
+
+- (void)registerWithRoomServerForRoomId:(NSString *)roomId
+ completionHandler:(void (^)(ARDRegisterResponse *))completionHandler {
+ NSString *urlString =
+ [NSString stringWithFormat:kARDRoomServerRegisterFormat, roomId];
+ NSURL *roomURL = [NSURL URLWithString:urlString];
+ NSLog(@"Registering with room server.");
+ __weak ARDAppClient *weakSelf = self;
+ [NSURLConnection sendAsyncPostToURL:roomURL
+ withData:nil
+ completionHandler:^(BOOL succeeded, NSData *data) {
+ ARDAppClient *strongSelf = weakSelf;
+ if (!succeeded) {
+ NSError *error = [self roomServerNetworkError];
+ [strongSelf.delegate appClient:strongSelf didError:error];
+ completionHandler(nil);
+ return;
+ }
+ ARDRegisterResponse *response =
+ [ARDRegisterResponse responseFromJSONData:data];
+ completionHandler(response);
+ }];
+}
+
+- (void)sendSignalingMessageToRoomServer:(ARDSignalingMessage *)message
+ completionHandler:(void (^)(ARDMessageResponse *))completionHandler {
+ NSData *data = [message JSONData];
+ NSString *urlString =
+ [NSString stringWithFormat:
+ kARDRoomServerMessageFormat, _roomId, _clientId];
+ NSURL *url = [NSURL URLWithString:urlString];
+ NSLog(@"C->RS POST: %@", message);
+ __weak ARDAppClient *weakSelf = self;
+ [NSURLConnection sendAsyncPostToURL:url
+ withData:data
+ completionHandler:^(BOOL succeeded, NSData *data) {
+ ARDAppClient *strongSelf = weakSelf;
+ if (!succeeded) {
+ NSError *error = [self roomServerNetworkError];
+ [strongSelf.delegate appClient:strongSelf didError:error];
+ return;
+ }
+ ARDMessageResponse *response =
+ [ARDMessageResponse responseFromJSONData:data];
+ NSError *error = nil;
+ switch (response.result) {
+ case kARDMessageResultTypeSuccess:
+ break;
+ case kARDMessageResultTypeUnknown:
+ error =
+ [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+ code:kARDAppClientErrorUnknown
+ userInfo:@{
+ NSLocalizedDescriptionKey: @"Unknown error.",
+ }];
+ case kARDMessageResultTypeInvalidClient:
+ error =
+ [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+ code:kARDAppClientErrorInvalidClient
+ userInfo:@{
+ NSLocalizedDescriptionKey: @"Invalid client.",
+ }];
+ break;
+ case kARDMessageResultTypeInvalidRoom:
+ error =
+ [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+ code:kARDAppClientErrorInvalidRoom
+ userInfo:@{
+ NSLocalizedDescriptionKey: @"Invalid room.",
+ }];
+ break;
+ };
+ if (error) {
+ [strongSelf.delegate appClient:strongSelf didError:error];
+ }
+ if (completionHandler) {
+ completionHandler(response);
+ }
+ }];
+}
+
+- (void)unregisterWithRoomServer {
+ NSString *urlString =
+ [NSString stringWithFormat:kARDRoomServerByeFormat, _roomId, _clientId];
+ NSURL *url = [NSURL URLWithString:urlString];
+ NSURLRequest *request = [NSURLRequest requestWithURL:url];
+ NSURLResponse *response = nil;
+ // We want a synchronous request so that we know that we're unregistered from
+ // room server before we do any further unregistration.
+ NSLog(@"C->RS: BYE");
+ NSError *error = nil;
+ [NSURLConnection sendSynchronousRequest:request
+ returningResponse:&response
+ error:&error];
+ if (error) {
+ NSLog(@"Error unregistering from room server: %@", error);
+ }
+ NSLog(@"Unregistered from room server.");
+}
+
+- (NSError *)roomServerNetworkError {
+ NSError *error =
+ [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
+ code:kARDAppClientErrorNetwork
+ userInfo:@{
+ NSLocalizedDescriptionKey: @"Room server network error",
+ }];
+ return error;
+}
+
+#pragma mark - Collider methods
+
+- (void)registerWithColliderIfReady {
+ if (!self.isRegisteredWithRoomServer) {
+ return;
+ }
+ // Open WebSocket connection.
+ _channel =
+ [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
+ restURL:_websocketRestURL
+ delegate:self];
+ [_channel registerForRoomId:_roomId clientId:_clientId];
+}
+
+- (void)sendSignalingMessageToCollider:(ARDSignalingMessage *)message {
+ NSData *data = [message JSONData];
+ [_channel sendData:data];
+}
+
+#pragma mark - Defaults
+
+- (RTCMediaConstraints *)defaultMediaStreamConstraints {
+ RTCMediaConstraints* constraints =
+ [[RTCMediaConstraints alloc]
+ initWithMandatoryConstraints:nil
+ optionalConstraints:nil];
+ return constraints;
+}
+
+- (RTCMediaConstraints *)defaultAnswerConstraints {
+ return [self defaultOfferConstraints];
+}
+
+- (RTCMediaConstraints *)defaultOfferConstraints {
+ NSArray *mandatoryConstraints = @[
+ [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"],
+ [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"]
+ ];
+ RTCMediaConstraints* constraints =
+ [[RTCMediaConstraints alloc]
+ initWithMandatoryConstraints:mandatoryConstraints
+ optionalConstraints:nil];
+ return constraints;
+}
+
+- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
+ NSArray *optionalConstraints = @[
+ [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"]
+ ];
+ RTCMediaConstraints* constraints =
+ [[RTCMediaConstraints alloc]
+ initWithMandatoryConstraints:nil
+ optionalConstraints:optionalConstraints];
+ return constraints;
+}
+
+- (RTCICEServer *)defaultSTUNServer {
+ NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl];
+ return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL
+ username:@""
+ password:@""];
+}
+
+@end