blob: c699381842e09ab03fc7d352cbba7698054132e3 [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"
Zeke Chin71f6f442015-06-29 14:34:58 -070030#import "ARDSDPUtils.h"
sakalc4adacf2017-03-28 01:22:48 -070031#import "ARDSettingsModel.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000032#import "ARDSignalingMessage.h"
sakalc4adacf2017-03-28 01:22:48 -070033#import "ARDTURNClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000034#import "ARDUtilities.h"
35#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070036#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000037#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070038
kthelgasoncc882af2017-01-13 05:59:46 -080039static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000040
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000041static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
42static NSInteger const kARDAppClientErrorUnknown = -1;
43static NSInteger const kARDAppClientErrorRoomFull = -2;
44static NSInteger const kARDAppClientErrorCreateSDP = -3;
45static NSInteger const kARDAppClientErrorSetSDP = -4;
46static NSInteger const kARDAppClientErrorInvalidClient = -5;
47static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070048static NSString * const kARDMediaStreamId = @"ARDAMS";
49static NSString * const kARDAudioTrackId = @"ARDAMSa0";
50static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080051static NSString * const kARDVideoTrackKind = @"video";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000052
tkchin204177f2016-06-14 15:03:11 -070053// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080054static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070055static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070056static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070057static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
denicija9af2b602016-11-17 00:43:43 -080058static int const kKbpsMultiplier = 1000;
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;
sakalc4adacf2017-03-28 01:22:48 -0700104 ARDSettingsModel *_settings;
sakalc522e752017-04-05 12:17:48 -0700105 RTCVideoTrack *_localVideoTrack;
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;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000129
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000130- (instancetype)init {
sakalc4adacf2017-03-28 01:22:48 -0700131 return [self initWithDelegate:nil];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000132}
133
sakalc4adacf2017-03-28 01:22:48 -0700134- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000135 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000136 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000137 _delegate = delegate;
kthelgasoncc882af2017-01-13 05:59:46 -0800138 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
139 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000140 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000141 }
142 return self;
143}
144
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000145// TODO(tkchin): Provide signaling channel factory interface so we can recreate
146// channel if we need to on network failure. Also, make this the default public
147// constructor.
148- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
149 signalingChannel:(id<ARDSignalingChannel>)channel
150 turnClient:(id<ARDTURNClient>)turnClient
151 delegate:(id<ARDAppClientDelegate>)delegate {
152 NSParameterAssert(rsClient);
153 NSParameterAssert(channel);
154 NSParameterAssert(turnClient);
155 if (self = [super init]) {
156 _roomServerClient = rsClient;
157 _channel = channel;
158 _turnClient = turnClient;
159 _delegate = delegate;
160 [self configure];
161 }
162 return self;
163}
164
165- (void)configure {
166 _factory = [[RTCPeerConnectionFactory alloc] init];
167 _messageQueue = [NSMutableArray array];
kthelgasoncc882af2017-01-13 05:59:46 -0800168 _iceServers = [NSMutableArray array];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700169 _fileLogger = [[RTCFileLogger alloc] init];
170 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000171}
172
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000173- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700174 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000175 [self disconnect];
176}
177
Zeke Chind3325802015-08-14 11:00:02 -0700178- (void)setShouldGetStats:(BOOL)shouldGetStats {
179 if (_shouldGetStats == shouldGetStats) {
180 return;
181 }
182 if (shouldGetStats) {
183 __weak ARDAppClient *weakSelf = self;
184 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
185 repeats:YES
186 timerHandler:^{
187 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700188 [strongSelf.peerConnection statsForTrack:nil
189 statsOutputLevel:RTCStatsOutputLevelDebug
190 completionHandler:^(NSArray *stats) {
191 dispatch_async(dispatch_get_main_queue(), ^{
192 ARDAppClient *strongSelf = weakSelf;
193 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
194 });
195 }];
Zeke Chind3325802015-08-14 11:00:02 -0700196 }];
197 } else {
198 [_statsTimer invalidate];
199 _statsTimer = nil;
200 }
201 _shouldGetStats = shouldGetStats;
202}
203
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000204- (void)setState:(ARDAppClientState)state {
205 if (_state == state) {
206 return;
207 }
208 _state = state;
209 [_delegate appClient:self didChangeState:_state];
210}
211
212- (void)connectToRoomWithId:(NSString *)roomId
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
tkchind1fb26d2016-02-03 01:51:18 -0800221#if defined(WEBRTC_IOS)
222 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700223 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800224 RTCStartInternalCapture(filePath);
225 }
226#endif
227
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000228 // Request TURN.
229 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000230 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
231 NSError *error) {
232 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700233 RTCLogError("Error retrieving TURN servers: %@",
234 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000235 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000236 ARDAppClient *strongSelf = weakSelf;
237 [strongSelf.iceServers addObjectsFromArray:turnServers];
238 strongSelf.isTurnComplete = YES;
239 [strongSelf startSignalingIfReady];
240 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000241
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000242 // Join room on room server.
243 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700244 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000245 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000246 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000247 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000248 [strongSelf.delegate appClient:strongSelf didError:error];
249 return;
250 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000251 NSError *joinError =
252 [[strongSelf class] errorForJoinResultType:response.result];
253 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700254 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000255 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000256 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000257 return;
258 }
tkchinc3f46a92015-07-23 12:50:55 -0700259 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000260 strongSelf.roomId = response.roomId;
261 strongSelf.clientId = response.clientId;
262 strongSelf.isInitiator = response.isInitiator;
263 for (ARDSignalingMessage *message in response.messages) {
264 if (message.type == kARDSignalingMessageTypeOffer ||
265 message.type == kARDSignalingMessageTypeAnswer) {
266 strongSelf.hasReceivedSdp = YES;
267 [strongSelf.messageQueue insertObject:message atIndex:0];
268 } else {
269 [strongSelf.messageQueue addObject:message];
270 }
271 }
272 strongSelf.webSocketURL = response.webSocketURL;
273 strongSelf.webSocketRestURL = response.webSocketRestURL;
274 [strongSelf registerWithColliderIfReady];
275 [strongSelf startSignalingIfReady];
276 }];
277}
278
279- (void)disconnect {
280 if (_state == kARDAppClientStateDisconnected) {
281 return;
282 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000283 if (self.hasJoinedRoomServerRoom) {
284 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000285 clientId:_clientId
286 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000287 }
288 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000289 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000290 // Tell the other client we're hanging up.
291 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000292 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000293 }
294 // Disconnect from collider.
295 _channel = nil;
296 }
297 _clientId = nil;
298 _roomId = nil;
299 _isInitiator = NO;
300 _hasReceivedSdp = NO;
301 _messageQueue = [NSMutableArray array];
sakalc522e752017-04-05 12:17:48 -0700302 _localVideoTrack = nil;
ivoc14d5dbe2016-07-04 07:06:55 -0700303#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700304 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700305 [_peerConnection stopRtcEventLog];
306#endif
magjedcc8b9062017-07-24 07:32:33 -0700307 [_peerConnection close];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000308 _peerConnection = nil;
309 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800310#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800311 if (kARDAppClientEnableTracing) {
312 RTCStopInternalCapture();
313 }
tkchind1fb26d2016-02-03 01:51:18 -0800314#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000315}
316
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000317#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000318
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000319- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000320 didReceiveMessage:(ARDSignalingMessage *)message {
321 switch (message.type) {
322 case kARDSignalingMessageTypeOffer:
323 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000324 // Offers and answers must be processed before any other message, so we
325 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000326 _hasReceivedSdp = YES;
327 [_messageQueue insertObject:message atIndex:0];
328 break;
329 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700330 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000331 [_messageQueue addObject:message];
332 break;
333 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000334 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000335 [self processSignalingMessage:message];
336 return;
337 }
338 [self drainMessageQueueIfReady];
339}
340
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000341- (void)channel:(id<ARDSignalingChannel>)channel
342 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000343 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000344 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000345 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000346 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000347 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000348 case kARDSignalingChannelStateClosed:
349 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000350 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
351 // completely if the websocket connection fails.
352 [self disconnect];
353 break;
354 }
355}
356
357#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000358// Callbacks for this delegate occur on non-main thread and need to be
359// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000360
361- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700362 didChangeSignalingState:(RTCSignalingState)stateChanged {
363 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000364}
365
366- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700367 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000368 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700369 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000370 (unsigned long)stream.videoTracks.count,
371 (unsigned long)stream.audioTracks.count);
372 if (stream.videoTracks.count) {
373 RTCVideoTrack *videoTrack = stream.videoTracks[0];
374 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
375 }
376 });
377}
378
379- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700380 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700381 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000382}
383
hjon79858f82016-03-13 22:08:26 -0700384- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700385 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000386}
387
388- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700389 didChangeIceConnectionState:(RTCIceConnectionState)newState {
390 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000391 dispatch_async(dispatch_get_main_queue(), ^{
392 [_delegate appClient:self didChangeConnectionState:newState];
393 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000394}
395
396- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700397 didChangeIceGatheringState:(RTCIceGatheringState)newState {
398 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000399}
400
401- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700402 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000403 dispatch_async(dispatch_get_main_queue(), ^{
404 ARDICECandidateMessage *message =
405 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
406 [self sendSignalingMessage:message];
407 });
408}
409
Zeke Chind3325802015-08-14 11:00:02 -0700410- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700411 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
412 dispatch_async(dispatch_get_main_queue(), ^{
413 ARDICECandidateRemovalMessage *message =
414 [[ARDICECandidateRemovalMessage alloc]
415 initWithRemovedCandidates:candidates];
416 [self sendSignalingMessage:message];
417 });
418}
419
420- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700421 didOpenDataChannel:(RTCDataChannel *)dataChannel {
422}
423
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000424#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000425// Callbacks for this delegate occur on non-main thread and need to be
426// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000427
428- (void)peerConnection:(RTCPeerConnection *)peerConnection
429 didCreateSessionDescription:(RTCSessionDescription *)sdp
430 error:(NSError *)error {
431 dispatch_async(dispatch_get_main_queue(), ^{
432 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700433 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000434 [self disconnect];
435 NSDictionary *userInfo = @{
436 NSLocalizedDescriptionKey: @"Failed to create session description.",
437 };
438 NSError *sdpError =
439 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
440 code:kARDAppClientErrorCreateSDP
441 userInfo:userInfo];
442 [_delegate appClient:self didError:sdpError];
443 return;
444 }
sakal68b5df92017-03-17 09:01:59 -0700445 // Prefer codec from settings if available.
446 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700447 [ARDSDPUtils descriptionForDescription:sdp
sakalc4adacf2017-03-28 01:22:48 -0700448 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700449 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700450 [_peerConnection setLocalDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700451 completionHandler:^(NSError *error) {
452 ARDAppClient *strongSelf = weakSelf;
453 [strongSelf peerConnection:strongSelf.peerConnection
454 didSetSessionDescriptionWithError:error];
455 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000456 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700457 [[ARDSessionDescriptionMessage alloc]
sakal68b5df92017-03-17 09:01:59 -0700458 initWithDescription:sdpPreferringCodec];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000459 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800460 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000461 });
462}
463
464- (void)peerConnection:(RTCPeerConnection *)peerConnection
465 didSetSessionDescriptionWithError:(NSError *)error {
466 dispatch_async(dispatch_get_main_queue(), ^{
467 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700468 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000469 [self disconnect];
470 NSDictionary *userInfo = @{
471 NSLocalizedDescriptionKey: @"Failed to set session description.",
472 };
473 NSError *sdpError =
474 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
475 code:kARDAppClientErrorSetSDP
476 userInfo:userInfo];
477 [_delegate appClient:self didError:sdpError];
478 return;
479 }
480 // If we're answering and we've just set the remote offer we need to create
481 // an answer and set the local description.
482 if (!_isInitiator && !_peerConnection.localDescription) {
483 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700484 __weak ARDAppClient *weakSelf = self;
485 [_peerConnection answerForConstraints:constraints
486 completionHandler:^(RTCSessionDescription *sdp,
487 NSError *error) {
488 ARDAppClient *strongSelf = weakSelf;
489 [strongSelf peerConnection:strongSelf.peerConnection
490 didCreateSessionDescription:sdp
491 error:error];
492 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000493 }
494 });
495}
496
497#pragma mark - Private
498
tkchin204177f2016-06-14 15:03:11 -0700499#if defined(WEBRTC_IOS)
500
501- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
502 NSParameterAssert(fileName.length);
503 NSArray *paths = NSSearchPathForDirectoriesInDomains(
504 NSDocumentDirectory, NSUserDomainMask, YES);
505 NSString *documentsDirPath = paths.firstObject;
506 NSString *filePath =
507 [documentsDirPath stringByAppendingPathComponent:fileName];
508 return filePath;
509}
510
511#endif
512
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000513- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000514 return _clientId.length;
515}
516
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000517// Begins the peer connection connection process if we have both joined a room
518// on the room server and tried to obtain a TURN server. Otherwise does nothing.
519// A peer connection object will be created with a stream that contains local
520// audio and video capture. If this client is the caller, an offer is created as
521// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000522- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000523 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000524 return;
525 }
526 self.state = kARDAppClientStateConnected;
527
528 // Create peer connection.
529 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700530 RTCConfiguration *config = [[RTCConfiguration alloc] init];
531 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700532 _peerConnection = [_factory peerConnectionWithConfiguration:config
533 constraints:constraints
534 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700535 // Create AV senders.
536 [self createAudioSender];
537 [self createVideoSender];
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;
sakal68b5df92017-03-17 09:01:59 -0700599 // Prefer codec from settings if available.
600 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700601 [ARDSDPUtils descriptionForDescription:description
sakalc4adacf2017-03-28 01:22:48 -0700602 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700603 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700604 [_peerConnection setRemoteDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700605 completionHandler:^(NSError *error) {
606 ARDAppClient *strongSelf = weakSelf;
607 [strongSelf peerConnection:strongSelf.peerConnection
608 didSetSessionDescriptionWithError:error];
609 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000610 break;
611 }
612 case kARDSignalingMessageTypeCandidate: {
613 ARDICECandidateMessage *candidateMessage =
614 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700615 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000616 break;
617 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700618 case kARDSignalingMessageTypeCandidateRemoval: {
619 ARDICECandidateRemovalMessage *candidateMessage =
620 (ARDICECandidateRemovalMessage *)message;
621 [_peerConnection removeIceCandidates:candidateMessage.candidates];
622 break;
623 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000624 case kARDSignalingMessageTypeBye:
625 // Other client disconnected.
626 // TODO(tkchin): support waiting in room for next client. For now just
627 // disconnect.
628 [self disconnect];
629 break;
630 }
631}
632
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000633// Sends a signaling message to the other client. The caller will send messages
634// through the room server, whereas the callee will send messages over the
635// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000636- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
637 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000638 __weak ARDAppClient *weakSelf = self;
639 [_roomServerClient sendMessage:message
640 forRoomId:_roomId
641 clientId:_clientId
642 completionHandler:^(ARDMessageResponse *response,
643 NSError *error) {
644 ARDAppClient *strongSelf = weakSelf;
645 if (error) {
646 [strongSelf.delegate appClient:strongSelf didError:error];
647 return;
648 }
649 NSError *messageError =
650 [[strongSelf class] errorForMessageResultType:response.result];
651 if (messageError) {
652 [strongSelf.delegate appClient:strongSelf didError:messageError];
653 return;
654 }
655 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000656 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000657 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000658 }
659}
660
skvladf3569c82016-04-29 15:30:16 -0700661- (RTCRtpSender *)createVideoSender {
662 RTCRtpSender *sender =
663 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
664 streamId:kARDMediaStreamId];
sakalc522e752017-04-05 12:17:48 -0700665 _localVideoTrack = [self createLocalVideoTrack];
666 if (_localVideoTrack) {
667 sender.track = _localVideoTrack;
668 [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700669 }
denicija9af2b602016-11-17 00:43:43 -0800670
skvladf3569c82016-04-29 15:30:16 -0700671 return sender;
672}
673
denicija9af2b602016-11-17 00:43:43 -0800674- (void)setMaxBitrateForPeerConnectionVideoSender {
675 for (RTCRtpSender *sender in _peerConnection.senders) {
676 if (sender.track != nil) {
677 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700678 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800679 }
680 }
denicija8c375de2016-11-08 06:28:17 -0800681 }
682}
683
denicija9af2b602016-11-17 00:43:43 -0800684- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
685 if (maxBitrate.intValue <= 0) {
686 return;
687 }
688
689 RTCRtpParameters *parametersToModify = sender.parameters;
690 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
691 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
692 }
693 [sender setParameters:parametersToModify];
694}
695
skvladf3569c82016-04-29 15:30:16 -0700696- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700697 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
698 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
699 RTCAudioTrack *track = [_factory audioTrackWithSource:source
700 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700701 RTCRtpSender *sender =
702 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
703 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700704 sender.track = track;
705 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700706}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000707
Zeke Chin57cc74e2015-05-05 07:52:31 -0700708- (RTCVideoTrack *)createLocalVideoTrack {
709 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000710 // The iOS simulator doesn't provide any sort of camera capture
711 // support or emulation (http://goo.gl/rHAnC1) so don't bother
712 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700713#if !TARGET_IPHONE_SIMULATOR
Anders Carlssone1500582017-06-15 16:05:13 +0200714 if (![_settings currentAudioOnlySettingFromStore]) {
sakalc522e752017-04-05 12:17:48 -0700715 RTCVideoSource *source = [_factory videoSource];
716 RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
717 [_delegate appClient:self didCreateLocalCapturer:capturer];
haysc913e6452015-10-02 11:44:03 -0700718 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700719 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700720 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700721 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000722#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700723 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000724}
725
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000726#pragma mark - Collider methods
727
728- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000729 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000730 return;
731 }
732 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000733 if (!_channel) {
734 _channel =
735 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
736 restURL:_websocketRestURL
737 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700738 if (_isLoopback) {
739 _loopbackChannel =
740 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
741 restURL:_websocketRestURL];
742 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000743 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000744 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700745 if (_isLoopback) {
746 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
747 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000748}
749
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000750#pragma mark - Defaults
751
tkchinab1293a2016-08-30 12:35:05 -0700752 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
Anders Carlssone1500582017-06-15 16:05:13 +0200753 NSString *valueLevelControl = [_settings currentUseLevelControllerSettingFromStore] ?
754 kRTCMediaConstraintsValueTrue :
755 kRTCMediaConstraintsValueFalse;
tkchinab1293a2016-08-30 12:35:05 -0700756 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700757 RTCMediaConstraints *constraints =
758 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
759 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700760 return constraints;
761}
762
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000763- (RTCMediaConstraints *)defaultAnswerConstraints {
764 return [self defaultOfferConstraints];
765}
766
767- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700768 NSDictionary *mandatoryConstraints = @{
769 @"OfferToReceiveAudio" : @"true",
770 @"OfferToReceiveVideo" : @"true"
771 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000772 RTCMediaConstraints* constraints =
773 [[RTCMediaConstraints alloc]
774 initWithMandatoryConstraints:mandatoryConstraints
775 optionalConstraints:nil];
776 return constraints;
777}
778
779- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000780 if (_defaultPeerConnectionConstraints) {
781 return _defaultPeerConnectionConstraints;
782 }
haysc913e6452015-10-02 11:44:03 -0700783 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700784 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000785 RTCMediaConstraints* constraints =
786 [[RTCMediaConstraints alloc]
787 initWithMandatoryConstraints:nil
788 optionalConstraints:optionalConstraints];
789 return constraints;
790}
791
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000792#pragma mark - Errors
793
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000794+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000795 NSError *error = nil;
796 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000797 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000798 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000799 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000800 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
801 code:kARDAppClientErrorUnknown
802 userInfo:@{
803 NSLocalizedDescriptionKey: @"Unknown error.",
804 }];
805 break;
806 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000807 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000808 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
809 code:kARDAppClientErrorRoomFull
810 userInfo:@{
811 NSLocalizedDescriptionKey: @"Room is full.",
812 }];
813 break;
814 }
815 }
816 return error;
817}
818
819+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
820 NSError *error = nil;
821 switch (resultType) {
822 case kARDMessageResultTypeSuccess:
823 break;
824 case kARDMessageResultTypeUnknown:
825 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
826 code:kARDAppClientErrorUnknown
827 userInfo:@{
828 NSLocalizedDescriptionKey: @"Unknown error.",
829 }];
830 break;
831 case kARDMessageResultTypeInvalidClient:
832 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
833 code:kARDAppClientErrorInvalidClient
834 userInfo:@{
835 NSLocalizedDescriptionKey: @"Invalid client.",
836 }];
837 break;
838 case kARDMessageResultTypeInvalidRoom:
839 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
840 code:kARDAppClientErrorInvalidRoom
841 userInfo:@{
842 NSLocalizedDescriptionKey: @"Invalid room.",
843 }];
844 break;
845 }
846 return error;
847}
848
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000849@end