blob: 1020621cc1bd2b91340963ac20609688aaacd8f6 [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;
104}
105
106@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000107@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700108@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000109@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000110@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700111@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000112@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000113@synthesize peerConnection = _peerConnection;
114@synthesize factory = _factory;
115@synthesize messageQueue = _messageQueue;
116@synthesize isTurnComplete = _isTurnComplete;
117@synthesize hasReceivedSdp = _hasReceivedSdp;
118@synthesize roomId = _roomId;
119@synthesize clientId = _clientId;
120@synthesize isInitiator = _isInitiator;
121@synthesize iceServers = _iceServers;
122@synthesize webSocketURL = _websocketURL;
123@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000124@synthesize defaultPeerConnectionConstraints =
125 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700126@synthesize isLoopback = _isLoopback;
127@synthesize isAudioOnly = _isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700128@synthesize shouldMakeAecDump = _shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700129@synthesize shouldUseLevelControl = _shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000130
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000131- (instancetype)init {
132 if (self = [super init]) {
133 _roomServerClient = [[ARDAppEngineClient alloc] init];
134 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
135 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
136 [self configure];
137 }
138 return self;
139}
140
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000141- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
142 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000143 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000144 _delegate = delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000145 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl];
146 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL];
147 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000148 }
149 return self;
150}
151
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000152// TODO(tkchin): Provide signaling channel factory interface so we can recreate
153// channel if we need to on network failure. Also, make this the default public
154// constructor.
155- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
156 signalingChannel:(id<ARDSignalingChannel>)channel
157 turnClient:(id<ARDTURNClient>)turnClient
158 delegate:(id<ARDAppClientDelegate>)delegate {
159 NSParameterAssert(rsClient);
160 NSParameterAssert(channel);
161 NSParameterAssert(turnClient);
162 if (self = [super init]) {
163 _roomServerClient = rsClient;
164 _channel = channel;
165 _turnClient = turnClient;
166 _delegate = delegate;
167 [self configure];
168 }
169 return self;
170}
171
172- (void)configure {
173 _factory = [[RTCPeerConnectionFactory alloc] init];
174 _messageQueue = [NSMutableArray array];
175 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700176 _fileLogger = [[RTCFileLogger alloc] init];
177 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000178}
179
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000180- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700181 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000182 [self disconnect];
183}
184
Zeke Chind3325802015-08-14 11:00:02 -0700185- (void)setShouldGetStats:(BOOL)shouldGetStats {
186 if (_shouldGetStats == shouldGetStats) {
187 return;
188 }
189 if (shouldGetStats) {
190 __weak ARDAppClient *weakSelf = self;
191 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
192 repeats:YES
193 timerHandler:^{
194 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700195 [strongSelf.peerConnection statsForTrack:nil
196 statsOutputLevel:RTCStatsOutputLevelDebug
197 completionHandler:^(NSArray *stats) {
198 dispatch_async(dispatch_get_main_queue(), ^{
199 ARDAppClient *strongSelf = weakSelf;
200 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
201 });
202 }];
Zeke Chind3325802015-08-14 11:00:02 -0700203 }];
204 } else {
205 [_statsTimer invalidate];
206 _statsTimer = nil;
207 }
208 _shouldGetStats = shouldGetStats;
209}
210
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000211- (void)setState:(ARDAppClientState)state {
212 if (_state == state) {
213 return;
214 }
215 _state = state;
216 [_delegate appClient:self didChangeState:_state];
217}
218
219- (void)connectToRoomWithId:(NSString *)roomId
haysc913e6452015-10-02 11:44:03 -0700220 isLoopback:(BOOL)isLoopback
peah5085b0c2016-08-25 22:15:14 -0700221 isAudioOnly:(BOOL)isAudioOnly
tkchinab1293a2016-08-30 12:35:05 -0700222 shouldMakeAecDump:(BOOL)shouldMakeAecDump
223 shouldUseLevelControl:(BOOL)shouldUseLevelControl {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000224 NSParameterAssert(roomId.length);
225 NSParameterAssert(_state == kARDAppClientStateDisconnected);
haysc913e6452015-10-02 11:44:03 -0700226 _isLoopback = isLoopback;
227 _isAudioOnly = isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700228 _shouldMakeAecDump = shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700229 _shouldUseLevelControl = shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000230 self.state = kARDAppClientStateConnecting;
231
tkchind1fb26d2016-02-03 01:51:18 -0800232#if defined(WEBRTC_IOS)
233 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700234 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800235 RTCStartInternalCapture(filePath);
236 }
237#endif
238
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000239 // Request TURN.
240 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000241 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
242 NSError *error) {
243 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700244 RTCLogError("Error retrieving TURN servers: %@",
245 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000246 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000247 ARDAppClient *strongSelf = weakSelf;
248 [strongSelf.iceServers addObjectsFromArray:turnServers];
249 strongSelf.isTurnComplete = YES;
250 [strongSelf startSignalingIfReady];
251 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000252
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000253 // Join room on room server.
254 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700255 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000256 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000257 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000258 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000259 [strongSelf.delegate appClient:strongSelf didError:error];
260 return;
261 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000262 NSError *joinError =
263 [[strongSelf class] errorForJoinResultType:response.result];
264 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700265 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000266 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000267 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000268 return;
269 }
tkchinc3f46a92015-07-23 12:50:55 -0700270 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000271 strongSelf.roomId = response.roomId;
272 strongSelf.clientId = response.clientId;
273 strongSelf.isInitiator = response.isInitiator;
274 for (ARDSignalingMessage *message in response.messages) {
275 if (message.type == kARDSignalingMessageTypeOffer ||
276 message.type == kARDSignalingMessageTypeAnswer) {
277 strongSelf.hasReceivedSdp = YES;
278 [strongSelf.messageQueue insertObject:message atIndex:0];
279 } else {
280 [strongSelf.messageQueue addObject:message];
281 }
282 }
283 strongSelf.webSocketURL = response.webSocketURL;
284 strongSelf.webSocketRestURL = response.webSocketRestURL;
285 [strongSelf registerWithColliderIfReady];
286 [strongSelf startSignalingIfReady];
287 }];
288}
289
290- (void)disconnect {
291 if (_state == kARDAppClientStateDisconnected) {
292 return;
293 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000294 if (self.hasJoinedRoomServerRoom) {
295 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000296 clientId:_clientId
297 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000298 }
299 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000300 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000301 // Tell the other client we're hanging up.
302 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000303 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000304 }
305 // Disconnect from collider.
306 _channel = nil;
307 }
308 _clientId = nil;
309 _roomId = nil;
310 _isInitiator = NO;
311 _hasReceivedSdp = NO;
312 _messageQueue = [NSMutableArray array];
ivoc14d5dbe2016-07-04 07:06:55 -0700313#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700314 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700315 [_peerConnection stopRtcEventLog];
316#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000317 _peerConnection = nil;
318 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800319#if defined(WEBRTC_IOS)
320 RTCStopInternalCapture();
321#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000322}
323
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000324#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000325
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000326- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000327 didReceiveMessage:(ARDSignalingMessage *)message {
328 switch (message.type) {
329 case kARDSignalingMessageTypeOffer:
330 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000331 // Offers and answers must be processed before any other message, so we
332 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000333 _hasReceivedSdp = YES;
334 [_messageQueue insertObject:message atIndex:0];
335 break;
336 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700337 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000338 [_messageQueue addObject:message];
339 break;
340 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000341 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000342 [self processSignalingMessage:message];
343 return;
344 }
345 [self drainMessageQueueIfReady];
346}
347
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000348- (void)channel:(id<ARDSignalingChannel>)channel
349 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000350 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000351 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000352 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000353 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000354 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000355 case kARDSignalingChannelStateClosed:
356 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000357 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
358 // completely if the websocket connection fails.
359 [self disconnect];
360 break;
361 }
362}
363
364#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000365// Callbacks for this delegate occur on non-main thread and need to be
366// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000367
368- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700369 didChangeSignalingState:(RTCSignalingState)stateChanged {
370 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000371}
372
373- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700374 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000375 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700376 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000377 (unsigned long)stream.videoTracks.count,
378 (unsigned long)stream.audioTracks.count);
379 if (stream.videoTracks.count) {
380 RTCVideoTrack *videoTrack = stream.videoTracks[0];
381 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
382 }
383 });
384}
385
386- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700387 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700388 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000389}
390
hjon79858f82016-03-13 22:08:26 -0700391- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700392 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000393}
394
395- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700396 didChangeIceConnectionState:(RTCIceConnectionState)newState {
397 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000398 dispatch_async(dispatch_get_main_queue(), ^{
399 [_delegate appClient:self didChangeConnectionState:newState];
400 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000401}
402
403- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700404 didChangeIceGatheringState:(RTCIceGatheringState)newState {
405 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000406}
407
408- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700409 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000410 dispatch_async(dispatch_get_main_queue(), ^{
411 ARDICECandidateMessage *message =
412 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
413 [self sendSignalingMessage:message];
414 });
415}
416
Zeke Chind3325802015-08-14 11:00:02 -0700417- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700418 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
419 dispatch_async(dispatch_get_main_queue(), ^{
420 ARDICECandidateRemovalMessage *message =
421 [[ARDICECandidateRemovalMessage alloc]
422 initWithRemovedCandidates:candidates];
423 [self sendSignalingMessage:message];
424 });
425}
426
427- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700428 didOpenDataChannel:(RTCDataChannel *)dataChannel {
429}
430
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000431#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000432// Callbacks for this delegate occur on non-main thread and need to be
433// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000434
435- (void)peerConnection:(RTCPeerConnection *)peerConnection
436 didCreateSessionDescription:(RTCSessionDescription *)sdp
437 error:(NSError *)error {
438 dispatch_async(dispatch_get_main_queue(), ^{
439 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700440 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000441 [self disconnect];
442 NSDictionary *userInfo = @{
443 NSLocalizedDescriptionKey: @"Failed to create session description.",
444 };
445 NSError *sdpError =
446 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
447 code:kARDAppClientErrorCreateSDP
448 userInfo:userInfo];
449 [_delegate appClient:self didError:sdpError];
450 return;
451 }
Zeke Chin71f6f442015-06-29 14:34:58 -0700452 // Prefer H264 if available.
453 RTCSessionDescription *sdpPreferringH264 =
454 [ARDSDPUtils descriptionForDescription:sdp
455 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700456 __weak ARDAppClient *weakSelf = self;
457 [_peerConnection setLocalDescription:sdpPreferringH264
458 completionHandler:^(NSError *error) {
459 ARDAppClient *strongSelf = weakSelf;
460 [strongSelf peerConnection:strongSelf.peerConnection
461 didSetSessionDescriptionWithError:error];
462 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000463 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700464 [[ARDSessionDescriptionMessage alloc]
465 initWithDescription:sdpPreferringH264];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000466 [self sendSignalingMessage:message];
467 });
468}
469
470- (void)peerConnection:(RTCPeerConnection *)peerConnection
471 didSetSessionDescriptionWithError:(NSError *)error {
472 dispatch_async(dispatch_get_main_queue(), ^{
473 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700474 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000475 [self disconnect];
476 NSDictionary *userInfo = @{
477 NSLocalizedDescriptionKey: @"Failed to set session description.",
478 };
479 NSError *sdpError =
480 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
481 code:kARDAppClientErrorSetSDP
482 userInfo:userInfo];
483 [_delegate appClient:self didError:sdpError];
484 return;
485 }
486 // If we're answering and we've just set the remote offer we need to create
487 // an answer and set the local description.
488 if (!_isInitiator && !_peerConnection.localDescription) {
489 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700490 __weak ARDAppClient *weakSelf = self;
491 [_peerConnection answerForConstraints:constraints
492 completionHandler:^(RTCSessionDescription *sdp,
493 NSError *error) {
494 ARDAppClient *strongSelf = weakSelf;
495 [strongSelf peerConnection:strongSelf.peerConnection
496 didCreateSessionDescription:sdp
497 error:error];
498 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000499 }
500 });
501}
502
503#pragma mark - Private
504
tkchin204177f2016-06-14 15:03:11 -0700505#if defined(WEBRTC_IOS)
506
507- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
508 NSParameterAssert(fileName.length);
509 NSArray *paths = NSSearchPathForDirectoriesInDomains(
510 NSDocumentDirectory, NSUserDomainMask, YES);
511 NSString *documentsDirPath = paths.firstObject;
512 NSString *filePath =
513 [documentsDirPath stringByAppendingPathComponent:fileName];
514 return filePath;
515}
516
517#endif
518
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000519- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000520 return _clientId.length;
521}
522
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000523// Begins the peer connection connection process if we have both joined a room
524// on the room server and tried to obtain a TURN server. Otherwise does nothing.
525// A peer connection object will be created with a stream that contains local
526// audio and video capture. If this client is the caller, an offer is created as
527// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000528- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000529 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000530 return;
531 }
532 self.state = kARDAppClientStateConnected;
533
534 // Create peer connection.
535 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700536 RTCConfiguration *config = [[RTCConfiguration alloc] init];
537 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700538 _peerConnection = [_factory peerConnectionWithConfiguration:config
539 constraints:constraints
540 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700541 // Create AV senders.
542 [self createAudioSender];
543 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000544 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000545 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700546 __weak ARDAppClient *weakSelf = self;
547 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
548 completionHandler:^(RTCSessionDescription *sdp,
549 NSError *error) {
550 ARDAppClient *strongSelf = weakSelf;
551 [strongSelf peerConnection:strongSelf.peerConnection
552 didCreateSessionDescription:sdp
553 error:error];
554 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000555 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000556 // Check if we've received an offer.
557 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000558 }
ivoc14d5dbe2016-07-04 07:06:55 -0700559#if defined(WEBRTC_IOS)
560 // Start event log.
561 if (kARDAppClientEnableRtcEventLog) {
562 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
563 if (![_peerConnection startRtcEventLogWithFilePath:filePath
564 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
565 RTCLogError(@"Failed to start event logging.");
566 }
567 }
peah5085b0c2016-08-25 22:15:14 -0700568
569 // Start aecdump diagnostic recording.
570 if (_shouldMakeAecDump) {
tkchinfce0e2c2016-08-30 12:58:11 -0700571 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
572 if (![_factory startAecDumpWithFilePath:filePath
573 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
574 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700575 }
576 }
ivoc14d5dbe2016-07-04 07:06:55 -0700577#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000578}
579
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000580// Processes the messages that we've received from the room server and the
581// signaling channel. The offer or answer message must be processed before other
582// signaling messages, however they can arrive out of order. Hence, this method
583// only processes pending messages if there is a peer connection object and
584// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000585- (void)drainMessageQueueIfReady {
586 if (!_peerConnection || !_hasReceivedSdp) {
587 return;
588 }
589 for (ARDSignalingMessage *message in _messageQueue) {
590 [self processSignalingMessage:message];
591 }
592 [_messageQueue removeAllObjects];
593}
594
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000595// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000596- (void)processSignalingMessage:(ARDSignalingMessage *)message {
597 NSParameterAssert(_peerConnection ||
598 message.type == kARDSignalingMessageTypeBye);
599 switch (message.type) {
600 case kARDSignalingMessageTypeOffer:
601 case kARDSignalingMessageTypeAnswer: {
602 ARDSessionDescriptionMessage *sdpMessage =
603 (ARDSessionDescriptionMessage *)message;
604 RTCSessionDescription *description = sdpMessage.sessionDescription;
Zeke Chin71f6f442015-06-29 14:34:58 -0700605 // Prefer H264 if available.
606 RTCSessionDescription *sdpPreferringH264 =
607 [ARDSDPUtils descriptionForDescription:description
608 preferredVideoCodec:@"H264"];
hjon79858f82016-03-13 22:08:26 -0700609 __weak ARDAppClient *weakSelf = self;
610 [_peerConnection setRemoteDescription:sdpPreferringH264
611 completionHandler:^(NSError *error) {
612 ARDAppClient *strongSelf = weakSelf;
613 [strongSelf peerConnection:strongSelf.peerConnection
614 didSetSessionDescriptionWithError:error];
615 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000616 break;
617 }
618 case kARDSignalingMessageTypeCandidate: {
619 ARDICECandidateMessage *candidateMessage =
620 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700621 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000622 break;
623 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700624 case kARDSignalingMessageTypeCandidateRemoval: {
625 ARDICECandidateRemovalMessage *candidateMessage =
626 (ARDICECandidateRemovalMessage *)message;
627 [_peerConnection removeIceCandidates:candidateMessage.candidates];
628 break;
629 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000630 case kARDSignalingMessageTypeBye:
631 // Other client disconnected.
632 // TODO(tkchin): support waiting in room for next client. For now just
633 // disconnect.
634 [self disconnect];
635 break;
636 }
637}
638
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000639// Sends a signaling message to the other client. The caller will send messages
640// through the room server, whereas the callee will send messages over the
641// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000642- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
643 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000644 __weak ARDAppClient *weakSelf = self;
645 [_roomServerClient sendMessage:message
646 forRoomId:_roomId
647 clientId:_clientId
648 completionHandler:^(ARDMessageResponse *response,
649 NSError *error) {
650 ARDAppClient *strongSelf = weakSelf;
651 if (error) {
652 [strongSelf.delegate appClient:strongSelf didError:error];
653 return;
654 }
655 NSError *messageError =
656 [[strongSelf class] errorForMessageResultType:response.result];
657 if (messageError) {
658 [strongSelf.delegate appClient:strongSelf didError:messageError];
659 return;
660 }
661 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000662 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000663 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000664 }
665}
666
skvladf3569c82016-04-29 15:30:16 -0700667- (RTCRtpSender *)createVideoSender {
668 RTCRtpSender *sender =
669 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
670 streamId:kARDMediaStreamId];
671 RTCVideoTrack *track = [self createLocalVideoTrack];
672 if (track) {
673 sender.track = track;
674 [_delegate appClient:self didReceiveLocalVideoTrack:track];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700675 }
skvladf3569c82016-04-29 15:30:16 -0700676 return sender;
677}
678
679- (RTCRtpSender *)createAudioSender {
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];
skvladf3569c82016-04-29 15:30:16 -0700684 RTCRtpSender *sender =
685 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
686 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700687 sender.track = track;
688 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700689}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000690
Zeke Chin57cc74e2015-05-05 07:52:31 -0700691- (RTCVideoTrack *)createLocalVideoTrack {
692 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000693 // The iOS simulator doesn't provide any sort of camera capture
694 // support or emulation (http://goo.gl/rHAnC1) so don't bother
695 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700696#if !TARGET_IPHONE_SIMULATOR
haysc913e6452015-10-02 11:44:03 -0700697 if (!_isAudioOnly) {
698 RTCMediaConstraints *mediaConstraints =
699 [self defaultMediaStreamConstraints];
700 RTCAVFoundationVideoSource *source =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700701 [_factory avFoundationVideoSourceWithConstraints:mediaConstraints];
haysc913e6452015-10-02 11:44:03 -0700702 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700703 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700704 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700705 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000706#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700707 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000708}
709
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000710#pragma mark - Collider methods
711
712- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000713 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000714 return;
715 }
716 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000717 if (!_channel) {
718 _channel =
719 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
720 restURL:_websocketRestURL
721 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700722 if (_isLoopback) {
723 _loopbackChannel =
724 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
725 restURL:_websocketRestURL];
726 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000727 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000728 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700729 if (_isLoopback) {
730 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
731 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000732}
733
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000734#pragma mark - Defaults
735
tkchinab1293a2016-08-30 12:35:05 -0700736 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
737 NSString *valueLevelControl = _shouldUseLevelControl ?
738 kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
739 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
740 RTCMediaConstraints* constraints =
741 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
742 optionalConstraints:nil];
743 return constraints;
744}
745
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000746- (RTCMediaConstraints *)defaultMediaStreamConstraints {
747 RTCMediaConstraints* constraints =
748 [[RTCMediaConstraints alloc]
749 initWithMandatoryConstraints:nil
750 optionalConstraints:nil];
751 return constraints;
752}
753
754- (RTCMediaConstraints *)defaultAnswerConstraints {
755 return [self defaultOfferConstraints];
756}
757
758- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700759 NSDictionary *mandatoryConstraints = @{
760 @"OfferToReceiveAudio" : @"true",
761 @"OfferToReceiveVideo" : @"true"
762 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000763 RTCMediaConstraints* constraints =
764 [[RTCMediaConstraints alloc]
765 initWithMandatoryConstraints:mandatoryConstraints
766 optionalConstraints:nil];
767 return constraints;
768}
769
770- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000771 if (_defaultPeerConnectionConstraints) {
772 return _defaultPeerConnectionConstraints;
773 }
haysc913e6452015-10-02 11:44:03 -0700774 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700775 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000776 RTCMediaConstraints* constraints =
777 [[RTCMediaConstraints alloc]
778 initWithMandatoryConstraints:nil
779 optionalConstraints:optionalConstraints];
780 return constraints;
781}
782
hjon79858f82016-03-13 22:08:26 -0700783- (RTCIceServer *)defaultSTUNServer {
784 return [[RTCIceServer alloc] initWithURLStrings:@[kARDDefaultSTUNServerUrl]
785 username:@""
786 credential:@""];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000787}
788
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000789#pragma mark - Errors
790
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000791+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000792 NSError *error = nil;
793 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000794 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000795 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000796 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000797 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
798 code:kARDAppClientErrorUnknown
799 userInfo:@{
800 NSLocalizedDescriptionKey: @"Unknown error.",
801 }];
802 break;
803 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000804 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000805 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
806 code:kARDAppClientErrorRoomFull
807 userInfo:@{
808 NSLocalizedDescriptionKey: @"Room is full.",
809 }];
810 break;
811 }
812 }
813 return error;
814}
815
816+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
817 NSError *error = nil;
818 switch (resultType) {
819 case kARDMessageResultTypeSuccess:
820 break;
821 case kARDMessageResultTypeUnknown:
822 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
823 code:kARDAppClientErrorUnknown
824 userInfo:@{
825 NSLocalizedDescriptionKey: @"Unknown error.",
826 }];
827 break;
828 case kARDMessageResultTypeInvalidClient:
829 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
830 code:kARDAppClientErrorInvalidClient
831 userInfo:@{
832 NSLocalizedDescriptionKey: @"Invalid client.",
833 }];
834 break;
835 case kARDMessageResultTypeInvalidRoom:
836 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
837 code:kARDAppClientErrorInvalidRoom
838 userInfo:@{
839 NSLocalizedDescriptionKey: @"Invalid room.",
840 }];
841 break;
842 }
843 return error;
844}
845
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000846@end