blob: 8e933b921bb7d894acbad1d857c6a6fbd83cebce [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"
sakalc522e752017-04-05 12:17:48 -070015#import "WebRTC/RTCCameraVideoCapturer.h"
tkchin9eeb6242016-04-27 01:54:20 -070016#import "WebRTC/RTCConfiguration.h"
17#import "WebRTC/RTCFileLogger.h"
18#import "WebRTC/RTCIceServer.h"
19#import "WebRTC/RTCLogging.h"
20#import "WebRTC/RTCMediaConstraints.h"
21#import "WebRTC/RTCMediaStream.h"
22#import "WebRTC/RTCPeerConnectionFactory.h"
skvladf3569c82016-04-29 15:30:16 -070023#import "WebRTC/RTCRtpSender.h"
tkchin204177f2016-06-14 15:03:11 -070024#import "WebRTC/RTCTracing.h"
sakalc522e752017-04-05 12:17:48 -070025#import "WebRTC/RTCVideoTrack.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000026
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000027#import "ARDAppEngineClient.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000028#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000029#import "ARDMessageResponse.h"
sakalc4adacf2017-03-28 01:22:48 -070030#import "ARDSettingsModel.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000031#import "ARDSignalingMessage.h"
sakalc4adacf2017-03-28 01:22:48 -070032#import "ARDTURNClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000033#import "ARDUtilities.h"
Anders Carlsson7e042812017-10-05 16:55:38 +020034#import "ARDVideoDecoderFactory.h"
35#import "ARDVideoEncoderFactory.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000036#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070037#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000038#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070039
kthelgasoncc882af2017-01-13 05:59:46 -080040static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000041
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000042static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
43static NSInteger const kARDAppClientErrorUnknown = -1;
44static NSInteger const kARDAppClientErrorRoomFull = -2;
45static NSInteger const kARDAppClientErrorCreateSDP = -3;
46static NSInteger const kARDAppClientErrorSetSDP = -4;
47static NSInteger const kARDAppClientErrorInvalidClient = -5;
48static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070049static NSString * const kARDMediaStreamId = @"ARDAMS";
50static NSString * const kARDAudioTrackId = @"ARDAMSa0";
51static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080052static NSString * const kARDVideoTrackKind = @"video";
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.
denicija9af2b602016-11-17 00:43:43 -080059static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080060
Zeke Chind3325802015-08-14 11:00:02 -070061// We need a proxy to NSTimer because it causes a strong retain cycle. When
62// using the proxy, |invalidate| must be called before it properly deallocs.
63@interface ARDTimerProxy : NSObject
64
65- (instancetype)initWithInterval:(NSTimeInterval)interval
66 repeats:(BOOL)repeats
67 timerHandler:(void (^)(void))timerHandler;
68- (void)invalidate;
69
70@end
71
72@implementation ARDTimerProxy {
73 NSTimer *_timer;
74 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070075}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000076
Zeke Chind3325802015-08-14 11:00:02 -070077- (instancetype)initWithInterval:(NSTimeInterval)interval
78 repeats:(BOOL)repeats
79 timerHandler:(void (^)(void))timerHandler {
80 NSParameterAssert(timerHandler);
81 if (self = [super init]) {
82 _timerHandler = timerHandler;
83 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
84 target:self
85 selector:@selector(timerDidFire:)
86 userInfo:nil
87 repeats:repeats];
88 }
89 return self;
90}
91
92- (void)invalidate {
93 [_timer invalidate];
94}
95
96- (void)timerDidFire:(NSTimer *)timer {
97 _timerHandler();
98}
99
100@end
101
102@implementation ARDAppClient {
103 RTCFileLogger *_fileLogger;
104 ARDTimerProxy *_statsTimer;
sakalc4adacf2017-03-28 01:22:48 -0700105 ARDSettingsModel *_settings;
sakalc522e752017-04-05 12:17:48 -0700106 RTCVideoTrack *_localVideoTrack;
Zeke Chind3325802015-08-14 11:00:02 -0700107}
108
109@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000110@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700111@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000112@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000113@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700114@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000115@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000116@synthesize peerConnection = _peerConnection;
117@synthesize factory = _factory;
118@synthesize messageQueue = _messageQueue;
119@synthesize isTurnComplete = _isTurnComplete;
120@synthesize hasReceivedSdp = _hasReceivedSdp;
121@synthesize roomId = _roomId;
122@synthesize clientId = _clientId;
123@synthesize isInitiator = _isInitiator;
124@synthesize iceServers = _iceServers;
125@synthesize webSocketURL = _websocketURL;
126@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000127@synthesize defaultPeerConnectionConstraints =
128 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700129@synthesize isLoopback = _isLoopback;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000130
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000131- (instancetype)init {
sakalc4adacf2017-03-28 01:22:48 -0700132 return [self initWithDelegate:nil];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000133}
134
sakalc4adacf2017-03-28 01:22:48 -0700135- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000136 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000137 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000138 _delegate = delegate;
kthelgasoncc882af2017-01-13 05:59:46 -0800139 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
140 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000141 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000142 }
143 return self;
144}
145
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000146// TODO(tkchin): Provide signaling channel factory interface so we can recreate
147// channel if we need to on network failure. Also, make this the default public
148// constructor.
149- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
150 signalingChannel:(id<ARDSignalingChannel>)channel
151 turnClient:(id<ARDTURNClient>)turnClient
152 delegate:(id<ARDAppClientDelegate>)delegate {
153 NSParameterAssert(rsClient);
154 NSParameterAssert(channel);
155 NSParameterAssert(turnClient);
156 if (self = [super init]) {
157 _roomServerClient = rsClient;
158 _channel = channel;
159 _turnClient = turnClient;
160 _delegate = delegate;
161 [self configure];
162 }
163 return self;
164}
165
166- (void)configure {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000167 _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
sakalc4adacf2017-03-28 01:22:48 -0700213 settings:(ARDSettingsModel *)settings
Anders Carlssone1500582017-06-15 16:05:13 +0200214 isLoopback:(BOOL)isLoopback {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000215 NSParameterAssert(roomId.length);
216 NSParameterAssert(_state == kARDAppClientStateDisconnected);
sakalc4adacf2017-03-28 01:22:48 -0700217 _settings = settings;
haysc913e6452015-10-02 11:44:03 -0700218 _isLoopback = isLoopback;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000219 self.state = kARDAppClientStateConnecting;
220
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200221 ARDVideoDecoderFactory *decoderFactory = [[ARDVideoDecoderFactory alloc] init];
222 ARDVideoEncoderFactory *encoderFactory = [[ARDVideoEncoderFactory alloc] init];
223 encoderFactory.preferredCodec = [settings currentVideoCodecSettingFromStore];
224 _factory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory:encoderFactory
225 decoderFactory:decoderFactory];
226
tkchind1fb26d2016-02-03 01:51:18 -0800227#if defined(WEBRTC_IOS)
228 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700229 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800230 RTCStartInternalCapture(filePath);
231 }
232#endif
233
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000234 // Request TURN.
235 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000236 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
237 NSError *error) {
238 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700239 RTCLogError("Error retrieving TURN servers: %@",
240 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000241 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000242 ARDAppClient *strongSelf = weakSelf;
243 [strongSelf.iceServers addObjectsFromArray:turnServers];
244 strongSelf.isTurnComplete = YES;
245 [strongSelf startSignalingIfReady];
246 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000247
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000248 // Join room on room server.
249 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700250 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000251 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000252 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000253 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000254 [strongSelf.delegate appClient:strongSelf didError:error];
255 return;
256 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000257 NSError *joinError =
258 [[strongSelf class] errorForJoinResultType:response.result];
259 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700260 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000261 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000262 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000263 return;
264 }
tkchinc3f46a92015-07-23 12:50:55 -0700265 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000266 strongSelf.roomId = response.roomId;
267 strongSelf.clientId = response.clientId;
268 strongSelf.isInitiator = response.isInitiator;
269 for (ARDSignalingMessage *message in response.messages) {
270 if (message.type == kARDSignalingMessageTypeOffer ||
271 message.type == kARDSignalingMessageTypeAnswer) {
272 strongSelf.hasReceivedSdp = YES;
273 [strongSelf.messageQueue insertObject:message atIndex:0];
274 } else {
275 [strongSelf.messageQueue addObject:message];
276 }
277 }
278 strongSelf.webSocketURL = response.webSocketURL;
279 strongSelf.webSocketRestURL = response.webSocketRestURL;
280 [strongSelf registerWithColliderIfReady];
281 [strongSelf startSignalingIfReady];
282 }];
283}
284
285- (void)disconnect {
286 if (_state == kARDAppClientStateDisconnected) {
287 return;
288 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000289 if (self.hasJoinedRoomServerRoom) {
290 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000291 clientId:_clientId
292 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000293 }
294 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000295 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000296 // Tell the other client we're hanging up.
297 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000298 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000299 }
300 // Disconnect from collider.
301 _channel = nil;
302 }
303 _clientId = nil;
304 _roomId = nil;
305 _isInitiator = NO;
306 _hasReceivedSdp = NO;
307 _messageQueue = [NSMutableArray array];
sakalc522e752017-04-05 12:17:48 -0700308 _localVideoTrack = nil;
ivoc14d5dbe2016-07-04 07:06:55 -0700309#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700310 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700311 [_peerConnection stopRtcEventLog];
312#endif
magjedcc8b9062017-07-24 07:32:33 -0700313 [_peerConnection close];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000314 _peerConnection = nil;
315 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800316#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800317 if (kARDAppClientEnableTracing) {
318 RTCStopInternalCapture();
319 }
tkchind1fb26d2016-02-03 01:51:18 -0800320#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000321}
322
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000323#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000324
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000325- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000326 didReceiveMessage:(ARDSignalingMessage *)message {
327 switch (message.type) {
328 case kARDSignalingMessageTypeOffer:
329 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000330 // Offers and answers must be processed before any other message, so we
331 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000332 _hasReceivedSdp = YES;
333 [_messageQueue insertObject:message atIndex:0];
334 break;
335 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700336 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000337 [_messageQueue addObject:message];
338 break;
339 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000340 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000341 [self processSignalingMessage:message];
342 return;
343 }
344 [self drainMessageQueueIfReady];
345}
346
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000347- (void)channel:(id<ARDSignalingChannel>)channel
348 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000349 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000350 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000351 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000352 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000353 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000354 case kARDSignalingChannelStateClosed:
355 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000356 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
357 // completely if the websocket connection fails.
358 [self disconnect];
359 break;
360 }
361}
362
363#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000364// Callbacks for this delegate occur on non-main thread and need to be
365// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000366
367- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700368 didChangeSignalingState:(RTCSignalingState)stateChanged {
369 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000370}
371
372- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700373 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000374 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700375 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000376 (unsigned long)stream.videoTracks.count,
377 (unsigned long)stream.audioTracks.count);
378 if (stream.videoTracks.count) {
379 RTCVideoTrack *videoTrack = stream.videoTracks[0];
380 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
381 }
382 });
383}
384
385- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700386 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700387 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000388}
389
hjon79858f82016-03-13 22:08:26 -0700390- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700391 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000392}
393
394- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700395 didChangeIceConnectionState:(RTCIceConnectionState)newState {
396 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000397 dispatch_async(dispatch_get_main_queue(), ^{
398 [_delegate appClient:self didChangeConnectionState:newState];
399 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000400}
401
402- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700403 didChangeIceGatheringState:(RTCIceGatheringState)newState {
404 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000405}
406
407- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700408 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000409 dispatch_async(dispatch_get_main_queue(), ^{
410 ARDICECandidateMessage *message =
411 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
412 [self sendSignalingMessage:message];
413 });
414}
415
Zeke Chind3325802015-08-14 11:00:02 -0700416- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700417 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
418 dispatch_async(dispatch_get_main_queue(), ^{
419 ARDICECandidateRemovalMessage *message =
420 [[ARDICECandidateRemovalMessage alloc]
421 initWithRemovedCandidates:candidates];
422 [self sendSignalingMessage:message];
423 });
424}
425
426- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700427 didOpenDataChannel:(RTCDataChannel *)dataChannel {
428}
429
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000430#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000431// Callbacks for this delegate occur on non-main thread and need to be
432// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000433
434- (void)peerConnection:(RTCPeerConnection *)peerConnection
435 didCreateSessionDescription:(RTCSessionDescription *)sdp
436 error:(NSError *)error {
437 dispatch_async(dispatch_get_main_queue(), ^{
438 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700439 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000440 [self disconnect];
441 NSDictionary *userInfo = @{
442 NSLocalizedDescriptionKey: @"Failed to create session description.",
443 };
444 NSError *sdpError =
445 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
446 code:kARDAppClientErrorCreateSDP
447 userInfo:userInfo];
448 [_delegate appClient:self didError:sdpError];
449 return;
450 }
hjon79858f82016-03-13 22:08:26 -0700451 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200452 [_peerConnection setLocalDescription:sdp
hjon79858f82016-03-13 22:08:26 -0700453 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200454 ARDAppClient *strongSelf = weakSelf;
455 [strongSelf peerConnection:strongSelf.peerConnection
456 didSetSessionDescriptionWithError:error];
457 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000458 ARDSessionDescriptionMessage *message =
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200459 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000460 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800461 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000462 });
463}
464
465- (void)peerConnection:(RTCPeerConnection *)peerConnection
466 didSetSessionDescriptionWithError:(NSError *)error {
467 dispatch_async(dispatch_get_main_queue(), ^{
468 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700469 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000470 [self disconnect];
471 NSDictionary *userInfo = @{
472 NSLocalizedDescriptionKey: @"Failed to set session description.",
473 };
474 NSError *sdpError =
475 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
476 code:kARDAppClientErrorSetSDP
477 userInfo:userInfo];
478 [_delegate appClient:self didError:sdpError];
479 return;
480 }
481 // If we're answering and we've just set the remote offer we need to create
482 // an answer and set the local description.
483 if (!_isInitiator && !_peerConnection.localDescription) {
484 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700485 __weak ARDAppClient *weakSelf = self;
486 [_peerConnection answerForConstraints:constraints
487 completionHandler:^(RTCSessionDescription *sdp,
488 NSError *error) {
489 ARDAppClient *strongSelf = weakSelf;
490 [strongSelf peerConnection:strongSelf.peerConnection
491 didCreateSessionDescription:sdp
492 error:error];
493 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000494 }
495 });
496}
497
498#pragma mark - Private
499
tkchin204177f2016-06-14 15:03:11 -0700500#if defined(WEBRTC_IOS)
501
502- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
503 NSParameterAssert(fileName.length);
504 NSArray *paths = NSSearchPathForDirectoriesInDomains(
505 NSDocumentDirectory, NSUserDomainMask, YES);
506 NSString *documentsDirPath = paths.firstObject;
507 NSString *filePath =
508 [documentsDirPath stringByAppendingPathComponent:fileName];
509 return filePath;
510}
511
512#endif
513
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000514- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000515 return _clientId.length;
516}
517
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000518// Begins the peer connection connection process if we have both joined a room
519// on the room server and tried to obtain a TURN server. Otherwise does nothing.
520// A peer connection object will be created with a stream that contains local
521// audio and video capture. If this client is the caller, an offer is created as
522// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000523- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000524 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000525 return;
526 }
527 self.state = kARDAppClientStateConnected;
528
529 // Create peer connection.
530 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700531 RTCConfiguration *config = [[RTCConfiguration alloc] init];
532 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700533 _peerConnection = [_factory peerConnectionWithConfiguration:config
534 constraints:constraints
535 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700536 // Create AV senders.
Alex Narestb3944f02017-10-13 14:56:18 +0200537 [self createMediaSenders];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000538 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000539 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700540 __weak ARDAppClient *weakSelf = self;
541 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
542 completionHandler:^(RTCSessionDescription *sdp,
543 NSError *error) {
544 ARDAppClient *strongSelf = weakSelf;
545 [strongSelf peerConnection:strongSelf.peerConnection
546 didCreateSessionDescription:sdp
547 error:error];
548 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000549 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000550 // Check if we've received an offer.
551 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000552 }
ivoc14d5dbe2016-07-04 07:06:55 -0700553#if defined(WEBRTC_IOS)
554 // Start event log.
555 if (kARDAppClientEnableRtcEventLog) {
556 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
557 if (![_peerConnection startRtcEventLogWithFilePath:filePath
558 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
559 RTCLogError(@"Failed to start event logging.");
560 }
561 }
peah5085b0c2016-08-25 22:15:14 -0700562
563 // Start aecdump diagnostic recording.
Anders Carlssone1500582017-06-15 16:05:13 +0200564 if ([_settings currentCreateAecDumpSettingFromStore]) {
tkchinfce0e2c2016-08-30 12:58:11 -0700565 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
566 if (![_factory startAecDumpWithFilePath:filePath
567 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
568 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700569 }
570 }
ivoc14d5dbe2016-07-04 07:06:55 -0700571#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000572}
573
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000574// Processes the messages that we've received from the room server and the
575// signaling channel. The offer or answer message must be processed before other
576// signaling messages, however they can arrive out of order. Hence, this method
577// only processes pending messages if there is a peer connection object and
578// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000579- (void)drainMessageQueueIfReady {
580 if (!_peerConnection || !_hasReceivedSdp) {
581 return;
582 }
583 for (ARDSignalingMessage *message in _messageQueue) {
584 [self processSignalingMessage:message];
585 }
586 [_messageQueue removeAllObjects];
587}
588
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000589// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000590- (void)processSignalingMessage:(ARDSignalingMessage *)message {
591 NSParameterAssert(_peerConnection ||
592 message.type == kARDSignalingMessageTypeBye);
593 switch (message.type) {
594 case kARDSignalingMessageTypeOffer:
595 case kARDSignalingMessageTypeAnswer: {
596 ARDSessionDescriptionMessage *sdpMessage =
597 (ARDSessionDescriptionMessage *)message;
598 RTCSessionDescription *description = sdpMessage.sessionDescription;
hjon79858f82016-03-13 22:08:26 -0700599 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200600 [_peerConnection setRemoteDescription:description
hjon79858f82016-03-13 22:08:26 -0700601 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200602 ARDAppClient *strongSelf = weakSelf;
603 [strongSelf peerConnection:strongSelf.peerConnection
604 didSetSessionDescriptionWithError:error];
605 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000606 break;
607 }
608 case kARDSignalingMessageTypeCandidate: {
609 ARDICECandidateMessage *candidateMessage =
610 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700611 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000612 break;
613 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700614 case kARDSignalingMessageTypeCandidateRemoval: {
615 ARDICECandidateRemovalMessage *candidateMessage =
616 (ARDICECandidateRemovalMessage *)message;
617 [_peerConnection removeIceCandidates:candidateMessage.candidates];
618 break;
619 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000620 case kARDSignalingMessageTypeBye:
621 // Other client disconnected.
622 // TODO(tkchin): support waiting in room for next client. For now just
623 // disconnect.
624 [self disconnect];
625 break;
626 }
627}
628
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000629// Sends a signaling message to the other client. The caller will send messages
630// through the room server, whereas the callee will send messages over the
631// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000632- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
633 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000634 __weak ARDAppClient *weakSelf = self;
635 [_roomServerClient sendMessage:message
636 forRoomId:_roomId
637 clientId:_clientId
638 completionHandler:^(ARDMessageResponse *response,
639 NSError *error) {
640 ARDAppClient *strongSelf = weakSelf;
641 if (error) {
642 [strongSelf.delegate appClient:strongSelf didError:error];
643 return;
644 }
645 NSError *messageError =
646 [[strongSelf class] errorForMessageResultType:response.result];
647 if (messageError) {
648 [strongSelf.delegate appClient:strongSelf didError:messageError];
649 return;
650 }
651 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000652 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000653 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000654 }
655}
656
denicija9af2b602016-11-17 00:43:43 -0800657- (void)setMaxBitrateForPeerConnectionVideoSender {
658 for (RTCRtpSender *sender in _peerConnection.senders) {
659 if (sender.track != nil) {
660 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700661 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800662 }
663 }
denicija8c375de2016-11-08 06:28:17 -0800664 }
665}
666
denicija9af2b602016-11-17 00:43:43 -0800667- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
668 if (maxBitrate.intValue <= 0) {
669 return;
670 }
671
672 RTCRtpParameters *parametersToModify = sender.parameters;
673 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
674 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
675 }
676 [sender setParameters:parametersToModify];
677}
678
Alex Narestb3944f02017-10-13 14:56:18 +0200679- (void)createMediaSenders {
tkchinab1293a2016-08-30 12:35:05 -0700680 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
681 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
682 RTCAudioTrack *track = [_factory audioTrackWithSource:source
683 trackId:kARDAudioTrackId];
Alex Narestb3944f02017-10-13 14:56:18 +0200684 RTCMediaStream *stream = [_factory mediaStreamWithStreamId:kARDMediaStreamId];
685 [stream addAudioTrack:track];
686 _localVideoTrack = [self createLocalVideoTrack];
687 if (_localVideoTrack) {
688 [stream addVideoTrack:_localVideoTrack];
689 }
690 [_peerConnection addStream:stream];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700691}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000692
Zeke Chin57cc74e2015-05-05 07:52:31 -0700693- (RTCVideoTrack *)createLocalVideoTrack {
694 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000695 // The iOS simulator doesn't provide any sort of camera capture
696 // support or emulation (http://goo.gl/rHAnC1) so don't bother
697 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700698#if !TARGET_IPHONE_SIMULATOR
Anders Carlssone1500582017-06-15 16:05:13 +0200699 if (![_settings currentAudioOnlySettingFromStore]) {
sakalc522e752017-04-05 12:17:48 -0700700 RTCVideoSource *source = [_factory videoSource];
701 RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
702 [_delegate appClient:self didCreateLocalCapturer:capturer];
haysc913e6452015-10-02 11:44:03 -0700703 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700704 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700705 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700706 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000707#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700708 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000709}
710
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000711#pragma mark - Collider methods
712
713- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000714 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000715 return;
716 }
717 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000718 if (!_channel) {
719 _channel =
720 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
721 restURL:_websocketRestURL
722 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700723 if (_isLoopback) {
724 _loopbackChannel =
725 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
726 restURL:_websocketRestURL];
727 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000728 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000729 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700730 if (_isLoopback) {
731 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
732 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000733}
734
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000735#pragma mark - Defaults
736
tkchinab1293a2016-08-30 12:35:05 -0700737 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
Anders Carlssone1500582017-06-15 16:05:13 +0200738 NSString *valueLevelControl = [_settings currentUseLevelControllerSettingFromStore] ?
739 kRTCMediaConstraintsValueTrue :
740 kRTCMediaConstraintsValueFalse;
tkchinab1293a2016-08-30 12:35:05 -0700741 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700742 RTCMediaConstraints *constraints =
743 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
744 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700745 return constraints;
746}
747
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000748- (RTCMediaConstraints *)defaultAnswerConstraints {
749 return [self defaultOfferConstraints];
750}
751
752- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700753 NSDictionary *mandatoryConstraints = @{
754 @"OfferToReceiveAudio" : @"true",
755 @"OfferToReceiveVideo" : @"true"
756 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000757 RTCMediaConstraints* constraints =
758 [[RTCMediaConstraints alloc]
759 initWithMandatoryConstraints:mandatoryConstraints
760 optionalConstraints:nil];
761 return constraints;
762}
763
764- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000765 if (_defaultPeerConnectionConstraints) {
766 return _defaultPeerConnectionConstraints;
767 }
haysc913e6452015-10-02 11:44:03 -0700768 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700769 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000770 RTCMediaConstraints* constraints =
771 [[RTCMediaConstraints alloc]
772 initWithMandatoryConstraints:nil
773 optionalConstraints:optionalConstraints];
774 return constraints;
775}
776
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000777#pragma mark - Errors
778
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000779+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000780 NSError *error = nil;
781 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000782 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000783 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000784 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000785 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
786 code:kARDAppClientErrorUnknown
787 userInfo:@{
788 NSLocalizedDescriptionKey: @"Unknown error.",
789 }];
790 break;
791 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000792 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000793 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
794 code:kARDAppClientErrorRoomFull
795 userInfo:@{
796 NSLocalizedDescriptionKey: @"Room is full.",
797 }];
798 break;
799 }
800 }
801 return error;
802}
803
804+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
805 NSError *error = nil;
806 switch (resultType) {
807 case kARDMessageResultTypeSuccess:
808 break;
809 case kARDMessageResultTypeUnknown:
810 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
811 code:kARDAppClientErrorUnknown
812 userInfo:@{
813 NSLocalizedDescriptionKey: @"Unknown error.",
814 }];
815 break;
816 case kARDMessageResultTypeInvalidClient:
817 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
818 code:kARDAppClientErrorInvalidClient
819 userInfo:@{
820 NSLocalizedDescriptionKey: @"Invalid client.",
821 }];
822 break;
823 case kARDMessageResultTypeInvalidRoom:
824 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
825 code:kARDAppClientErrorInvalidRoom
826 userInfo:@{
827 NSLocalizedDescriptionKey: @"Invalid room.",
828 }];
829 break;
830 }
831 return error;
832}
833
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000834@end