blob: ef21ab617b7efdc279a465b0a477de4d3e8fd12e [file] [log] [blame]
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00001/*
Donald E Curtisa8736442015-08-05 15:48:13 -07002 * Copyright 2014 The WebRTC Project Authors. All rights reserved.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00003 *
Donald E Curtisa8736442015-08-05 15:48:13 -07004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00009 */
10
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000011#import "ARDAppClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000012
tkchin9eeb6242016-04-27 01:54:20 -070013#import "WebRTC/RTCAVFoundationVideoSource.h"
tkchin9eeb6242016-04-27 01:54:20 -070014#import "WebRTC/RTCAudioTrack.h"
15#import "WebRTC/RTCConfiguration.h"
16#import "WebRTC/RTCFileLogger.h"
17#import "WebRTC/RTCIceServer.h"
18#import "WebRTC/RTCLogging.h"
19#import "WebRTC/RTCMediaConstraints.h"
20#import "WebRTC/RTCMediaStream.h"
21#import "WebRTC/RTCPeerConnectionFactory.h"
skvladf3569c82016-04-29 15:30:16 -070022#import "WebRTC/RTCRtpSender.h"
tkchin204177f2016-06-14 15:03:11 -070023#import "WebRTC/RTCTracing.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000024
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000025#import "ARDAppEngineClient.h"
kthelgasoncc882af2017-01-13 05:59:46 -080026#import "ARDTURNClient+Internal.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000027#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000028#import "ARDMessageResponse.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070029#import "ARDSDPUtils.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000030#import "ARDSignalingMessage.h"
31#import "ARDUtilities.h"
32#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070033#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000034#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070035
kthelgasoncc882af2017-01-13 05:59:46 -080036static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000037
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000038static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
39static NSInteger const kARDAppClientErrorUnknown = -1;
40static NSInteger const kARDAppClientErrorRoomFull = -2;
41static NSInteger const kARDAppClientErrorCreateSDP = -3;
42static NSInteger const kARDAppClientErrorSetSDP = -4;
43static NSInteger const kARDAppClientErrorInvalidClient = -5;
44static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070045static NSString * const kARDMediaStreamId = @"ARDAMS";
46static NSString * const kARDAudioTrackId = @"ARDAMSa0";
47static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080048static NSString * const kARDVideoTrackKind = @"video";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000049
tkchin204177f2016-06-14 15:03:11 -070050// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080051static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070052static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070053static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070054static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
denicija9af2b602016-11-17 00:43:43 -080055static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080056
Zeke Chind3325802015-08-14 11:00:02 -070057// We need a proxy to NSTimer because it causes a strong retain cycle. When
58// using the proxy, |invalidate| must be called before it properly deallocs.
59@interface ARDTimerProxy : NSObject
60
61- (instancetype)initWithInterval:(NSTimeInterval)interval
62 repeats:(BOOL)repeats
63 timerHandler:(void (^)(void))timerHandler;
64- (void)invalidate;
65
66@end
67
68@implementation ARDTimerProxy {
69 NSTimer *_timer;
70 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070071}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000072
Zeke Chind3325802015-08-14 11:00:02 -070073- (instancetype)initWithInterval:(NSTimeInterval)interval
74 repeats:(BOOL)repeats
75 timerHandler:(void (^)(void))timerHandler {
76 NSParameterAssert(timerHandler);
77 if (self = [super init]) {
78 _timerHandler = timerHandler;
79 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
80 target:self
81 selector:@selector(timerDidFire:)
82 userInfo:nil
83 repeats:repeats];
84 }
85 return self;
86}
87
88- (void)invalidate {
89 [_timer invalidate];
90}
91
92- (void)timerDidFire:(NSTimer *)timer {
93 _timerHandler();
94}
95
96@end
97
98@implementation ARDAppClient {
99 RTCFileLogger *_fileLogger;
100 ARDTimerProxy *_statsTimer;
denicijad17d5362016-11-02 02:56:09 -0700101 RTCMediaConstraints *_cameraConstraints;
denicija8c375de2016-11-08 06:28:17 -0800102 NSNumber *_maxBitrate;
Zeke Chind3325802015-08-14 11:00:02 -0700103}
104
105@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000106@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700107@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000108@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000109@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700110@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000111@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000112@synthesize peerConnection = _peerConnection;
113@synthesize factory = _factory;
114@synthesize messageQueue = _messageQueue;
115@synthesize isTurnComplete = _isTurnComplete;
116@synthesize hasReceivedSdp = _hasReceivedSdp;
117@synthesize roomId = _roomId;
118@synthesize clientId = _clientId;
119@synthesize isInitiator = _isInitiator;
120@synthesize iceServers = _iceServers;
121@synthesize webSocketURL = _websocketURL;
122@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000123@synthesize defaultPeerConnectionConstraints =
124 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700125@synthesize isLoopback = _isLoopback;
126@synthesize isAudioOnly = _isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700127@synthesize shouldMakeAecDump = _shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700128@synthesize shouldUseLevelControl = _shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000129
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000130- (instancetype)init {
kthelgasoncc882af2017-01-13 05:59:46 -0800131 return [self initWithDelegate:nil];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000132}
133
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000134- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
135 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000136 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000137 _delegate = delegate;
kthelgasoncc882af2017-01-13 05:59:46 -0800138 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
139 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000140 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000141 }
142 return self;
143}
144
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000145// TODO(tkchin): Provide signaling channel factory interface so we can recreate
146// channel if we need to on network failure. Also, make this the default public
147// constructor.
148- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
149 signalingChannel:(id<ARDSignalingChannel>)channel
150 turnClient:(id<ARDTURNClient>)turnClient
151 delegate:(id<ARDAppClientDelegate>)delegate {
152 NSParameterAssert(rsClient);
153 NSParameterAssert(channel);
154 NSParameterAssert(turnClient);
155 if (self = [super init]) {
156 _roomServerClient = rsClient;
157 _channel = channel;
158 _turnClient = turnClient;
159 _delegate = delegate;
160 [self configure];
161 }
162 return self;
163}
164
165- (void)configure {
166 _factory = [[RTCPeerConnectionFactory alloc] init];
167 _messageQueue = [NSMutableArray array];
kthelgasoncc882af2017-01-13 05:59:46 -0800168 _iceServers = [NSMutableArray array];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700169 _fileLogger = [[RTCFileLogger alloc] init];
170 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000171}
172
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000173- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700174 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000175 [self disconnect];
176}
177
Zeke Chind3325802015-08-14 11:00:02 -0700178- (void)setShouldGetStats:(BOOL)shouldGetStats {
179 if (_shouldGetStats == shouldGetStats) {
180 return;
181 }
182 if (shouldGetStats) {
183 __weak ARDAppClient *weakSelf = self;
184 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
185 repeats:YES
186 timerHandler:^{
187 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700188 [strongSelf.peerConnection statsForTrack:nil
189 statsOutputLevel:RTCStatsOutputLevelDebug
190 completionHandler:^(NSArray *stats) {
191 dispatch_async(dispatch_get_main_queue(), ^{
192 ARDAppClient *strongSelf = weakSelf;
193 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
194 });
195 }];
Zeke Chind3325802015-08-14 11:00:02 -0700196 }];
197 } else {
198 [_statsTimer invalidate];
199 _statsTimer = nil;
200 }
201 _shouldGetStats = shouldGetStats;
202}
203
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000204- (void)setState:(ARDAppClientState)state {
205 if (_state == state) {
206 return;
207 }
208 _state = state;
209 [_delegate appClient:self didChangeState:_state];
210}
211
212- (void)connectToRoomWithId:(NSString *)roomId
haysc913e6452015-10-02 11:44:03 -0700213 isLoopback:(BOOL)isLoopback
peah5085b0c2016-08-25 22:15:14 -0700214 isAudioOnly:(BOOL)isAudioOnly
tkchinab1293a2016-08-30 12:35:05 -0700215 shouldMakeAecDump:(BOOL)shouldMakeAecDump
216 shouldUseLevelControl:(BOOL)shouldUseLevelControl {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000217 NSParameterAssert(roomId.length);
218 NSParameterAssert(_state == kARDAppClientStateDisconnected);
haysc913e6452015-10-02 11:44:03 -0700219 _isLoopback = isLoopback;
220 _isAudioOnly = isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700221 _shouldMakeAecDump = shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700222 _shouldUseLevelControl = shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000223 self.state = kARDAppClientStateConnecting;
224
tkchind1fb26d2016-02-03 01:51:18 -0800225#if defined(WEBRTC_IOS)
226 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700227 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800228 RTCStartInternalCapture(filePath);
229 }
230#endif
231
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000232 // Request TURN.
233 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000234 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
235 NSError *error) {
236 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700237 RTCLogError("Error retrieving TURN servers: %@",
238 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000239 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000240 ARDAppClient *strongSelf = weakSelf;
241 [strongSelf.iceServers addObjectsFromArray:turnServers];
242 strongSelf.isTurnComplete = YES;
243 [strongSelf startSignalingIfReady];
244 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000245
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000246 // Join room on room server.
247 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700248 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000249 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000250 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000251 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000252 [strongSelf.delegate appClient:strongSelf didError:error];
253 return;
254 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000255 NSError *joinError =
256 [[strongSelf class] errorForJoinResultType:response.result];
257 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700258 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000259 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000260 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000261 return;
262 }
tkchinc3f46a92015-07-23 12:50:55 -0700263 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000264 strongSelf.roomId = response.roomId;
265 strongSelf.clientId = response.clientId;
266 strongSelf.isInitiator = response.isInitiator;
267 for (ARDSignalingMessage *message in response.messages) {
268 if (message.type == kARDSignalingMessageTypeOffer ||
269 message.type == kARDSignalingMessageTypeAnswer) {
270 strongSelf.hasReceivedSdp = YES;
271 [strongSelf.messageQueue insertObject:message atIndex:0];
272 } else {
273 [strongSelf.messageQueue addObject:message];
274 }
275 }
276 strongSelf.webSocketURL = response.webSocketURL;
277 strongSelf.webSocketRestURL = response.webSocketRestURL;
278 [strongSelf registerWithColliderIfReady];
279 [strongSelf startSignalingIfReady];
280 }];
281}
282
283- (void)disconnect {
284 if (_state == kARDAppClientStateDisconnected) {
285 return;
286 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000287 if (self.hasJoinedRoomServerRoom) {
288 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000289 clientId:_clientId
290 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000291 }
292 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000293 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000294 // Tell the other client we're hanging up.
295 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000296 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000297 }
298 // Disconnect from collider.
299 _channel = nil;
300 }
301 _clientId = nil;
302 _roomId = nil;
303 _isInitiator = NO;
304 _hasReceivedSdp = NO;
305 _messageQueue = [NSMutableArray array];
ivoc14d5dbe2016-07-04 07:06:55 -0700306#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700307 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700308 [_peerConnection stopRtcEventLog];
309#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000310 _peerConnection = nil;
311 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800312#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800313 if (kARDAppClientEnableTracing) {
314 RTCStopInternalCapture();
315 }
tkchind1fb26d2016-02-03 01:51:18 -0800316#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000317}
318
denicijad17d5362016-11-02 02:56:09 -0700319- (void)setCameraConstraints:(RTCMediaConstraints *)mediaConstraints {
320 _cameraConstraints = mediaConstraints;
321}
322
denicija8c375de2016-11-08 06:28:17 -0800323- (void)setMaxBitrate:(NSNumber *)maxBitrate {
324 _maxBitrate = maxBitrate;
325}
326
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000327#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000328
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000329- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000330 didReceiveMessage:(ARDSignalingMessage *)message {
331 switch (message.type) {
332 case kARDSignalingMessageTypeOffer:
333 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000334 // Offers and answers must be processed before any other message, so we
335 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000336 _hasReceivedSdp = YES;
337 [_messageQueue insertObject:message atIndex:0];
338 break;
339 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700340 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000341 [_messageQueue addObject:message];
342 break;
343 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000344 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000345 [self processSignalingMessage:message];
346 return;
347 }
348 [self drainMessageQueueIfReady];
349}
350
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000351- (void)channel:(id<ARDSignalingChannel>)channel
352 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000353 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000354 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000355 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000356 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000357 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000358 case kARDSignalingChannelStateClosed:
359 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000360 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
361 // completely if the websocket connection fails.
362 [self disconnect];
363 break;
364 }
365}
366
367#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000368// Callbacks for this delegate occur on non-main thread and need to be
369// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000370
371- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700372 didChangeSignalingState:(RTCSignalingState)stateChanged {
373 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000374}
375
376- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700377 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000378 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700379 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000380 (unsigned long)stream.videoTracks.count,
381 (unsigned long)stream.audioTracks.count);
382 if (stream.videoTracks.count) {
383 RTCVideoTrack *videoTrack = stream.videoTracks[0];
384 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
385 }
386 });
387}
388
389- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700390 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700391 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000392}
393
hjon79858f82016-03-13 22:08:26 -0700394- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700395 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000396}
397
398- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700399 didChangeIceConnectionState:(RTCIceConnectionState)newState {
400 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000401 dispatch_async(dispatch_get_main_queue(), ^{
402 [_delegate appClient:self didChangeConnectionState:newState];
403 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000404}
405
406- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700407 didChangeIceGatheringState:(RTCIceGatheringState)newState {
408 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000409}
410
411- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700412 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000413 dispatch_async(dispatch_get_main_queue(), ^{
414 ARDICECandidateMessage *message =
415 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
416 [self sendSignalingMessage:message];
417 });
418}
419
Zeke Chind3325802015-08-14 11:00:02 -0700420- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700421 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
422 dispatch_async(dispatch_get_main_queue(), ^{
423 ARDICECandidateRemovalMessage *message =
424 [[ARDICECandidateRemovalMessage alloc]
425 initWithRemovedCandidates:candidates];
426 [self sendSignalingMessage:message];
427 });
428}
429
430- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700431 didOpenDataChannel:(RTCDataChannel *)dataChannel {
432}
433
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000434#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000435// Callbacks for this delegate occur on non-main thread and need to be
436// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000437
438- (void)peerConnection:(RTCPeerConnection *)peerConnection
439 didCreateSessionDescription:(RTCSessionDescription *)sdp
440 error:(NSError *)error {
441 dispatch_async(dispatch_get_main_queue(), ^{
442 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700443 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000444 [self disconnect];
445 NSDictionary *userInfo = @{
446 NSLocalizedDescriptionKey: @"Failed to create session description.",
447 };
448 NSError *sdpError =
449 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
450 code:kARDAppClientErrorCreateSDP
451 userInfo:userInfo];
452 [_delegate appClient:self didError:sdpError];
453 return;
454 }
Zeke Chin71f6f442015-06-29 14:34:58 -0700455 // Prefer H264 if available.
456 RTCSessionDescription *sdpPreferringH264 =
457 [ARDSDPUtils descriptionForDescription:sdp
458 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700459 __weak ARDAppClient *weakSelf = self;
460 [_peerConnection setLocalDescription:sdpPreferringH264
461 completionHandler:^(NSError *error) {
462 ARDAppClient *strongSelf = weakSelf;
463 [strongSelf peerConnection:strongSelf.peerConnection
464 didSetSessionDescriptionWithError:error];
465 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000466 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700467 [[ARDSessionDescriptionMessage alloc]
468 initWithDescription:sdpPreferringH264];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000469 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800470 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000471 });
472}
473
474- (void)peerConnection:(RTCPeerConnection *)peerConnection
475 didSetSessionDescriptionWithError:(NSError *)error {
476 dispatch_async(dispatch_get_main_queue(), ^{
477 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700478 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000479 [self disconnect];
480 NSDictionary *userInfo = @{
481 NSLocalizedDescriptionKey: @"Failed to set session description.",
482 };
483 NSError *sdpError =
484 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
485 code:kARDAppClientErrorSetSDP
486 userInfo:userInfo];
487 [_delegate appClient:self didError:sdpError];
488 return;
489 }
490 // If we're answering and we've just set the remote offer we need to create
491 // an answer and set the local description.
492 if (!_isInitiator && !_peerConnection.localDescription) {
493 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700494 __weak ARDAppClient *weakSelf = self;
495 [_peerConnection answerForConstraints:constraints
496 completionHandler:^(RTCSessionDescription *sdp,
497 NSError *error) {
498 ARDAppClient *strongSelf = weakSelf;
499 [strongSelf peerConnection:strongSelf.peerConnection
500 didCreateSessionDescription:sdp
501 error:error];
502 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000503 }
504 });
505}
506
507#pragma mark - Private
508
tkchin204177f2016-06-14 15:03:11 -0700509#if defined(WEBRTC_IOS)
510
511- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
512 NSParameterAssert(fileName.length);
513 NSArray *paths = NSSearchPathForDirectoriesInDomains(
514 NSDocumentDirectory, NSUserDomainMask, YES);
515 NSString *documentsDirPath = paths.firstObject;
516 NSString *filePath =
517 [documentsDirPath stringByAppendingPathComponent:fileName];
518 return filePath;
519}
520
521#endif
522
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000523- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000524 return _clientId.length;
525}
526
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000527// Begins the peer connection connection process if we have both joined a room
528// on the room server and tried to obtain a TURN server. Otherwise does nothing.
529// A peer connection object will be created with a stream that contains local
530// audio and video capture. If this client is the caller, an offer is created as
531// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000532- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000533 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000534 return;
535 }
536 self.state = kARDAppClientStateConnected;
537
538 // Create peer connection.
539 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700540 RTCConfiguration *config = [[RTCConfiguration alloc] init];
541 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700542 _peerConnection = [_factory peerConnectionWithConfiguration:config
543 constraints:constraints
544 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700545 // Create AV senders.
546 [self createAudioSender];
547 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000548 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000549 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700550 __weak ARDAppClient *weakSelf = self;
551 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
552 completionHandler:^(RTCSessionDescription *sdp,
553 NSError *error) {
554 ARDAppClient *strongSelf = weakSelf;
555 [strongSelf peerConnection:strongSelf.peerConnection
556 didCreateSessionDescription:sdp
557 error:error];
558 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000559 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000560 // Check if we've received an offer.
561 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000562 }
ivoc14d5dbe2016-07-04 07:06:55 -0700563#if defined(WEBRTC_IOS)
564 // Start event log.
565 if (kARDAppClientEnableRtcEventLog) {
566 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
567 if (![_peerConnection startRtcEventLogWithFilePath:filePath
568 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
569 RTCLogError(@"Failed to start event logging.");
570 }
571 }
peah5085b0c2016-08-25 22:15:14 -0700572
573 // Start aecdump diagnostic recording.
574 if (_shouldMakeAecDump) {
tkchinfce0e2c2016-08-30 12:58:11 -0700575 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
576 if (![_factory startAecDumpWithFilePath:filePath
577 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
578 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700579 }
580 }
ivoc14d5dbe2016-07-04 07:06:55 -0700581#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000582}
583
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000584// Processes the messages that we've received from the room server and the
585// signaling channel. The offer or answer message must be processed before other
586// signaling messages, however they can arrive out of order. Hence, this method
587// only processes pending messages if there is a peer connection object and
588// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000589- (void)drainMessageQueueIfReady {
590 if (!_peerConnection || !_hasReceivedSdp) {
591 return;
592 }
593 for (ARDSignalingMessage *message in _messageQueue) {
594 [self processSignalingMessage:message];
595 }
596 [_messageQueue removeAllObjects];
597}
598
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000599// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000600- (void)processSignalingMessage:(ARDSignalingMessage *)message {
601 NSParameterAssert(_peerConnection ||
602 message.type == kARDSignalingMessageTypeBye);
603 switch (message.type) {
604 case kARDSignalingMessageTypeOffer:
605 case kARDSignalingMessageTypeAnswer: {
606 ARDSessionDescriptionMessage *sdpMessage =
607 (ARDSessionDescriptionMessage *)message;
608 RTCSessionDescription *description = sdpMessage.sessionDescription;
Zeke Chin71f6f442015-06-29 14:34:58 -0700609 // Prefer H264 if available.
610 RTCSessionDescription *sdpPreferringH264 =
611 [ARDSDPUtils descriptionForDescription:description
612 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700613 __weak ARDAppClient *weakSelf = self;
614 [_peerConnection setRemoteDescription:sdpPreferringH264
615 completionHandler:^(NSError *error) {
616 ARDAppClient *strongSelf = weakSelf;
617 [strongSelf peerConnection:strongSelf.peerConnection
618 didSetSessionDescriptionWithError:error];
619 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000620 break;
621 }
622 case kARDSignalingMessageTypeCandidate: {
623 ARDICECandidateMessage *candidateMessage =
624 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700625 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000626 break;
627 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700628 case kARDSignalingMessageTypeCandidateRemoval: {
629 ARDICECandidateRemovalMessage *candidateMessage =
630 (ARDICECandidateRemovalMessage *)message;
631 [_peerConnection removeIceCandidates:candidateMessage.candidates];
632 break;
633 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000634 case kARDSignalingMessageTypeBye:
635 // Other client disconnected.
636 // TODO(tkchin): support waiting in room for next client. For now just
637 // disconnect.
638 [self disconnect];
639 break;
640 }
641}
642
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000643// Sends a signaling message to the other client. The caller will send messages
644// through the room server, whereas the callee will send messages over the
645// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000646- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
647 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000648 __weak ARDAppClient *weakSelf = self;
649 [_roomServerClient sendMessage:message
650 forRoomId:_roomId
651 clientId:_clientId
652 completionHandler:^(ARDMessageResponse *response,
653 NSError *error) {
654 ARDAppClient *strongSelf = weakSelf;
655 if (error) {
656 [strongSelf.delegate appClient:strongSelf didError:error];
657 return;
658 }
659 NSError *messageError =
660 [[strongSelf class] errorForMessageResultType:response.result];
661 if (messageError) {
662 [strongSelf.delegate appClient:strongSelf didError:messageError];
663 return;
664 }
665 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000666 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000667 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000668 }
669}
670
skvladf3569c82016-04-29 15:30:16 -0700671- (RTCRtpSender *)createVideoSender {
672 RTCRtpSender *sender =
673 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
674 streamId:kARDMediaStreamId];
675 RTCVideoTrack *track = [self createLocalVideoTrack];
676 if (track) {
677 sender.track = track;
678 [_delegate appClient:self didReceiveLocalVideoTrack:track];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700679 }
denicija9af2b602016-11-17 00:43:43 -0800680
skvladf3569c82016-04-29 15:30:16 -0700681 return sender;
682}
683
denicija9af2b602016-11-17 00:43:43 -0800684- (void)setMaxBitrateForPeerConnectionVideoSender {
685 for (RTCRtpSender *sender in _peerConnection.senders) {
686 if (sender.track != nil) {
687 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
688 [self setMaxBitrate:_maxBitrate forVideoSender:sender];
689 }
690 }
denicija8c375de2016-11-08 06:28:17 -0800691 }
692}
693
denicija9af2b602016-11-17 00:43:43 -0800694- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
695 if (maxBitrate.intValue <= 0) {
696 return;
697 }
698
699 RTCRtpParameters *parametersToModify = sender.parameters;
700 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
701 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
702 }
703 [sender setParameters:parametersToModify];
704}
705
skvladf3569c82016-04-29 15:30:16 -0700706- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700707 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
708 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
709 RTCAudioTrack *track = [_factory audioTrackWithSource:source
710 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700711 RTCRtpSender *sender =
712 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
713 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700714 sender.track = track;
715 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700716}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000717
Zeke Chin57cc74e2015-05-05 07:52:31 -0700718- (RTCVideoTrack *)createLocalVideoTrack {
719 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000720 // The iOS simulator doesn't provide any sort of camera capture
721 // support or emulation (http://goo.gl/rHAnC1) so don't bother
722 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700723#if !TARGET_IPHONE_SIMULATOR
haysc913e6452015-10-02 11:44:03 -0700724 if (!_isAudioOnly) {
denicijad17d5362016-11-02 02:56:09 -0700725 RTCMediaConstraints *cameraConstraints =
726 [self cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700727 RTCAVFoundationVideoSource *source =
denicijad17d5362016-11-02 02:56:09 -0700728 [_factory avFoundationVideoSourceWithConstraints:cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700729 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700730 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700731 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700732 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000733#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700734 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000735}
736
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000737#pragma mark - Collider methods
738
739- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000740 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000741 return;
742 }
743 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000744 if (!_channel) {
745 _channel =
746 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
747 restURL:_websocketRestURL
748 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700749 if (_isLoopback) {
750 _loopbackChannel =
751 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
752 restURL:_websocketRestURL];
753 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000754 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000755 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700756 if (_isLoopback) {
757 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
758 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000759}
760
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000761#pragma mark - Defaults
762
tkchinab1293a2016-08-30 12:35:05 -0700763 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
764 NSString *valueLevelControl = _shouldUseLevelControl ?
765 kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
766 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700767 RTCMediaConstraints *constraints =
768 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
769 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700770 return constraints;
771}
772
denicijad17d5362016-11-02 02:56:09 -0700773- (RTCMediaConstraints *)cameraConstraints {
774 return _cameraConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000775}
776
777- (RTCMediaConstraints *)defaultAnswerConstraints {
778 return [self defaultOfferConstraints];
779}
780
781- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700782 NSDictionary *mandatoryConstraints = @{
783 @"OfferToReceiveAudio" : @"true",
784 @"OfferToReceiveVideo" : @"true"
785 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000786 RTCMediaConstraints* constraints =
787 [[RTCMediaConstraints alloc]
788 initWithMandatoryConstraints:mandatoryConstraints
789 optionalConstraints:nil];
790 return constraints;
791}
792
793- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000794 if (_defaultPeerConnectionConstraints) {
795 return _defaultPeerConnectionConstraints;
796 }
haysc913e6452015-10-02 11:44:03 -0700797 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700798 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000799 RTCMediaConstraints* constraints =
800 [[RTCMediaConstraints alloc]
801 initWithMandatoryConstraints:nil
802 optionalConstraints:optionalConstraints];
803 return constraints;
804}
805
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000806#pragma mark - Errors
807
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000808+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000809 NSError *error = nil;
810 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000811 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000812 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000813 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000814 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
815 code:kARDAppClientErrorUnknown
816 userInfo:@{
817 NSLocalizedDescriptionKey: @"Unknown error.",
818 }];
819 break;
820 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000821 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000822 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
823 code:kARDAppClientErrorRoomFull
824 userInfo:@{
825 NSLocalizedDescriptionKey: @"Room is full.",
826 }];
827 break;
828 }
829 }
830 return error;
831}
832
833+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
834 NSError *error = nil;
835 switch (resultType) {
836 case kARDMessageResultTypeSuccess:
837 break;
838 case kARDMessageResultTypeUnknown:
839 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
840 code:kARDAppClientErrorUnknown
841 userInfo:@{
842 NSLocalizedDescriptionKey: @"Unknown error.",
843 }];
844 break;
845 case kARDMessageResultTypeInvalidClient:
846 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
847 code:kARDAppClientErrorInvalidClient
848 userInfo:@{
849 NSLocalizedDescriptionKey: @"Invalid client.",
850 }];
851 break;
852 case kARDMessageResultTypeInvalidRoom:
853 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
854 code:kARDAppClientErrorInvalidRoom
855 userInfo:@{
856 NSLocalizedDescriptionKey: @"Invalid room.",
857 }];
858 break;
859 }
860 return error;
861}
862
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000863@end