blob: 7e06612e6a56017306dacc0a0c1e2b839c4545d0 [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;
129@synthesize isAudioOnly = _isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700130@synthesize shouldMakeAecDump = _shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700131@synthesize shouldUseLevelControl = _shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000132
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000133- (instancetype)init {
sakalc4adacf2017-03-28 01:22:48 -0700134 return [self initWithDelegate:nil];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000135}
136
sakalc4adacf2017-03-28 01:22:48 -0700137- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000138 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000139 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000140 _delegate = delegate;
kthelgasoncc882af2017-01-13 05:59:46 -0800141 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
142 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000143 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000144 }
145 return self;
146}
147
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000148// TODO(tkchin): Provide signaling channel factory interface so we can recreate
149// channel if we need to on network failure. Also, make this the default public
150// constructor.
151- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
152 signalingChannel:(id<ARDSignalingChannel>)channel
153 turnClient:(id<ARDTURNClient>)turnClient
154 delegate:(id<ARDAppClientDelegate>)delegate {
155 NSParameterAssert(rsClient);
156 NSParameterAssert(channel);
157 NSParameterAssert(turnClient);
158 if (self = [super init]) {
159 _roomServerClient = rsClient;
160 _channel = channel;
161 _turnClient = turnClient;
162 _delegate = delegate;
163 [self configure];
164 }
165 return self;
166}
167
168- (void)configure {
169 _factory = [[RTCPeerConnectionFactory alloc] init];
170 _messageQueue = [NSMutableArray array];
kthelgasoncc882af2017-01-13 05:59:46 -0800171 _iceServers = [NSMutableArray array];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700172 _fileLogger = [[RTCFileLogger alloc] init];
173 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000174}
175
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000176- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700177 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000178 [self disconnect];
179}
180
Zeke Chind3325802015-08-14 11:00:02 -0700181- (void)setShouldGetStats:(BOOL)shouldGetStats {
182 if (_shouldGetStats == shouldGetStats) {
183 return;
184 }
185 if (shouldGetStats) {
186 __weak ARDAppClient *weakSelf = self;
187 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
188 repeats:YES
189 timerHandler:^{
190 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700191 [strongSelf.peerConnection statsForTrack:nil
192 statsOutputLevel:RTCStatsOutputLevelDebug
193 completionHandler:^(NSArray *stats) {
194 dispatch_async(dispatch_get_main_queue(), ^{
195 ARDAppClient *strongSelf = weakSelf;
196 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
197 });
198 }];
Zeke Chind3325802015-08-14 11:00:02 -0700199 }];
200 } else {
201 [_statsTimer invalidate];
202 _statsTimer = nil;
203 }
204 _shouldGetStats = shouldGetStats;
205}
206
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000207- (void)setState:(ARDAppClientState)state {
208 if (_state == state) {
209 return;
210 }
211 _state = state;
212 [_delegate appClient:self didChangeState:_state];
213}
214
215- (void)connectToRoomWithId:(NSString *)roomId
sakalc4adacf2017-03-28 01:22:48 -0700216 settings:(ARDSettingsModel *)settings
haysc913e6452015-10-02 11:44:03 -0700217 isLoopback:(BOOL)isLoopback
peah5085b0c2016-08-25 22:15:14 -0700218 isAudioOnly:(BOOL)isAudioOnly
tkchinab1293a2016-08-30 12:35:05 -0700219 shouldMakeAecDump:(BOOL)shouldMakeAecDump
220 shouldUseLevelControl:(BOOL)shouldUseLevelControl {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000221 NSParameterAssert(roomId.length);
222 NSParameterAssert(_state == kARDAppClientStateDisconnected);
sakalc4adacf2017-03-28 01:22:48 -0700223 _settings = settings;
haysc913e6452015-10-02 11:44:03 -0700224 _isLoopback = isLoopback;
225 _isAudioOnly = isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700226 _shouldMakeAecDump = shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700227 _shouldUseLevelControl = shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000228 self.state = kARDAppClientStateConnecting;
229
tkchind1fb26d2016-02-03 01:51:18 -0800230#if defined(WEBRTC_IOS)
231 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700232 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800233 RTCStartInternalCapture(filePath);
234 }
235#endif
236
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000237 // Request TURN.
238 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000239 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
240 NSError *error) {
241 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700242 RTCLogError("Error retrieving TURN servers: %@",
243 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000244 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000245 ARDAppClient *strongSelf = weakSelf;
246 [strongSelf.iceServers addObjectsFromArray:turnServers];
247 strongSelf.isTurnComplete = YES;
248 [strongSelf startSignalingIfReady];
249 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000250
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000251 // Join room on room server.
252 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700253 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000254 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000255 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000256 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000257 [strongSelf.delegate appClient:strongSelf didError:error];
258 return;
259 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000260 NSError *joinError =
261 [[strongSelf class] errorForJoinResultType:response.result];
262 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700263 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000264 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000265 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000266 return;
267 }
tkchinc3f46a92015-07-23 12:50:55 -0700268 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000269 strongSelf.roomId = response.roomId;
270 strongSelf.clientId = response.clientId;
271 strongSelf.isInitiator = response.isInitiator;
272 for (ARDSignalingMessage *message in response.messages) {
273 if (message.type == kARDSignalingMessageTypeOffer ||
274 message.type == kARDSignalingMessageTypeAnswer) {
275 strongSelf.hasReceivedSdp = YES;
276 [strongSelf.messageQueue insertObject:message atIndex:0];
277 } else {
278 [strongSelf.messageQueue addObject:message];
279 }
280 }
281 strongSelf.webSocketURL = response.webSocketURL;
282 strongSelf.webSocketRestURL = response.webSocketRestURL;
283 [strongSelf registerWithColliderIfReady];
284 [strongSelf startSignalingIfReady];
285 }];
286}
287
288- (void)disconnect {
289 if (_state == kARDAppClientStateDisconnected) {
290 return;
291 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000292 if (self.hasJoinedRoomServerRoom) {
293 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000294 clientId:_clientId
295 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000296 }
297 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000298 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000299 // Tell the other client we're hanging up.
300 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000301 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000302 }
303 // Disconnect from collider.
304 _channel = nil;
305 }
306 _clientId = nil;
307 _roomId = nil;
308 _isInitiator = NO;
309 _hasReceivedSdp = NO;
310 _messageQueue = [NSMutableArray array];
sakalc522e752017-04-05 12:17:48 -0700311 _localVideoTrack = nil;
ivoc14d5dbe2016-07-04 07:06:55 -0700312#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700313 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700314 [_peerConnection stopRtcEventLog];
315#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000316 _peerConnection = nil;
317 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800318#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800319 if (kARDAppClientEnableTracing) {
320 RTCStopInternalCapture();
321 }
tkchind1fb26d2016-02-03 01:51:18 -0800322#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000323}
324
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000325#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000326
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000327- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000328 didReceiveMessage:(ARDSignalingMessage *)message {
329 switch (message.type) {
330 case kARDSignalingMessageTypeOffer:
331 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000332 // Offers and answers must be processed before any other message, so we
333 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000334 _hasReceivedSdp = YES;
335 [_messageQueue insertObject:message atIndex:0];
336 break;
337 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700338 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000339 [_messageQueue addObject:message];
340 break;
341 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000342 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000343 [self processSignalingMessage:message];
344 return;
345 }
346 [self drainMessageQueueIfReady];
347}
348
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000349- (void)channel:(id<ARDSignalingChannel>)channel
350 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000351 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000352 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000353 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000354 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000355 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000356 case kARDSignalingChannelStateClosed:
357 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000358 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
359 // completely if the websocket connection fails.
360 [self disconnect];
361 break;
362 }
363}
364
365#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000366// Callbacks for this delegate occur on non-main thread and need to be
367// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000368
369- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700370 didChangeSignalingState:(RTCSignalingState)stateChanged {
371 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000372}
373
374- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700375 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000376 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700377 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000378 (unsigned long)stream.videoTracks.count,
379 (unsigned long)stream.audioTracks.count);
380 if (stream.videoTracks.count) {
381 RTCVideoTrack *videoTrack = stream.videoTracks[0];
382 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
383 }
384 });
385}
386
387- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700388 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700389 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000390}
391
hjon79858f82016-03-13 22:08:26 -0700392- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700393 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000394}
395
396- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700397 didChangeIceConnectionState:(RTCIceConnectionState)newState {
398 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000399 dispatch_async(dispatch_get_main_queue(), ^{
400 [_delegate appClient:self didChangeConnectionState:newState];
401 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000402}
403
404- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700405 didChangeIceGatheringState:(RTCIceGatheringState)newState {
406 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000407}
408
409- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700410 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000411 dispatch_async(dispatch_get_main_queue(), ^{
412 ARDICECandidateMessage *message =
413 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
414 [self sendSignalingMessage:message];
415 });
416}
417
Zeke Chind3325802015-08-14 11:00:02 -0700418- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700419 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
420 dispatch_async(dispatch_get_main_queue(), ^{
421 ARDICECandidateRemovalMessage *message =
422 [[ARDICECandidateRemovalMessage alloc]
423 initWithRemovedCandidates:candidates];
424 [self sendSignalingMessage:message];
425 });
426}
427
428- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700429 didOpenDataChannel:(RTCDataChannel *)dataChannel {
430}
431
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000432#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000433// Callbacks for this delegate occur on non-main thread and need to be
434// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000435
436- (void)peerConnection:(RTCPeerConnection *)peerConnection
437 didCreateSessionDescription:(RTCSessionDescription *)sdp
438 error:(NSError *)error {
439 dispatch_async(dispatch_get_main_queue(), ^{
440 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700441 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000442 [self disconnect];
443 NSDictionary *userInfo = @{
444 NSLocalizedDescriptionKey: @"Failed to create session description.",
445 };
446 NSError *sdpError =
447 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
448 code:kARDAppClientErrorCreateSDP
449 userInfo:userInfo];
450 [_delegate appClient:self didError:sdpError];
451 return;
452 }
sakal68b5df92017-03-17 09:01:59 -0700453 // Prefer codec from settings if available.
454 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700455 [ARDSDPUtils descriptionForDescription:sdp
sakalc4adacf2017-03-28 01:22:48 -0700456 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700457 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700458 [_peerConnection setLocalDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700459 completionHandler:^(NSError *error) {
460 ARDAppClient *strongSelf = weakSelf;
461 [strongSelf peerConnection:strongSelf.peerConnection
462 didSetSessionDescriptionWithError:error];
463 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000464 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700465 [[ARDSessionDescriptionMessage alloc]
sakal68b5df92017-03-17 09:01:59 -0700466 initWithDescription:sdpPreferringCodec];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000467 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800468 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000469 });
470}
471
472- (void)peerConnection:(RTCPeerConnection *)peerConnection
473 didSetSessionDescriptionWithError:(NSError *)error {
474 dispatch_async(dispatch_get_main_queue(), ^{
475 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700476 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000477 [self disconnect];
478 NSDictionary *userInfo = @{
479 NSLocalizedDescriptionKey: @"Failed to set session description.",
480 };
481 NSError *sdpError =
482 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
483 code:kARDAppClientErrorSetSDP
484 userInfo:userInfo];
485 [_delegate appClient:self didError:sdpError];
486 return;
487 }
488 // If we're answering and we've just set the remote offer we need to create
489 // an answer and set the local description.
490 if (!_isInitiator && !_peerConnection.localDescription) {
491 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700492 __weak ARDAppClient *weakSelf = self;
493 [_peerConnection answerForConstraints:constraints
494 completionHandler:^(RTCSessionDescription *sdp,
495 NSError *error) {
496 ARDAppClient *strongSelf = weakSelf;
497 [strongSelf peerConnection:strongSelf.peerConnection
498 didCreateSessionDescription:sdp
499 error:error];
500 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000501 }
502 });
503}
504
505#pragma mark - Private
506
tkchin204177f2016-06-14 15:03:11 -0700507#if defined(WEBRTC_IOS)
508
509- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
510 NSParameterAssert(fileName.length);
511 NSArray *paths = NSSearchPathForDirectoriesInDomains(
512 NSDocumentDirectory, NSUserDomainMask, YES);
513 NSString *documentsDirPath = paths.firstObject;
514 NSString *filePath =
515 [documentsDirPath stringByAppendingPathComponent:fileName];
516 return filePath;
517}
518
519#endif
520
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000521- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000522 return _clientId.length;
523}
524
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000525// Begins the peer connection connection process if we have both joined a room
526// on the room server and tried to obtain a TURN server. Otherwise does nothing.
527// A peer connection object will be created with a stream that contains local
528// audio and video capture. If this client is the caller, an offer is created as
529// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000530- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000531 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000532 return;
533 }
534 self.state = kARDAppClientStateConnected;
535
536 // Create peer connection.
537 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700538 RTCConfiguration *config = [[RTCConfiguration alloc] init];
539 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700540 _peerConnection = [_factory peerConnectionWithConfiguration:config
541 constraints:constraints
542 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700543 // Create AV senders.
544 [self createAudioSender];
545 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000546 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000547 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700548 __weak ARDAppClient *weakSelf = self;
549 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
550 completionHandler:^(RTCSessionDescription *sdp,
551 NSError *error) {
552 ARDAppClient *strongSelf = weakSelf;
553 [strongSelf peerConnection:strongSelf.peerConnection
554 didCreateSessionDescription:sdp
555 error:error];
556 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000557 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000558 // Check if we've received an offer.
559 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000560 }
ivoc14d5dbe2016-07-04 07:06:55 -0700561#if defined(WEBRTC_IOS)
562 // Start event log.
563 if (kARDAppClientEnableRtcEventLog) {
564 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
565 if (![_peerConnection startRtcEventLogWithFilePath:filePath
566 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
567 RTCLogError(@"Failed to start event logging.");
568 }
569 }
peah5085b0c2016-08-25 22:15:14 -0700570
571 // Start aecdump diagnostic recording.
572 if (_shouldMakeAecDump) {
tkchinfce0e2c2016-08-30 12:58:11 -0700573 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
574 if (![_factory startAecDumpWithFilePath:filePath
575 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
576 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700577 }
578 }
ivoc14d5dbe2016-07-04 07:06:55 -0700579#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000580}
581
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000582// Processes the messages that we've received from the room server and the
583// signaling channel. The offer or answer message must be processed before other
584// signaling messages, however they can arrive out of order. Hence, this method
585// only processes pending messages if there is a peer connection object and
586// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000587- (void)drainMessageQueueIfReady {
588 if (!_peerConnection || !_hasReceivedSdp) {
589 return;
590 }
591 for (ARDSignalingMessage *message in _messageQueue) {
592 [self processSignalingMessage:message];
593 }
594 [_messageQueue removeAllObjects];
595}
596
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000597// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000598- (void)processSignalingMessage:(ARDSignalingMessage *)message {
599 NSParameterAssert(_peerConnection ||
600 message.type == kARDSignalingMessageTypeBye);
601 switch (message.type) {
602 case kARDSignalingMessageTypeOffer:
603 case kARDSignalingMessageTypeAnswer: {
604 ARDSessionDescriptionMessage *sdpMessage =
605 (ARDSessionDescriptionMessage *)message;
606 RTCSessionDescription *description = sdpMessage.sessionDescription;
sakal68b5df92017-03-17 09:01:59 -0700607 // Prefer codec from settings if available.
608 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700609 [ARDSDPUtils descriptionForDescription:description
sakalc4adacf2017-03-28 01:22:48 -0700610 preferredVideoCodec:[_settings currentVideoCodecSettingFromStore]];
hjon79858f82016-03-13 22:08:26 -0700611 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700612 [_peerConnection setRemoteDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700613 completionHandler:^(NSError *error) {
614 ARDAppClient *strongSelf = weakSelf;
615 [strongSelf peerConnection:strongSelf.peerConnection
616 didSetSessionDescriptionWithError:error];
617 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000618 break;
619 }
620 case kARDSignalingMessageTypeCandidate: {
621 ARDICECandidateMessage *candidateMessage =
622 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700623 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000624 break;
625 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700626 case kARDSignalingMessageTypeCandidateRemoval: {
627 ARDICECandidateRemovalMessage *candidateMessage =
628 (ARDICECandidateRemovalMessage *)message;
629 [_peerConnection removeIceCandidates:candidateMessage.candidates];
630 break;
631 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000632 case kARDSignalingMessageTypeBye:
633 // Other client disconnected.
634 // TODO(tkchin): support waiting in room for next client. For now just
635 // disconnect.
636 [self disconnect];
637 break;
638 }
639}
640
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000641// Sends a signaling message to the other client. The caller will send messages
642// through the room server, whereas the callee will send messages over the
643// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000644- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
645 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000646 __weak ARDAppClient *weakSelf = self;
647 [_roomServerClient sendMessage:message
648 forRoomId:_roomId
649 clientId:_clientId
650 completionHandler:^(ARDMessageResponse *response,
651 NSError *error) {
652 ARDAppClient *strongSelf = weakSelf;
653 if (error) {
654 [strongSelf.delegate appClient:strongSelf didError:error];
655 return;
656 }
657 NSError *messageError =
658 [[strongSelf class] errorForMessageResultType:response.result];
659 if (messageError) {
660 [strongSelf.delegate appClient:strongSelf didError:messageError];
661 return;
662 }
663 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000664 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000665 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000666 }
667}
668
skvladf3569c82016-04-29 15:30:16 -0700669- (RTCRtpSender *)createVideoSender {
670 RTCRtpSender *sender =
671 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
672 streamId:kARDMediaStreamId];
sakalc522e752017-04-05 12:17:48 -0700673 _localVideoTrack = [self createLocalVideoTrack];
674 if (_localVideoTrack) {
675 sender.track = _localVideoTrack;
676 [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700677 }
denicija9af2b602016-11-17 00:43:43 -0800678
skvladf3569c82016-04-29 15:30:16 -0700679 return sender;
680}
681
denicija9af2b602016-11-17 00:43:43 -0800682- (void)setMaxBitrateForPeerConnectionVideoSender {
683 for (RTCRtpSender *sender in _peerConnection.senders) {
684 if (sender.track != nil) {
685 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700686 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800687 }
688 }
denicija8c375de2016-11-08 06:28:17 -0800689 }
690}
691
denicija9af2b602016-11-17 00:43:43 -0800692- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
693 if (maxBitrate.intValue <= 0) {
694 return;
695 }
696
697 RTCRtpParameters *parametersToModify = sender.parameters;
698 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
699 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
700 }
701 [sender setParameters:parametersToModify];
702}
703
skvladf3569c82016-04-29 15:30:16 -0700704- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700705 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
706 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
707 RTCAudioTrack *track = [_factory audioTrackWithSource:source
708 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700709 RTCRtpSender *sender =
710 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
711 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700712 sender.track = track;
713 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700714}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000715
Zeke Chin57cc74e2015-05-05 07:52:31 -0700716- (RTCVideoTrack *)createLocalVideoTrack {
717 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000718 // The iOS simulator doesn't provide any sort of camera capture
719 // support or emulation (http://goo.gl/rHAnC1) so don't bother
720 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700721#if !TARGET_IPHONE_SIMULATOR
haysc913e6452015-10-02 11:44:03 -0700722 if (!_isAudioOnly) {
sakalc522e752017-04-05 12:17:48 -0700723 RTCVideoSource *source = [_factory videoSource];
724 RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
725 [_delegate appClient:self didCreateLocalCapturer:capturer];
haysc913e6452015-10-02 11:44:03 -0700726 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700727 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700728 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700729 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000730#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700731 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000732}
733
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000734#pragma mark - Collider methods
735
736- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000737 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000738 return;
739 }
740 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000741 if (!_channel) {
742 _channel =
743 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
744 restURL:_websocketRestURL
745 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700746 if (_isLoopback) {
747 _loopbackChannel =
748 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
749 restURL:_websocketRestURL];
750 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000751 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000752 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700753 if (_isLoopback) {
754 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
755 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000756}
757
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000758#pragma mark - Defaults
759
tkchinab1293a2016-08-30 12:35:05 -0700760 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
761 NSString *valueLevelControl = _shouldUseLevelControl ?
762 kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
763 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700764 RTCMediaConstraints *constraints =
765 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
766 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700767 return constraints;
768}
769
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000770- (RTCMediaConstraints *)defaultAnswerConstraints {
771 return [self defaultOfferConstraints];
772}
773
774- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700775 NSDictionary *mandatoryConstraints = @{
776 @"OfferToReceiveAudio" : @"true",
777 @"OfferToReceiveVideo" : @"true"
778 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000779 RTCMediaConstraints* constraints =
780 [[RTCMediaConstraints alloc]
781 initWithMandatoryConstraints:mandatoryConstraints
782 optionalConstraints:nil];
783 return constraints;
784}
785
786- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000787 if (_defaultPeerConnectionConstraints) {
788 return _defaultPeerConnectionConstraints;
789 }
haysc913e6452015-10-02 11:44:03 -0700790 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700791 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000792 RTCMediaConstraints* constraints =
793 [[RTCMediaConstraints alloc]
794 initWithMandatoryConstraints:nil
795 optionalConstraints:optionalConstraints];
796 return constraints;
797}
798
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000799#pragma mark - Errors
800
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000801+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000802 NSError *error = nil;
803 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000804 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000805 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000806 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000807 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
808 code:kARDAppClientErrorUnknown
809 userInfo:@{
810 NSLocalizedDescriptionKey: @"Unknown error.",
811 }];
812 break;
813 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000814 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000815 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
816 code:kARDAppClientErrorRoomFull
817 userInfo:@{
818 NSLocalizedDescriptionKey: @"Room is full.",
819 }];
820 break;
821 }
822 }
823 return error;
824}
825
826+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
827 NSError *error = nil;
828 switch (resultType) {
829 case kARDMessageResultTypeSuccess:
830 break;
831 case kARDMessageResultTypeUnknown:
832 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
833 code:kARDAppClientErrorUnknown
834 userInfo:@{
835 NSLocalizedDescriptionKey: @"Unknown error.",
836 }];
837 break;
838 case kARDMessageResultTypeInvalidClient:
839 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
840 code:kARDAppClientErrorInvalidClient
841 userInfo:@{
842 NSLocalizedDescriptionKey: @"Invalid client.",
843 }];
844 break;
845 case kARDMessageResultTypeInvalidRoom:
846 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
847 code:kARDAppClientErrorInvalidRoom
848 userInfo:@{
849 NSLocalizedDescriptionKey: @"Invalid room.",
850 }];
851 break;
852 }
853 return error;
854}
855
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000856@end