blob: ef38138319ac56b2e9655eaec5cedef932cbd2f7 [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"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000026#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000027#import "ARDMessageResponse.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070028#import "ARDSDPUtils.h"
sakalc4adacf2017-03-28 01:22:48 -070029#import "ARDSettingsModel.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000030#import "ARDSignalingMessage.h"
sakalc4adacf2017-03-28 01:22:48 -070031#import "ARDTURNClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000032#import "ARDUtilities.h"
33#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070034#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000035#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070036
kthelgasoncc882af2017-01-13 05:59:46 -080037static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000038
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000039static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
40static NSInteger const kARDAppClientErrorUnknown = -1;
41static NSInteger const kARDAppClientErrorRoomFull = -2;
42static NSInteger const kARDAppClientErrorCreateSDP = -3;
43static NSInteger const kARDAppClientErrorSetSDP = -4;
44static NSInteger const kARDAppClientErrorInvalidClient = -5;
45static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070046static NSString * const kARDMediaStreamId = @"ARDAMS";
47static NSString * const kARDAudioTrackId = @"ARDAMSa0";
48static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080049static NSString * const kARDVideoTrackKind = @"video";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000050
tkchin204177f2016-06-14 15:03:11 -070051// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080052static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070053static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070054static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070055static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
denicija9af2b602016-11-17 00:43:43 -080056static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080057
Zeke Chind3325802015-08-14 11:00:02 -070058// We need a proxy to NSTimer because it causes a strong retain cycle. When
59// using the proxy, |invalidate| must be called before it properly deallocs.
60@interface ARDTimerProxy : NSObject
61
62- (instancetype)initWithInterval:(NSTimeInterval)interval
63 repeats:(BOOL)repeats
64 timerHandler:(void (^)(void))timerHandler;
65- (void)invalidate;
66
67@end
68
69@implementation ARDTimerProxy {
70 NSTimer *_timer;
71 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070072}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000073
Zeke Chind3325802015-08-14 11:00:02 -070074- (instancetype)initWithInterval:(NSTimeInterval)interval
75 repeats:(BOOL)repeats
76 timerHandler:(void (^)(void))timerHandler {
77 NSParameterAssert(timerHandler);
78 if (self = [super init]) {
79 _timerHandler = timerHandler;
80 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
81 target:self
82 selector:@selector(timerDidFire:)
83 userInfo:nil
84 repeats:repeats];
85 }
86 return self;
87}
88
89- (void)invalidate {
90 [_timer invalidate];
91}
92
93- (void)timerDidFire:(NSTimer *)timer {
94 _timerHandler();
95}
96
97@end
98
99@implementation ARDAppClient {
100 RTCFileLogger *_fileLogger;
101 ARDTimerProxy *_statsTimer;
sakalc4adacf2017-03-28 01:22:48 -0700102 ARDSettingsModel *_settings;
Zeke Chind3325802015-08-14 11:00:02 -0700103}
104
105@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000106@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700107@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000108@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000109@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700110@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000111@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000112@synthesize peerConnection = _peerConnection;
113@synthesize factory = _factory;
114@synthesize messageQueue = _messageQueue;
115@synthesize isTurnComplete = _isTurnComplete;
116@synthesize hasReceivedSdp = _hasReceivedSdp;
117@synthesize roomId = _roomId;
118@synthesize clientId = _clientId;
119@synthesize isInitiator = _isInitiator;
120@synthesize iceServers = _iceServers;
121@synthesize webSocketURL = _websocketURL;
122@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000123@synthesize defaultPeerConnectionConstraints =
124 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700125@synthesize isLoopback = _isLoopback;
126@synthesize isAudioOnly = _isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700127@synthesize shouldMakeAecDump = _shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700128@synthesize shouldUseLevelControl = _shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000129
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000130- (instancetype)init {
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
haysc913e6452015-10-02 11:44:03 -0700214 isLoopback:(BOOL)isLoopback
peah5085b0c2016-08-25 22:15:14 -0700215 isAudioOnly:(BOOL)isAudioOnly
tkchinab1293a2016-08-30 12:35:05 -0700216 shouldMakeAecDump:(BOOL)shouldMakeAecDump
217 shouldUseLevelControl:(BOOL)shouldUseLevelControl {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000218 NSParameterAssert(roomId.length);
219 NSParameterAssert(_state == kARDAppClientStateDisconnected);
sakalc4adacf2017-03-28 01:22:48 -0700220 _settings = settings;
haysc913e6452015-10-02 11:44:03 -0700221 _isLoopback = isLoopback;
222 _isAudioOnly = isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700223 _shouldMakeAecDump = shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700224 _shouldUseLevelControl = shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000225 self.state = kARDAppClientStateConnecting;
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];
ivoc14d5dbe2016-07-04 07:06:55 -0700308#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700309 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700310 [_peerConnection stopRtcEventLog];
311#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000312 _peerConnection = nil;
313 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800314#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800315 if (kARDAppClientEnableTracing) {
316 RTCStopInternalCapture();
317 }
tkchind1fb26d2016-02-03 01:51:18 -0800318#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000319}
320
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000321#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000322
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000323- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000324 didReceiveMessage:(ARDSignalingMessage *)message {
325 switch (message.type) {
326 case kARDSignalingMessageTypeOffer:
327 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000328 // Offers and answers must be processed before any other message, so we
329 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000330 _hasReceivedSdp = YES;
331 [_messageQueue insertObject:message atIndex:0];
332 break;
333 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700334 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000335 [_messageQueue addObject:message];
336 break;
337 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000338 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000339 [self processSignalingMessage:message];
340 return;
341 }
342 [self drainMessageQueueIfReady];
343}
344
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000345- (void)channel:(id<ARDSignalingChannel>)channel
346 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000347 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000348 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000349 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000350 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000351 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000352 case kARDSignalingChannelStateClosed:
353 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000354 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
355 // completely if the websocket connection fails.
356 [self disconnect];
357 break;
358 }
359}
360
361#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000362// Callbacks for this delegate occur on non-main thread and need to be
363// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000364
365- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700366 didChangeSignalingState:(RTCSignalingState)stateChanged {
367 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000368}
369
370- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700371 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000372 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700373 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000374 (unsigned long)stream.videoTracks.count,
375 (unsigned long)stream.audioTracks.count);
376 if (stream.videoTracks.count) {
377 RTCVideoTrack *videoTrack = stream.videoTracks[0];
378 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
379 }
380 });
381}
382
383- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700384 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700385 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000386}
387
hjon79858f82016-03-13 22:08:26 -0700388- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700389 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000390}
391
392- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700393 didChangeIceConnectionState:(RTCIceConnectionState)newState {
394 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000395 dispatch_async(dispatch_get_main_queue(), ^{
396 [_delegate appClient:self didChangeConnectionState:newState];
397 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000398}
399
400- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700401 didChangeIceGatheringState:(RTCIceGatheringState)newState {
402 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000403}
404
405- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700406 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000407 dispatch_async(dispatch_get_main_queue(), ^{
408 ARDICECandidateMessage *message =
409 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
410 [self sendSignalingMessage:message];
411 });
412}
413
Zeke Chind3325802015-08-14 11:00:02 -0700414- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700415 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
416 dispatch_async(dispatch_get_main_queue(), ^{
417 ARDICECandidateRemovalMessage *message =
418 [[ARDICECandidateRemovalMessage alloc]
419 initWithRemovedCandidates:candidates];
420 [self sendSignalingMessage:message];
421 });
422}
423
424- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700425 didOpenDataChannel:(RTCDataChannel *)dataChannel {
426}
427
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000428#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000429// Callbacks for this delegate occur on non-main thread and need to be
430// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000431
432- (void)peerConnection:(RTCPeerConnection *)peerConnection
433 didCreateSessionDescription:(RTCSessionDescription *)sdp
434 error:(NSError *)error {
435 dispatch_async(dispatch_get_main_queue(), ^{
436 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700437 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000438 [self disconnect];
439 NSDictionary *userInfo = @{
440 NSLocalizedDescriptionKey: @"Failed to create session description.",
441 };
442 NSError *sdpError =
443 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
444 code:kARDAppClientErrorCreateSDP
445 userInfo:userInfo];
446 [_delegate appClient:self didError:sdpError];
447 return;
448 }
sakal68b5df92017-03-17 09:01:59 -0700449 // Prefer codec from settings if available.
450 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700451 [ARDSDPUtils descriptionForDescription:sdp
sakalc4adacf2017-03-28 01:22:48 -0700452 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700453 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700454 [_peerConnection setLocalDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700455 completionHandler:^(NSError *error) {
456 ARDAppClient *strongSelf = weakSelf;
457 [strongSelf peerConnection:strongSelf.peerConnection
458 didSetSessionDescriptionWithError:error];
459 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000460 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700461 [[ARDSessionDescriptionMessage alloc]
sakal68b5df92017-03-17 09:01:59 -0700462 initWithDescription:sdpPreferringCodec];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000463 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800464 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000465 });
466}
467
468- (void)peerConnection:(RTCPeerConnection *)peerConnection
469 didSetSessionDescriptionWithError:(NSError *)error {
470 dispatch_async(dispatch_get_main_queue(), ^{
471 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700472 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000473 [self disconnect];
474 NSDictionary *userInfo = @{
475 NSLocalizedDescriptionKey: @"Failed to set session description.",
476 };
477 NSError *sdpError =
478 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
479 code:kARDAppClientErrorSetSDP
480 userInfo:userInfo];
481 [_delegate appClient:self didError:sdpError];
482 return;
483 }
484 // If we're answering and we've just set the remote offer we need to create
485 // an answer and set the local description.
486 if (!_isInitiator && !_peerConnection.localDescription) {
487 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700488 __weak ARDAppClient *weakSelf = self;
489 [_peerConnection answerForConstraints:constraints
490 completionHandler:^(RTCSessionDescription *sdp,
491 NSError *error) {
492 ARDAppClient *strongSelf = weakSelf;
493 [strongSelf peerConnection:strongSelf.peerConnection
494 didCreateSessionDescription:sdp
495 error:error];
496 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000497 }
498 });
499}
500
501#pragma mark - Private
502
tkchin204177f2016-06-14 15:03:11 -0700503#if defined(WEBRTC_IOS)
504
505- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
506 NSParameterAssert(fileName.length);
507 NSArray *paths = NSSearchPathForDirectoriesInDomains(
508 NSDocumentDirectory, NSUserDomainMask, YES);
509 NSString *documentsDirPath = paths.firstObject;
510 NSString *filePath =
511 [documentsDirPath stringByAppendingPathComponent:fileName];
512 return filePath;
513}
514
515#endif
516
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000517- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000518 return _clientId.length;
519}
520
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000521// Begins the peer connection connection process if we have both joined a room
522// on the room server and tried to obtain a TURN server. Otherwise does nothing.
523// A peer connection object will be created with a stream that contains local
524// audio and video capture. If this client is the caller, an offer is created as
525// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000526- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000527 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000528 return;
529 }
530 self.state = kARDAppClientStateConnected;
531
532 // Create peer connection.
533 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700534 RTCConfiguration *config = [[RTCConfiguration alloc] init];
535 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700536 _peerConnection = [_factory peerConnectionWithConfiguration:config
537 constraints:constraints
538 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700539 // Create AV senders.
540 [self createAudioSender];
541 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000542 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000543 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700544 __weak ARDAppClient *weakSelf = self;
545 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
546 completionHandler:^(RTCSessionDescription *sdp,
547 NSError *error) {
548 ARDAppClient *strongSelf = weakSelf;
549 [strongSelf peerConnection:strongSelf.peerConnection
550 didCreateSessionDescription:sdp
551 error:error];
552 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000553 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000554 // Check if we've received an offer.
555 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000556 }
ivoc14d5dbe2016-07-04 07:06:55 -0700557#if defined(WEBRTC_IOS)
558 // Start event log.
559 if (kARDAppClientEnableRtcEventLog) {
560 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
561 if (![_peerConnection startRtcEventLogWithFilePath:filePath
562 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
563 RTCLogError(@"Failed to start event logging.");
564 }
565 }
peah5085b0c2016-08-25 22:15:14 -0700566
567 // Start aecdump diagnostic recording.
568 if (_shouldMakeAecDump) {
tkchinfce0e2c2016-08-30 12:58:11 -0700569 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
570 if (![_factory startAecDumpWithFilePath:filePath
571 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
572 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700573 }
574 }
ivoc14d5dbe2016-07-04 07:06:55 -0700575#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000576}
577
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000578// Processes the messages that we've received from the room server and the
579// signaling channel. The offer or answer message must be processed before other
580// signaling messages, however they can arrive out of order. Hence, this method
581// only processes pending messages if there is a peer connection object and
582// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000583- (void)drainMessageQueueIfReady {
584 if (!_peerConnection || !_hasReceivedSdp) {
585 return;
586 }
587 for (ARDSignalingMessage *message in _messageQueue) {
588 [self processSignalingMessage:message];
589 }
590 [_messageQueue removeAllObjects];
591}
592
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000593// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000594- (void)processSignalingMessage:(ARDSignalingMessage *)message {
595 NSParameterAssert(_peerConnection ||
596 message.type == kARDSignalingMessageTypeBye);
597 switch (message.type) {
598 case kARDSignalingMessageTypeOffer:
599 case kARDSignalingMessageTypeAnswer: {
600 ARDSessionDescriptionMessage *sdpMessage =
601 (ARDSessionDescriptionMessage *)message;
602 RTCSessionDescription *description = sdpMessage.sessionDescription;
sakal68b5df92017-03-17 09:01:59 -0700603 // Prefer codec from settings if available.
604 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700605 [ARDSDPUtils descriptionForDescription:description
sakalc4adacf2017-03-28 01:22:48 -0700606 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700607 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700608 [_peerConnection setRemoteDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700609 completionHandler:^(NSError *error) {
610 ARDAppClient *strongSelf = weakSelf;
611 [strongSelf peerConnection:strongSelf.peerConnection
612 didSetSessionDescriptionWithError:error];
613 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000614 break;
615 }
616 case kARDSignalingMessageTypeCandidate: {
617 ARDICECandidateMessage *candidateMessage =
618 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700619 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000620 break;
621 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700622 case kARDSignalingMessageTypeCandidateRemoval: {
623 ARDICECandidateRemovalMessage *candidateMessage =
624 (ARDICECandidateRemovalMessage *)message;
625 [_peerConnection removeIceCandidates:candidateMessage.candidates];
626 break;
627 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000628 case kARDSignalingMessageTypeBye:
629 // Other client disconnected.
630 // TODO(tkchin): support waiting in room for next client. For now just
631 // disconnect.
632 [self disconnect];
633 break;
634 }
635}
636
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000637// Sends a signaling message to the other client. The caller will send messages
638// through the room server, whereas the callee will send messages over the
639// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000640- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
641 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000642 __weak ARDAppClient *weakSelf = self;
643 [_roomServerClient sendMessage:message
644 forRoomId:_roomId
645 clientId:_clientId
646 completionHandler:^(ARDMessageResponse *response,
647 NSError *error) {
648 ARDAppClient *strongSelf = weakSelf;
649 if (error) {
650 [strongSelf.delegate appClient:strongSelf didError:error];
651 return;
652 }
653 NSError *messageError =
654 [[strongSelf class] errorForMessageResultType:response.result];
655 if (messageError) {
656 [strongSelf.delegate appClient:strongSelf didError:messageError];
657 return;
658 }
659 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000660 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000661 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000662 }
663}
664
skvladf3569c82016-04-29 15:30:16 -0700665- (RTCRtpSender *)createVideoSender {
666 RTCRtpSender *sender =
667 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
668 streamId:kARDMediaStreamId];
669 RTCVideoTrack *track = [self createLocalVideoTrack];
670 if (track) {
671 sender.track = track;
672 [_delegate appClient:self didReceiveLocalVideoTrack:track];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700673 }
denicija9af2b602016-11-17 00:43:43 -0800674
skvladf3569c82016-04-29 15:30:16 -0700675 return sender;
676}
677
denicija9af2b602016-11-17 00:43:43 -0800678- (void)setMaxBitrateForPeerConnectionVideoSender {
679 for (RTCRtpSender *sender in _peerConnection.senders) {
680 if (sender.track != nil) {
681 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700682 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800683 }
684 }
denicija8c375de2016-11-08 06:28:17 -0800685 }
686}
687
denicija9af2b602016-11-17 00:43:43 -0800688- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
689 if (maxBitrate.intValue <= 0) {
690 return;
691 }
692
693 RTCRtpParameters *parametersToModify = sender.parameters;
694 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
695 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
696 }
697 [sender setParameters:parametersToModify];
698}
699
skvladf3569c82016-04-29 15:30:16 -0700700- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700701 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
702 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
703 RTCAudioTrack *track = [_factory audioTrackWithSource:source
704 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700705 RTCRtpSender *sender =
706 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
707 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700708 sender.track = track;
709 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700710}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000711
Zeke Chin57cc74e2015-05-05 07:52:31 -0700712- (RTCVideoTrack *)createLocalVideoTrack {
713 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000714 // The iOS simulator doesn't provide any sort of camera capture
715 // support or emulation (http://goo.gl/rHAnC1) so don't bother
716 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700717#if !TARGET_IPHONE_SIMULATOR
haysc913e6452015-10-02 11:44:03 -0700718 if (!_isAudioOnly) {
denicijad17d5362016-11-02 02:56:09 -0700719 RTCMediaConstraints *cameraConstraints =
720 [self cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700721 RTCAVFoundationVideoSource *source =
denicijad17d5362016-11-02 02:56:09 -0700722 [_factory avFoundationVideoSourceWithConstraints:cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700723 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700724 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700725 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700726 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000727#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700728 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000729}
730
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000731#pragma mark - Collider methods
732
733- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000734 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000735 return;
736 }
737 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000738 if (!_channel) {
739 _channel =
740 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
741 restURL:_websocketRestURL
742 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700743 if (_isLoopback) {
744 _loopbackChannel =
745 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
746 restURL:_websocketRestURL];
747 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000748 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000749 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700750 if (_isLoopback) {
751 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
752 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000753}
754
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000755#pragma mark - Defaults
756
tkchinab1293a2016-08-30 12:35:05 -0700757 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
758 NSString *valueLevelControl = _shouldUseLevelControl ?
759 kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
760 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700761 RTCMediaConstraints *constraints =
762 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
763 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700764 return constraints;
765}
766
denicijad17d5362016-11-02 02:56:09 -0700767- (RTCMediaConstraints *)cameraConstraints {
sakalc4adacf2017-03-28 01:22:48 -0700768 RTCMediaConstraints *cameraConstraints = [[RTCMediaConstraints alloc]
769 initWithMandatoryConstraints:nil
770 optionalConstraints:[_settings currentMediaConstraintFromStoreAsRTCDictionary]];
771 return cameraConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000772}
773
774- (RTCMediaConstraints *)defaultAnswerConstraints {
775 return [self defaultOfferConstraints];
776}
777
778- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700779 NSDictionary *mandatoryConstraints = @{
780 @"OfferToReceiveAudio" : @"true",
781 @"OfferToReceiveVideo" : @"true"
782 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000783 RTCMediaConstraints* constraints =
784 [[RTCMediaConstraints alloc]
785 initWithMandatoryConstraints:mandatoryConstraints
786 optionalConstraints:nil];
787 return constraints;
788}
789
790- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000791 if (_defaultPeerConnectionConstraints) {
792 return _defaultPeerConnectionConstraints;
793 }
haysc913e6452015-10-02 11:44:03 -0700794 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700795 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000796 RTCMediaConstraints* constraints =
797 [[RTCMediaConstraints alloc]
798 initWithMandatoryConstraints:nil
799 optionalConstraints:optionalConstraints];
800 return constraints;
801}
802
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000803#pragma mark - Errors
804
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000805+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000806 NSError *error = nil;
807 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000808 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000809 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000810 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000811 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
812 code:kARDAppClientErrorUnknown
813 userInfo:@{
814 NSLocalizedDescriptionKey: @"Unknown error.",
815 }];
816 break;
817 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000818 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000819 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
820 code:kARDAppClientErrorRoomFull
821 userInfo:@{
822 NSLocalizedDescriptionKey: @"Room is full.",
823 }];
824 break;
825 }
826 }
827 return error;
828}
829
830+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
831 NSError *error = nil;
832 switch (resultType) {
833 case kARDMessageResultTypeSuccess:
834 break;
835 case kARDMessageResultTypeUnknown:
836 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
837 code:kARDAppClientErrorUnknown
838 userInfo:@{
839 NSLocalizedDescriptionKey: @"Unknown error.",
840 }];
841 break;
842 case kARDMessageResultTypeInvalidClient:
843 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
844 code:kARDAppClientErrorInvalidClient
845 userInfo:@{
846 NSLocalizedDescriptionKey: @"Invalid client.",
847 }];
848 break;
849 case kARDMessageResultTypeInvalidRoom:
850 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
851 code:kARDAppClientErrorInvalidRoom
852 userInfo:@{
853 NSLocalizedDescriptionKey: @"Invalid room.",
854 }];
855 break;
856 }
857 return error;
858}
859
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000860@end