blob: cfcb3565483c9f9e04a8c1f49232b0aaf74b4fd3 [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"
26#import "ARDCEODTURNClient.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
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000036static NSString * const kARDDefaultSTUNServerUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000037 @"stun:stun.l.google.com:19302";
38// TODO(tkchin): figure out a better username for CEOD statistics.
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000039static NSString * const kARDTurnRequestUrl =
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000040 @"https://computeengineondemand.appspot.com"
41 @"/turn?username=iapprtc&key=4080218913";
42
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000043static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
44static NSInteger const kARDAppClientErrorUnknown = -1;
45static NSInteger const kARDAppClientErrorRoomFull = -2;
46static NSInteger const kARDAppClientErrorCreateSDP = -3;
47static NSInteger const kARDAppClientErrorSetSDP = -4;
48static NSInteger const kARDAppClientErrorInvalidClient = -5;
49static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070050static NSString * const kARDMediaStreamId = @"ARDAMS";
51static NSString * const kARDAudioTrackId = @"ARDAMSa0";
52static NSString * const kARDVideoTrackId = @"ARDAMSv0";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000053
tkchin204177f2016-06-14 15:03:11 -070054// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080055static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070056static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070057static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070058static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
tkchind1fb26d2016-02-03 01:51:18 -080059
Zeke Chind3325802015-08-14 11:00:02 -070060// We need a proxy to NSTimer because it causes a strong retain cycle. When
61// using the proxy, |invalidate| must be called before it properly deallocs.
62@interface ARDTimerProxy : NSObject
63
64- (instancetype)initWithInterval:(NSTimeInterval)interval
65 repeats:(BOOL)repeats
66 timerHandler:(void (^)(void))timerHandler;
67- (void)invalidate;
68
69@end
70
71@implementation ARDTimerProxy {
72 NSTimer *_timer;
73 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070074}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000075
Zeke Chind3325802015-08-14 11:00:02 -070076- (instancetype)initWithInterval:(NSTimeInterval)interval
77 repeats:(BOOL)repeats
78 timerHandler:(void (^)(void))timerHandler {
79 NSParameterAssert(timerHandler);
80 if (self = [super init]) {
81 _timerHandler = timerHandler;
82 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
83 target:self
84 selector:@selector(timerDidFire:)
85 userInfo:nil
86 repeats:repeats];
87 }
88 return self;
89}
90
91- (void)invalidate {
92 [_timer invalidate];
93}
94
95- (void)timerDidFire:(NSTimer *)timer {
96 _timerHandler();
97}
98
99@end
100
101@implementation ARDAppClient {
102 RTCFileLogger *_fileLogger;
103 ARDTimerProxy *_statsTimer;
denicijad17d5362016-11-02 02:56:09 -0700104 RTCMediaConstraints *_cameraConstraints;
denicija8c375de2016-11-08 06:28:17 -0800105 NSNumber *_maxBitrate;
Zeke Chind3325802015-08-14 11:00:02 -0700106}
107
108@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000109@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700110@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000111@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000112@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700113@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000114@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000115@synthesize peerConnection = _peerConnection;
116@synthesize factory = _factory;
117@synthesize messageQueue = _messageQueue;
118@synthesize isTurnComplete = _isTurnComplete;
119@synthesize hasReceivedSdp = _hasReceivedSdp;
120@synthesize roomId = _roomId;
121@synthesize clientId = _clientId;
122@synthesize isInitiator = _isInitiator;
123@synthesize iceServers = _iceServers;
124@synthesize webSocketURL = _websocketURL;
125@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000126@synthesize defaultPeerConnectionConstraints =
127 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700128@synthesize isLoopback = _isLoopback;
129@synthesize isAudioOnly = _isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700130@synthesize shouldMakeAecDump = _shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700131@synthesize shouldUseLevelControl = _shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000132
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000133- (instancetype)init {
134 if (self = [super init]) {
135 _roomServerClient = [[ARDAppEngineClient alloc] init];
136 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
137 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
138 [self configure];
139 }
140 return self;
141}
142
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000143- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
144 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000145 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000146 _delegate = delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000147 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
148 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
149 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000150 }
151 return self;
152}
153
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000154// TODO(tkchin): Provide signaling channel factory interface so we can recreate
155// channel if we need to on network failure. Also, make this the default public
156// constructor.
157- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
158 signalingChannel:(id<ARDSignalingChannel>)channel
159 turnClient:(id<ARDTURNClient>)turnClient
160 delegate:(id<ARDAppClientDelegate>)delegate {
161 NSParameterAssert(rsClient);
162 NSParameterAssert(channel);
163 NSParameterAssert(turnClient);
164 if (self = [super init]) {
165 _roomServerClient = rsClient;
166 _channel = channel;
167 _turnClient = turnClient;
168 _delegate = delegate;
169 [self configure];
170 }
171 return self;
172}
173
174- (void)configure {
175 _factory = [[RTCPeerConnectionFactory alloc] init];
176 _messageQueue = [NSMutableArray array];
177 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700178 _fileLogger = [[RTCFileLogger alloc] init];
179 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000180}
181
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000182- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700183 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000184 [self disconnect];
185}
186
Zeke Chind3325802015-08-14 11:00:02 -0700187- (void)setShouldGetStats:(BOOL)shouldGetStats {
188 if (_shouldGetStats == shouldGetStats) {
189 return;
190 }
191 if (shouldGetStats) {
192 __weak ARDAppClient *weakSelf = self;
193 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
194 repeats:YES
195 timerHandler:^{
196 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700197 [strongSelf.peerConnection statsForTrack:nil
198 statsOutputLevel:RTCStatsOutputLevelDebug
199 completionHandler:^(NSArray *stats) {
200 dispatch_async(dispatch_get_main_queue(), ^{
201 ARDAppClient *strongSelf = weakSelf;
202 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
203 });
204 }];
Zeke Chind3325802015-08-14 11:00:02 -0700205 }];
206 } else {
207 [_statsTimer invalidate];
208 _statsTimer = nil;
209 }
210 _shouldGetStats = shouldGetStats;
211}
212
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000213- (void)setState:(ARDAppClientState)state {
214 if (_state == state) {
215 return;
216 }
217 _state = state;
218 [_delegate appClient:self didChangeState:_state];
219}
220
221- (void)connectToRoomWithId:(NSString *)roomId
haysc913e6452015-10-02 11:44:03 -0700222 isLoopback:(BOOL)isLoopback
peah5085b0c2016-08-25 22:15:14 -0700223 isAudioOnly:(BOOL)isAudioOnly
tkchinab1293a2016-08-30 12:35:05 -0700224 shouldMakeAecDump:(BOOL)shouldMakeAecDump
225 shouldUseLevelControl:(BOOL)shouldUseLevelControl {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000226 NSParameterAssert(roomId.length);
227 NSParameterAssert(_state == kARDAppClientStateDisconnected);
haysc913e6452015-10-02 11:44:03 -0700228 _isLoopback = isLoopback;
229 _isAudioOnly = isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700230 _shouldMakeAecDump = shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700231 _shouldUseLevelControl = shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000232 self.state = kARDAppClientStateConnecting;
233
tkchind1fb26d2016-02-03 01:51:18 -0800234#if defined(WEBRTC_IOS)
235 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700236 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800237 RTCStartInternalCapture(filePath);
238 }
239#endif
240
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000241 // Request TURN.
242 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000243 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
244 NSError *error) {
245 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700246 RTCLogError("Error retrieving TURN servers: %@",
247 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000248 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000249 ARDAppClient *strongSelf = weakSelf;
250 [strongSelf.iceServers addObjectsFromArray:turnServers];
251 strongSelf.isTurnComplete = YES;
252 [strongSelf startSignalingIfReady];
253 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000254
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000255 // Join room on room server.
256 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700257 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000258 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000259 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000260 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000261 [strongSelf.delegate appClient:strongSelf didError:error];
262 return;
263 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000264 NSError *joinError =
265 [[strongSelf class] errorForJoinResultType:response.result];
266 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700267 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000268 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000269 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000270 return;
271 }
tkchinc3f46a92015-07-23 12:50:55 -0700272 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000273 strongSelf.roomId = response.roomId;
274 strongSelf.clientId = response.clientId;
275 strongSelf.isInitiator = response.isInitiator;
276 for (ARDSignalingMessage *message in response.messages) {
277 if (message.type == kARDSignalingMessageTypeOffer ||
278 message.type == kARDSignalingMessageTypeAnswer) {
279 strongSelf.hasReceivedSdp = YES;
280 [strongSelf.messageQueue insertObject:message atIndex:0];
281 } else {
282 [strongSelf.messageQueue addObject:message];
283 }
284 }
285 strongSelf.webSocketURL = response.webSocketURL;
286 strongSelf.webSocketRestURL = response.webSocketRestURL;
287 [strongSelf registerWithColliderIfReady];
288 [strongSelf startSignalingIfReady];
289 }];
290}
291
292- (void)disconnect {
293 if (_state == kARDAppClientStateDisconnected) {
294 return;
295 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000296 if (self.hasJoinedRoomServerRoom) {
297 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000298 clientId:_clientId
299 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000300 }
301 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000302 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000303 // Tell the other client we're hanging up.
304 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000305 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000306 }
307 // Disconnect from collider.
308 _channel = nil;
309 }
310 _clientId = nil;
311 _roomId = nil;
312 _isInitiator = NO;
313 _hasReceivedSdp = NO;
314 _messageQueue = [NSMutableArray array];
ivoc14d5dbe2016-07-04 07:06:55 -0700315#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700316 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700317 [_peerConnection stopRtcEventLog];
318#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000319 _peerConnection = nil;
320 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800321#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800322 if (kARDAppClientEnableTracing) {
323 RTCStopInternalCapture();
324 }
tkchind1fb26d2016-02-03 01:51:18 -0800325#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000326}
327
denicijad17d5362016-11-02 02:56:09 -0700328- (void)setCameraConstraints:(RTCMediaConstraints *)mediaConstraints {
329 _cameraConstraints = mediaConstraints;
330}
331
denicija8c375de2016-11-08 06:28:17 -0800332- (void)setMaxBitrate:(NSNumber *)maxBitrate {
333 _maxBitrate = maxBitrate;
334}
335
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000336#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000337
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000338- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000339 didReceiveMessage:(ARDSignalingMessage *)message {
340 switch (message.type) {
341 case kARDSignalingMessageTypeOffer:
342 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000343 // Offers and answers must be processed before any other message, so we
344 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000345 _hasReceivedSdp = YES;
346 [_messageQueue insertObject:message atIndex:0];
347 break;
348 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700349 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000350 [_messageQueue addObject:message];
351 break;
352 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000353 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000354 [self processSignalingMessage:message];
355 return;
356 }
357 [self drainMessageQueueIfReady];
358}
359
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000360- (void)channel:(id<ARDSignalingChannel>)channel
361 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000362 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000363 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000364 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000365 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000366 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000367 case kARDSignalingChannelStateClosed:
368 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000369 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
370 // completely if the websocket connection fails.
371 [self disconnect];
372 break;
373 }
374}
375
376#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000377// Callbacks for this delegate occur on non-main thread and need to be
378// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000379
380- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700381 didChangeSignalingState:(RTCSignalingState)stateChanged {
382 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000383}
384
385- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700386 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000387 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700388 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000389 (unsigned long)stream.videoTracks.count,
390 (unsigned long)stream.audioTracks.count);
391 if (stream.videoTracks.count) {
392 RTCVideoTrack *videoTrack = stream.videoTracks[0];
393 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
394 }
395 });
396}
397
398- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700399 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700400 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000401}
402
hjon79858f82016-03-13 22:08:26 -0700403- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700404 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000405}
406
407- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700408 didChangeIceConnectionState:(RTCIceConnectionState)newState {
409 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000410 dispatch_async(dispatch_get_main_queue(), ^{
411 [_delegate appClient:self didChangeConnectionState:newState];
412 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000413}
414
415- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700416 didChangeIceGatheringState:(RTCIceGatheringState)newState {
417 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000418}
419
420- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700421 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000422 dispatch_async(dispatch_get_main_queue(), ^{
423 ARDICECandidateMessage *message =
424 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
425 [self sendSignalingMessage:message];
426 });
427}
428
Zeke Chind3325802015-08-14 11:00:02 -0700429- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700430 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
431 dispatch_async(dispatch_get_main_queue(), ^{
432 ARDICECandidateRemovalMessage *message =
433 [[ARDICECandidateRemovalMessage alloc]
434 initWithRemovedCandidates:candidates];
435 [self sendSignalingMessage:message];
436 });
437}
438
439- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700440 didOpenDataChannel:(RTCDataChannel *)dataChannel {
441}
442
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000443#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000444// Callbacks for this delegate occur on non-main thread and need to be
445// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000446
447- (void)peerConnection:(RTCPeerConnection *)peerConnection
448 didCreateSessionDescription:(RTCSessionDescription *)sdp
449 error:(NSError *)error {
450 dispatch_async(dispatch_get_main_queue(), ^{
451 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700452 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000453 [self disconnect];
454 NSDictionary *userInfo = @{
455 NSLocalizedDescriptionKey: @"Failed to create session description.",
456 };
457 NSError *sdpError =
458 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
459 code:kARDAppClientErrorCreateSDP
460 userInfo:userInfo];
461 [_delegate appClient:self didError:sdpError];
462 return;
463 }
Zeke Chin71f6f442015-06-29 14:34:58 -0700464 // Prefer H264 if available.
465 RTCSessionDescription *sdpPreferringH264 =
466 [ARDSDPUtils descriptionForDescription:sdp
467 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700468 __weak ARDAppClient *weakSelf = self;
469 [_peerConnection setLocalDescription:sdpPreferringH264
470 completionHandler:^(NSError *error) {
471 ARDAppClient *strongSelf = weakSelf;
472 [strongSelf peerConnection:strongSelf.peerConnection
473 didSetSessionDescriptionWithError:error];
474 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000475 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700476 [[ARDSessionDescriptionMessage alloc]
477 initWithDescription:sdpPreferringH264];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000478 [self sendSignalingMessage:message];
479 });
480}
481
482- (void)peerConnection:(RTCPeerConnection *)peerConnection
483 didSetSessionDescriptionWithError:(NSError *)error {
484 dispatch_async(dispatch_get_main_queue(), ^{
485 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700486 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000487 [self disconnect];
488 NSDictionary *userInfo = @{
489 NSLocalizedDescriptionKey: @"Failed to set session description.",
490 };
491 NSError *sdpError =
492 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
493 code:kARDAppClientErrorSetSDP
494 userInfo:userInfo];
495 [_delegate appClient:self didError:sdpError];
496 return;
497 }
498 // If we're answering and we've just set the remote offer we need to create
499 // an answer and set the local description.
500 if (!_isInitiator && !_peerConnection.localDescription) {
501 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700502 __weak ARDAppClient *weakSelf = self;
503 [_peerConnection answerForConstraints:constraints
504 completionHandler:^(RTCSessionDescription *sdp,
505 NSError *error) {
506 ARDAppClient *strongSelf = weakSelf;
507 [strongSelf peerConnection:strongSelf.peerConnection
508 didCreateSessionDescription:sdp
509 error:error];
510 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000511 }
512 });
513}
514
515#pragma mark - Private
516
tkchin204177f2016-06-14 15:03:11 -0700517#if defined(WEBRTC_IOS)
518
519- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
520 NSParameterAssert(fileName.length);
521 NSArray *paths = NSSearchPathForDirectoriesInDomains(
522 NSDocumentDirectory, NSUserDomainMask, YES);
523 NSString *documentsDirPath = paths.firstObject;
524 NSString *filePath =
525 [documentsDirPath stringByAppendingPathComponent:fileName];
526 return filePath;
527}
528
529#endif
530
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000531- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000532 return _clientId.length;
533}
534
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000535// Begins the peer connection connection process if we have both joined a room
536// on the room server and tried to obtain a TURN server. Otherwise does nothing.
537// A peer connection object will be created with a stream that contains local
538// audio and video capture. If this client is the caller, an offer is created as
539// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000540- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000541 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000542 return;
543 }
544 self.state = kARDAppClientStateConnected;
545
546 // Create peer connection.
547 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700548 RTCConfiguration *config = [[RTCConfiguration alloc] init];
549 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700550 _peerConnection = [_factory peerConnectionWithConfiguration:config
551 constraints:constraints
552 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700553 // Create AV senders.
554 [self createAudioSender];
555 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000556 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000557 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700558 __weak ARDAppClient *weakSelf = self;
559 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
560 completionHandler:^(RTCSessionDescription *sdp,
561 NSError *error) {
562 ARDAppClient *strongSelf = weakSelf;
563 [strongSelf peerConnection:strongSelf.peerConnection
564 didCreateSessionDescription:sdp
565 error:error];
566 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000567 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000568 // Check if we've received an offer.
569 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000570 }
ivoc14d5dbe2016-07-04 07:06:55 -0700571#if defined(WEBRTC_IOS)
572 // Start event log.
573 if (kARDAppClientEnableRtcEventLog) {
574 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
575 if (![_peerConnection startRtcEventLogWithFilePath:filePath
576 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
577 RTCLogError(@"Failed to start event logging.");
578 }
579 }
peah5085b0c2016-08-25 22:15:14 -0700580
581 // Start aecdump diagnostic recording.
582 if (_shouldMakeAecDump) {
tkchinfce0e2c2016-08-30 12:58:11 -0700583 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
584 if (![_factory startAecDumpWithFilePath:filePath
585 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
586 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700587 }
588 }
ivoc14d5dbe2016-07-04 07:06:55 -0700589#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000590}
591
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000592// Processes the messages that we've received from the room server and the
593// signaling channel. The offer or answer message must be processed before other
594// signaling messages, however they can arrive out of order. Hence, this method
595// only processes pending messages if there is a peer connection object and
596// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000597- (void)drainMessageQueueIfReady {
598 if (!_peerConnection || !_hasReceivedSdp) {
599 return;
600 }
601 for (ARDSignalingMessage *message in _messageQueue) {
602 [self processSignalingMessage:message];
603 }
604 [_messageQueue removeAllObjects];
605}
606
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000607// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000608- (void)processSignalingMessage:(ARDSignalingMessage *)message {
609 NSParameterAssert(_peerConnection ||
610 message.type == kARDSignalingMessageTypeBye);
611 switch (message.type) {
612 case kARDSignalingMessageTypeOffer:
613 case kARDSignalingMessageTypeAnswer: {
614 ARDSessionDescriptionMessage *sdpMessage =
615 (ARDSessionDescriptionMessage *)message;
616 RTCSessionDescription *description = sdpMessage.sessionDescription;
Zeke Chin71f6f442015-06-29 14:34:58 -0700617 // Prefer H264 if available.
618 RTCSessionDescription *sdpPreferringH264 =
619 [ARDSDPUtils descriptionForDescription:description
620 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700621 __weak ARDAppClient *weakSelf = self;
622 [_peerConnection setRemoteDescription:sdpPreferringH264
623 completionHandler:^(NSError *error) {
624 ARDAppClient *strongSelf = weakSelf;
625 [strongSelf peerConnection:strongSelf.peerConnection
626 didSetSessionDescriptionWithError:error];
627 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000628 break;
629 }
630 case kARDSignalingMessageTypeCandidate: {
631 ARDICECandidateMessage *candidateMessage =
632 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700633 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000634 break;
635 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700636 case kARDSignalingMessageTypeCandidateRemoval: {
637 ARDICECandidateRemovalMessage *candidateMessage =
638 (ARDICECandidateRemovalMessage *)message;
639 [_peerConnection removeIceCandidates:candidateMessage.candidates];
640 break;
641 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000642 case kARDSignalingMessageTypeBye:
643 // Other client disconnected.
644 // TODO(tkchin): support waiting in room for next client. For now just
645 // disconnect.
646 [self disconnect];
647 break;
648 }
649}
650
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000651// Sends a signaling message to the other client. The caller will send messages
652// through the room server, whereas the callee will send messages over the
653// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000654- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
655 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000656 __weak ARDAppClient *weakSelf = self;
657 [_roomServerClient sendMessage:message
658 forRoomId:_roomId
659 clientId:_clientId
660 completionHandler:^(ARDMessageResponse *response,
661 NSError *error) {
662 ARDAppClient *strongSelf = weakSelf;
663 if (error) {
664 [strongSelf.delegate appClient:strongSelf didError:error];
665 return;
666 }
667 NSError *messageError =
668 [[strongSelf class] errorForMessageResultType:response.result];
669 if (messageError) {
670 [strongSelf.delegate appClient:strongSelf didError:messageError];
671 return;
672 }
673 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000674 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000675 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000676 }
677}
678
skvladf3569c82016-04-29 15:30:16 -0700679- (RTCRtpSender *)createVideoSender {
680 RTCRtpSender *sender =
681 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
682 streamId:kARDMediaStreamId];
denicija8c375de2016-11-08 06:28:17 -0800683
684 [self setMaxBitrate:_maxBitrate forVideoSender:sender];
685
skvladf3569c82016-04-29 15:30:16 -0700686 RTCVideoTrack *track = [self createLocalVideoTrack];
687 if (track) {
688 sender.track = track;
689 [_delegate appClient:self didReceiveLocalVideoTrack:track];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700690 }
skvladf3569c82016-04-29 15:30:16 -0700691 return sender;
692}
693
denicija8c375de2016-11-08 06:28:17 -0800694- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
695 for (RTCRtpEncodingParameters *encoding in sender.parameters.encodings) {
696 encoding.maxBitrateBps = maxBitrate;
697 }
698}
699
skvladf3569c82016-04-29 15:30:16 -0700700- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700701 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
702 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
703 RTCAudioTrack *track = [_factory audioTrackWithSource:source
704 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700705 RTCRtpSender *sender =
706 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
707 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700708 sender.track = track;
709 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700710}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000711
Zeke Chin57cc74e2015-05-05 07:52:31 -0700712- (RTCVideoTrack *)createLocalVideoTrack {
713 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000714 // The iOS simulator doesn't provide any sort of camera capture
715 // support or emulation (http://goo.gl/rHAnC1) so don't bother
716 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700717#if !TARGET_IPHONE_SIMULATOR
haysc913e6452015-10-02 11:44:03 -0700718 if (!_isAudioOnly) {
denicijad17d5362016-11-02 02:56:09 -0700719 RTCMediaConstraints *cameraConstraints =
720 [self cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700721 RTCAVFoundationVideoSource *source =
denicijad17d5362016-11-02 02:56:09 -0700722 [_factory avFoundationVideoSourceWithConstraints:cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700723 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700724 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700725 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700726 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000727#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700728 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000729}
730
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000731#pragma mark - Collider methods
732
733- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000734 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000735 return;
736 }
737 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000738 if (!_channel) {
739 _channel =
740 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
741 restURL:_websocketRestURL
742 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700743 if (_isLoopback) {
744 _loopbackChannel =
745 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
746 restURL:_websocketRestURL];
747 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000748 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000749 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700750 if (_isLoopback) {
751 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
752 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000753}
754
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000755#pragma mark - Defaults
756
tkchinab1293a2016-08-30 12:35:05 -0700757 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
758 NSString *valueLevelControl = _shouldUseLevelControl ?
759 kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
760 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700761 RTCMediaConstraints *constraints =
762 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
763 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700764 return constraints;
765}
766
denicijad17d5362016-11-02 02:56:09 -0700767- (RTCMediaConstraints *)cameraConstraints {
768 return _cameraConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000769}
770
771- (RTCMediaConstraints *)defaultAnswerConstraints {
772 return [self defaultOfferConstraints];
773}
774
775- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700776 NSDictionary *mandatoryConstraints = @{
777 @"OfferToReceiveAudio" : @"true",
778 @"OfferToReceiveVideo" : @"true"
779 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000780 RTCMediaConstraints* constraints =
781 [[RTCMediaConstraints alloc]
782 initWithMandatoryConstraints:mandatoryConstraints
783 optionalConstraints:nil];
784 return constraints;
785}
786
787- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000788 if (_defaultPeerConnectionConstraints) {
789 return _defaultPeerConnectionConstraints;
790 }
haysc913e6452015-10-02 11:44:03 -0700791 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700792 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000793 RTCMediaConstraints* constraints =
794 [[RTCMediaConstraints alloc]
795 initWithMandatoryConstraints:nil
796 optionalConstraints:optionalConstraints];
797 return constraints;
798}
799
hjon79858f82016-03-13 22:08:26 -0700800- (RTCIceServer *)defaultSTUNServer {
801 return [[RTCIceServer alloc] initWithURLStrings:@[kARDDefaultSTUNServerUrl]
802 username:@""
803 credential:@""];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000804}
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