blob: 2dfc617637ebe8aee36a8221d59009657a636f07 [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"
Anders Carlsson7e042812017-10-05 16:55:38 +020035#import "ARDVideoDecoderFactory.h"
36#import "ARDVideoEncoderFactory.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000037#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070038#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000039#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070040
kthelgasoncc882af2017-01-13 05:59:46 -080041static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000042
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";
denicija9af2b602016-11-17 00:43:43 -080053static NSString * const kARDVideoTrackKind = @"video";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000054
tkchin204177f2016-06-14 15:03:11 -070055// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080056static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070057static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070058static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070059static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
denicija9af2b602016-11-17 00:43:43 -080060static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080061
Zeke Chind3325802015-08-14 11:00:02 -070062// We need a proxy to NSTimer because it causes a strong retain cycle. When
63// using the proxy, |invalidate| must be called before it properly deallocs.
64@interface ARDTimerProxy : NSObject
65
66- (instancetype)initWithInterval:(NSTimeInterval)interval
67 repeats:(BOOL)repeats
68 timerHandler:(void (^)(void))timerHandler;
69- (void)invalidate;
70
71@end
72
73@implementation ARDTimerProxy {
74 NSTimer *_timer;
75 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070076}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000077
Zeke Chind3325802015-08-14 11:00:02 -070078- (instancetype)initWithInterval:(NSTimeInterval)interval
79 repeats:(BOOL)repeats
80 timerHandler:(void (^)(void))timerHandler {
81 NSParameterAssert(timerHandler);
82 if (self = [super init]) {
83 _timerHandler = timerHandler;
84 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
85 target:self
86 selector:@selector(timerDidFire:)
87 userInfo:nil
88 repeats:repeats];
89 }
90 return self;
91}
92
93- (void)invalidate {
94 [_timer invalidate];
95}
96
97- (void)timerDidFire:(NSTimer *)timer {
98 _timerHandler();
99}
100
101@end
102
103@implementation ARDAppClient {
104 RTCFileLogger *_fileLogger;
105 ARDTimerProxy *_statsTimer;
sakalc4adacf2017-03-28 01:22:48 -0700106 ARDSettingsModel *_settings;
sakalc522e752017-04-05 12:17:48 -0700107 RTCVideoTrack *_localVideoTrack;
Zeke Chind3325802015-08-14 11:00:02 -0700108}
109
110@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000111@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700112@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000113@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000114@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700115@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000116@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000117@synthesize peerConnection = _peerConnection;
118@synthesize factory = _factory;
119@synthesize messageQueue = _messageQueue;
120@synthesize isTurnComplete = _isTurnComplete;
121@synthesize hasReceivedSdp = _hasReceivedSdp;
122@synthesize roomId = _roomId;
123@synthesize clientId = _clientId;
124@synthesize isInitiator = _isInitiator;
125@synthesize iceServers = _iceServers;
126@synthesize webSocketURL = _websocketURL;
127@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000128@synthesize defaultPeerConnectionConstraints =
129 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700130@synthesize isLoopback = _isLoopback;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000131
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000132- (instancetype)init {
sakalc4adacf2017-03-28 01:22:48 -0700133 return [self initWithDelegate:nil];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000134}
135
sakalc4adacf2017-03-28 01:22:48 -0700136- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000137 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000138 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000139 _delegate = delegate;
kthelgasoncc882af2017-01-13 05:59:46 -0800140 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
141 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000142 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000143 }
144 return self;
145}
146
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000147// TODO(tkchin): Provide signaling channel factory interface so we can recreate
148// channel if we need to on network failure. Also, make this the default public
149// constructor.
150- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
151 signalingChannel:(id<ARDSignalingChannel>)channel
152 turnClient:(id<ARDTURNClient>)turnClient
153 delegate:(id<ARDAppClientDelegate>)delegate {
154 NSParameterAssert(rsClient);
155 NSParameterAssert(channel);
156 NSParameterAssert(turnClient);
157 if (self = [super init]) {
158 _roomServerClient = rsClient;
159 _channel = channel;
160 _turnClient = turnClient;
161 _delegate = delegate;
162 [self configure];
163 }
164 return self;
165}
166
167- (void)configure {
Anders Carlsson7e042812017-10-05 16:55:38 +0200168 ARDVideoDecoderFactory *decoderFactory = [[ARDVideoDecoderFactory alloc] init];
169 ARDVideoEncoderFactory *encoderFactory = [[ARDVideoEncoderFactory alloc] init];
170 _factory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory:encoderFactory
171 decoderFactory:decoderFactory];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000172 _messageQueue = [NSMutableArray array];
kthelgasoncc882af2017-01-13 05:59:46 -0800173 _iceServers = [NSMutableArray array];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700174 _fileLogger = [[RTCFileLogger alloc] init];
175 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000176}
177
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000178- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700179 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000180 [self disconnect];
181}
182
Zeke Chind3325802015-08-14 11:00:02 -0700183- (void)setShouldGetStats:(BOOL)shouldGetStats {
184 if (_shouldGetStats == shouldGetStats) {
185 return;
186 }
187 if (shouldGetStats) {
188 __weak ARDAppClient *weakSelf = self;
189 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
190 repeats:YES
191 timerHandler:^{
192 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700193 [strongSelf.peerConnection statsForTrack:nil
194 statsOutputLevel:RTCStatsOutputLevelDebug
195 completionHandler:^(NSArray *stats) {
196 dispatch_async(dispatch_get_main_queue(), ^{
197 ARDAppClient *strongSelf = weakSelf;
198 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
199 });
200 }];
Zeke Chind3325802015-08-14 11:00:02 -0700201 }];
202 } else {
203 [_statsTimer invalidate];
204 _statsTimer = nil;
205 }
206 _shouldGetStats = shouldGetStats;
207}
208
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000209- (void)setState:(ARDAppClientState)state {
210 if (_state == state) {
211 return;
212 }
213 _state = state;
214 [_delegate appClient:self didChangeState:_state];
215}
216
217- (void)connectToRoomWithId:(NSString *)roomId
sakalc4adacf2017-03-28 01:22:48 -0700218 settings:(ARDSettingsModel *)settings
Anders Carlssone1500582017-06-15 16:05:13 +0200219 isLoopback:(BOOL)isLoopback {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000220 NSParameterAssert(roomId.length);
221 NSParameterAssert(_state == kARDAppClientStateDisconnected);
sakalc4adacf2017-03-28 01:22:48 -0700222 _settings = settings;
haysc913e6452015-10-02 11:44:03 -0700223 _isLoopback = isLoopback;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000224 self.state = kARDAppClientStateConnecting;
225
tkchind1fb26d2016-02-03 01:51:18 -0800226#if defined(WEBRTC_IOS)
227 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700228 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800229 RTCStartInternalCapture(filePath);
230 }
231#endif
232
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000233 // Request TURN.
234 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000235 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
236 NSError *error) {
237 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700238 RTCLogError("Error retrieving TURN servers: %@",
239 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000240 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000241 ARDAppClient *strongSelf = weakSelf;
242 [strongSelf.iceServers addObjectsFromArray:turnServers];
243 strongSelf.isTurnComplete = YES;
244 [strongSelf startSignalingIfReady];
245 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000246
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000247 // Join room on room server.
248 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700249 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000250 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000251 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000252 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000253 [strongSelf.delegate appClient:strongSelf didError:error];
254 return;
255 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000256 NSError *joinError =
257 [[strongSelf class] errorForJoinResultType:response.result];
258 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700259 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000260 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000261 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000262 return;
263 }
tkchinc3f46a92015-07-23 12:50:55 -0700264 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000265 strongSelf.roomId = response.roomId;
266 strongSelf.clientId = response.clientId;
267 strongSelf.isInitiator = response.isInitiator;
268 for (ARDSignalingMessage *message in response.messages) {
269 if (message.type == kARDSignalingMessageTypeOffer ||
270 message.type == kARDSignalingMessageTypeAnswer) {
271 strongSelf.hasReceivedSdp = YES;
272 [strongSelf.messageQueue insertObject:message atIndex:0];
273 } else {
274 [strongSelf.messageQueue addObject:message];
275 }
276 }
277 strongSelf.webSocketURL = response.webSocketURL;
278 strongSelf.webSocketRestURL = response.webSocketRestURL;
279 [strongSelf registerWithColliderIfReady];
280 [strongSelf startSignalingIfReady];
281 }];
282}
283
284- (void)disconnect {
285 if (_state == kARDAppClientStateDisconnected) {
286 return;
287 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000288 if (self.hasJoinedRoomServerRoom) {
289 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000290 clientId:_clientId
291 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000292 }
293 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000294 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000295 // Tell the other client we're hanging up.
296 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000297 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000298 }
299 // Disconnect from collider.
300 _channel = nil;
301 }
302 _clientId = nil;
303 _roomId = nil;
304 _isInitiator = NO;
305 _hasReceivedSdp = NO;
306 _messageQueue = [NSMutableArray array];
sakalc522e752017-04-05 12:17:48 -0700307 _localVideoTrack = nil;
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
magjedcc8b9062017-07-24 07:32:33 -0700312 [_peerConnection close];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000313 _peerConnection = nil;
314 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800315#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800316 if (kARDAppClientEnableTracing) {
317 RTCStopInternalCapture();
318 }
tkchind1fb26d2016-02-03 01:51:18 -0800319#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000320}
321
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000322#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000323
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000324- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000325 didReceiveMessage:(ARDSignalingMessage *)message {
326 switch (message.type) {
327 case kARDSignalingMessageTypeOffer:
328 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000329 // Offers and answers must be processed before any other message, so we
330 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000331 _hasReceivedSdp = YES;
332 [_messageQueue insertObject:message atIndex:0];
333 break;
334 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700335 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000336 [_messageQueue addObject:message];
337 break;
338 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000339 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000340 [self processSignalingMessage:message];
341 return;
342 }
343 [self drainMessageQueueIfReady];
344}
345
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000346- (void)channel:(id<ARDSignalingChannel>)channel
347 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000348 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000349 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000350 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000351 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000352 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000353 case kARDSignalingChannelStateClosed:
354 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000355 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
356 // completely if the websocket connection fails.
357 [self disconnect];
358 break;
359 }
360}
361
362#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000363// Callbacks for this delegate occur on non-main thread and need to be
364// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000365
366- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700367 didChangeSignalingState:(RTCSignalingState)stateChanged {
368 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000369}
370
371- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700372 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000373 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700374 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000375 (unsigned long)stream.videoTracks.count,
376 (unsigned long)stream.audioTracks.count);
377 if (stream.videoTracks.count) {
378 RTCVideoTrack *videoTrack = stream.videoTracks[0];
379 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
380 }
381 });
382}
383
384- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700385 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700386 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000387}
388
hjon79858f82016-03-13 22:08:26 -0700389- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700390 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000391}
392
393- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700394 didChangeIceConnectionState:(RTCIceConnectionState)newState {
395 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000396 dispatch_async(dispatch_get_main_queue(), ^{
397 [_delegate appClient:self didChangeConnectionState:newState];
398 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000399}
400
401- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700402 didChangeIceGatheringState:(RTCIceGatheringState)newState {
403 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000404}
405
406- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700407 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000408 dispatch_async(dispatch_get_main_queue(), ^{
409 ARDICECandidateMessage *message =
410 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
411 [self sendSignalingMessage:message];
412 });
413}
414
Zeke Chind3325802015-08-14 11:00:02 -0700415- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700416 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
417 dispatch_async(dispatch_get_main_queue(), ^{
418 ARDICECandidateRemovalMessage *message =
419 [[ARDICECandidateRemovalMessage alloc]
420 initWithRemovedCandidates:candidates];
421 [self sendSignalingMessage:message];
422 });
423}
424
425- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700426 didOpenDataChannel:(RTCDataChannel *)dataChannel {
427}
428
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000429#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000430// Callbacks for this delegate occur on non-main thread and need to be
431// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000432
433- (void)peerConnection:(RTCPeerConnection *)peerConnection
434 didCreateSessionDescription:(RTCSessionDescription *)sdp
435 error:(NSError *)error {
436 dispatch_async(dispatch_get_main_queue(), ^{
437 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700438 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000439 [self disconnect];
440 NSDictionary *userInfo = @{
441 NSLocalizedDescriptionKey: @"Failed to create session description.",
442 };
443 NSError *sdpError =
444 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
445 code:kARDAppClientErrorCreateSDP
446 userInfo:userInfo];
447 [_delegate appClient:self didError:sdpError];
448 return;
449 }
sakal68b5df92017-03-17 09:01:59 -0700450 // Prefer codec from settings if available.
451 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700452 [ARDSDPUtils descriptionForDescription:sdp
sakalc4adacf2017-03-28 01:22:48 -0700453 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700454 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700455 [_peerConnection setLocalDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700456 completionHandler:^(NSError *error) {
457 ARDAppClient *strongSelf = weakSelf;
458 [strongSelf peerConnection:strongSelf.peerConnection
459 didSetSessionDescriptionWithError:error];
460 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000461 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700462 [[ARDSessionDescriptionMessage alloc]
sakal68b5df92017-03-17 09:01:59 -0700463 initWithDescription:sdpPreferringCodec];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000464 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800465 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000466 });
467}
468
469- (void)peerConnection:(RTCPeerConnection *)peerConnection
470 didSetSessionDescriptionWithError:(NSError *)error {
471 dispatch_async(dispatch_get_main_queue(), ^{
472 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700473 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000474 [self disconnect];
475 NSDictionary *userInfo = @{
476 NSLocalizedDescriptionKey: @"Failed to set session description.",
477 };
478 NSError *sdpError =
479 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
480 code:kARDAppClientErrorSetSDP
481 userInfo:userInfo];
482 [_delegate appClient:self didError:sdpError];
483 return;
484 }
485 // If we're answering and we've just set the remote offer we need to create
486 // an answer and set the local description.
487 if (!_isInitiator && !_peerConnection.localDescription) {
488 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700489 __weak ARDAppClient *weakSelf = self;
490 [_peerConnection answerForConstraints:constraints
491 completionHandler:^(RTCSessionDescription *sdp,
492 NSError *error) {
493 ARDAppClient *strongSelf = weakSelf;
494 [strongSelf peerConnection:strongSelf.peerConnection
495 didCreateSessionDescription:sdp
496 error:error];
497 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000498 }
499 });
500}
501
502#pragma mark - Private
503
tkchin204177f2016-06-14 15:03:11 -0700504#if defined(WEBRTC_IOS)
505
506- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
507 NSParameterAssert(fileName.length);
508 NSArray *paths = NSSearchPathForDirectoriesInDomains(
509 NSDocumentDirectory, NSUserDomainMask, YES);
510 NSString *documentsDirPath = paths.firstObject;
511 NSString *filePath =
512 [documentsDirPath stringByAppendingPathComponent:fileName];
513 return filePath;
514}
515
516#endif
517
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000518- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000519 return _clientId.length;
520}
521
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000522// Begins the peer connection connection process if we have both joined a room
523// on the room server and tried to obtain a TURN server. Otherwise does nothing.
524// A peer connection object will be created with a stream that contains local
525// audio and video capture. If this client is the caller, an offer is created as
526// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000527- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000528 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000529 return;
530 }
531 self.state = kARDAppClientStateConnected;
532
533 // Create peer connection.
534 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700535 RTCConfiguration *config = [[RTCConfiguration alloc] init];
536 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700537 _peerConnection = [_factory peerConnectionWithConfiguration:config
538 constraints:constraints
539 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700540 // Create AV senders.
541 [self createAudioSender];
542 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000543 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000544 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700545 __weak ARDAppClient *weakSelf = self;
546 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
547 completionHandler:^(RTCSessionDescription *sdp,
548 NSError *error) {
549 ARDAppClient *strongSelf = weakSelf;
550 [strongSelf peerConnection:strongSelf.peerConnection
551 didCreateSessionDescription:sdp
552 error:error];
553 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000554 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000555 // Check if we've received an offer.
556 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000557 }
ivoc14d5dbe2016-07-04 07:06:55 -0700558#if defined(WEBRTC_IOS)
559 // Start event log.
560 if (kARDAppClientEnableRtcEventLog) {
561 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
562 if (![_peerConnection startRtcEventLogWithFilePath:filePath
563 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
564 RTCLogError(@"Failed to start event logging.");
565 }
566 }
peah5085b0c2016-08-25 22:15:14 -0700567
568 // Start aecdump diagnostic recording.
Anders Carlssone1500582017-06-15 16:05:13 +0200569 if ([_settings currentCreateAecDumpSettingFromStore]) {
tkchinfce0e2c2016-08-30 12:58:11 -0700570 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
571 if (![_factory startAecDumpWithFilePath:filePath
572 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
573 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700574 }
575 }
ivoc14d5dbe2016-07-04 07:06:55 -0700576#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000577}
578
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000579// Processes the messages that we've received from the room server and the
580// signaling channel. The offer or answer message must be processed before other
581// signaling messages, however they can arrive out of order. Hence, this method
582// only processes pending messages if there is a peer connection object and
583// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000584- (void)drainMessageQueueIfReady {
585 if (!_peerConnection || !_hasReceivedSdp) {
586 return;
587 }
588 for (ARDSignalingMessage *message in _messageQueue) {
589 [self processSignalingMessage:message];
590 }
591 [_messageQueue removeAllObjects];
592}
593
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000594// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000595- (void)processSignalingMessage:(ARDSignalingMessage *)message {
596 NSParameterAssert(_peerConnection ||
597 message.type == kARDSignalingMessageTypeBye);
598 switch (message.type) {
599 case kARDSignalingMessageTypeOffer:
600 case kARDSignalingMessageTypeAnswer: {
601 ARDSessionDescriptionMessage *sdpMessage =
602 (ARDSessionDescriptionMessage *)message;
603 RTCSessionDescription *description = sdpMessage.sessionDescription;
sakal68b5df92017-03-17 09:01:59 -0700604 // Prefer codec from settings if available.
605 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700606 [ARDSDPUtils descriptionForDescription:description
sakalc4adacf2017-03-28 01:22:48 -0700607 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700608 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700609 [_peerConnection setRemoteDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700610 completionHandler:^(NSError *error) {
611 ARDAppClient *strongSelf = weakSelf;
612 [strongSelf peerConnection:strongSelf.peerConnection
613 didSetSessionDescriptionWithError:error];
614 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000615 break;
616 }
617 case kARDSignalingMessageTypeCandidate: {
618 ARDICECandidateMessage *candidateMessage =
619 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700620 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000621 break;
622 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700623 case kARDSignalingMessageTypeCandidateRemoval: {
624 ARDICECandidateRemovalMessage *candidateMessage =
625 (ARDICECandidateRemovalMessage *)message;
626 [_peerConnection removeIceCandidates:candidateMessage.candidates];
627 break;
628 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000629 case kARDSignalingMessageTypeBye:
630 // Other client disconnected.
631 // TODO(tkchin): support waiting in room for next client. For now just
632 // disconnect.
633 [self disconnect];
634 break;
635 }
636}
637
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000638// Sends a signaling message to the other client. The caller will send messages
639// through the room server, whereas the callee will send messages over the
640// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000641- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
642 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000643 __weak ARDAppClient *weakSelf = self;
644 [_roomServerClient sendMessage:message
645 forRoomId:_roomId
646 clientId:_clientId
647 completionHandler:^(ARDMessageResponse *response,
648 NSError *error) {
649 ARDAppClient *strongSelf = weakSelf;
650 if (error) {
651 [strongSelf.delegate appClient:strongSelf didError:error];
652 return;
653 }
654 NSError *messageError =
655 [[strongSelf class] errorForMessageResultType:response.result];
656 if (messageError) {
657 [strongSelf.delegate appClient:strongSelf didError:messageError];
658 return;
659 }
660 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000661 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000662 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000663 }
664}
665
skvladf3569c82016-04-29 15:30:16 -0700666- (RTCRtpSender *)createVideoSender {
667 RTCRtpSender *sender =
668 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
669 streamId:kARDMediaStreamId];
sakalc522e752017-04-05 12:17:48 -0700670 _localVideoTrack = [self createLocalVideoTrack];
671 if (_localVideoTrack) {
672 sender.track = _localVideoTrack;
673 [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700674 }
denicija9af2b602016-11-17 00:43:43 -0800675
skvladf3569c82016-04-29 15:30:16 -0700676 return sender;
677}
678
denicija9af2b602016-11-17 00:43:43 -0800679- (void)setMaxBitrateForPeerConnectionVideoSender {
680 for (RTCRtpSender *sender in _peerConnection.senders) {
681 if (sender.track != nil) {
682 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700683 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800684 }
685 }
denicija8c375de2016-11-08 06:28:17 -0800686 }
687}
688
denicija9af2b602016-11-17 00:43:43 -0800689- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
690 if (maxBitrate.intValue <= 0) {
691 return;
692 }
693
694 RTCRtpParameters *parametersToModify = sender.parameters;
695 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
696 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
697 }
698 [sender setParameters:parametersToModify];
699}
700
skvladf3569c82016-04-29 15:30:16 -0700701- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700702 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
703 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
704 RTCAudioTrack *track = [_factory audioTrackWithSource:source
705 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700706 RTCRtpSender *sender =
707 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
708 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700709 sender.track = track;
710 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700711}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000712
Zeke Chin57cc74e2015-05-05 07:52:31 -0700713- (RTCVideoTrack *)createLocalVideoTrack {
714 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000715 // The iOS simulator doesn't provide any sort of camera capture
716 // support or emulation (http://goo.gl/rHAnC1) so don't bother
717 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700718#if !TARGET_IPHONE_SIMULATOR
Anders Carlssone1500582017-06-15 16:05:13 +0200719 if (![_settings currentAudioOnlySettingFromStore]) {
sakalc522e752017-04-05 12:17:48 -0700720 RTCVideoSource *source = [_factory videoSource];
721 RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
722 [_delegate appClient:self didCreateLocalCapturer:capturer];
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 {
Anders Carlssone1500582017-06-15 16:05:13 +0200758 NSString *valueLevelControl = [_settings currentUseLevelControllerSettingFromStore] ?
759 kRTCMediaConstraintsValueTrue :
760 kRTCMediaConstraintsValueFalse;
tkchinab1293a2016-08-30 12:35:05 -0700761 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700762 RTCMediaConstraints *constraints =
763 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
764 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700765 return constraints;
766}
767
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000768- (RTCMediaConstraints *)defaultAnswerConstraints {
769 return [self defaultOfferConstraints];
770}
771
772- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700773 NSDictionary *mandatoryConstraints = @{
774 @"OfferToReceiveAudio" : @"true",
775 @"OfferToReceiveVideo" : @"true"
776 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000777 RTCMediaConstraints* constraints =
778 [[RTCMediaConstraints alloc]
779 initWithMandatoryConstraints:mandatoryConstraints
780 optionalConstraints:nil];
781 return constraints;
782}
783
784- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000785 if (_defaultPeerConnectionConstraints) {
786 return _defaultPeerConnectionConstraints;
787 }
haysc913e6452015-10-02 11:44:03 -0700788 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700789 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000790 RTCMediaConstraints* constraints =
791 [[RTCMediaConstraints alloc]
792 initWithMandatoryConstraints:nil
793 optionalConstraints:optionalConstraints];
794 return constraints;
795}
796
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000797#pragma mark - Errors
798
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000799+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000800 NSError *error = nil;
801 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000802 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000803 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000804 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000805 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
806 code:kARDAppClientErrorUnknown
807 userInfo:@{
808 NSLocalizedDescriptionKey: @"Unknown error.",
809 }];
810 break;
811 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000812 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000813 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
814 code:kARDAppClientErrorRoomFull
815 userInfo:@{
816 NSLocalizedDescriptionKey: @"Room is full.",
817 }];
818 break;
819 }
820 }
821 return error;
822}
823
824+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
825 NSError *error = nil;
826 switch (resultType) {
827 case kARDMessageResultTypeSuccess:
828 break;
829 case kARDMessageResultTypeUnknown:
830 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
831 code:kARDAppClientErrorUnknown
832 userInfo:@{
833 NSLocalizedDescriptionKey: @"Unknown error.",
834 }];
835 break;
836 case kARDMessageResultTypeInvalidClient:
837 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
838 code:kARDAppClientErrorInvalidClient
839 userInfo:@{
840 NSLocalizedDescriptionKey: @"Invalid client.",
841 }];
842 break;
843 case kARDMessageResultTypeInvalidRoom:
844 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
845 code:kARDAppClientErrorInvalidRoom
846 userInfo:@{
847 NSLocalizedDescriptionKey: @"Invalid room.",
848 }];
849 break;
850 }
851 return error;
852}
853
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000854@end