blob: f606de00f77e7791a100b53d3444efcd750f5abc [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"
Alex Naresta5fbc232017-10-18 18:31:07 +020028#import "ARDBitrateAllocationStrategy.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000029#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000030#import "ARDMessageResponse.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";
Alex Naresta5fbc232017-10-18 18:31:07 +020054static uint32_t const kSufficientAudioBitrate = 16000;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000055
tkchin204177f2016-06-14 15:03:11 -070056// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080057static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070058static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070059static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070060static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
denicija9af2b602016-11-17 00:43:43 -080061static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080062
Zeke Chind3325802015-08-14 11:00:02 -070063// We need a proxy to NSTimer because it causes a strong retain cycle. When
64// using the proxy, |invalidate| must be called before it properly deallocs.
65@interface ARDTimerProxy : NSObject
66
67- (instancetype)initWithInterval:(NSTimeInterval)interval
68 repeats:(BOOL)repeats
69 timerHandler:(void (^)(void))timerHandler;
70- (void)invalidate;
71
72@end
73
74@implementation ARDTimerProxy {
75 NSTimer *_timer;
76 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070077}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000078
Zeke Chind3325802015-08-14 11:00:02 -070079- (instancetype)initWithInterval:(NSTimeInterval)interval
80 repeats:(BOOL)repeats
81 timerHandler:(void (^)(void))timerHandler {
82 NSParameterAssert(timerHandler);
83 if (self = [super init]) {
84 _timerHandler = timerHandler;
85 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
86 target:self
87 selector:@selector(timerDidFire:)
88 userInfo:nil
89 repeats:repeats];
90 }
91 return self;
92}
93
94- (void)invalidate {
95 [_timer invalidate];
96}
97
98- (void)timerDidFire:(NSTimer *)timer {
99 _timerHandler();
100}
101
102@end
103
104@implementation ARDAppClient {
105 RTCFileLogger *_fileLogger;
106 ARDTimerProxy *_statsTimer;
sakalc4adacf2017-03-28 01:22:48 -0700107 ARDSettingsModel *_settings;
sakalc522e752017-04-05 12:17:48 -0700108 RTCVideoTrack *_localVideoTrack;
Alex Naresta5fbc232017-10-18 18:31:07 +0200109 ARDBitrateAllocationStrategy *_bitrateAllocationStrategy;
Zeke Chind3325802015-08-14 11:00:02 -0700110}
111
112@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000113@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700114@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000115@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000116@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700117@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000118@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000119@synthesize peerConnection = _peerConnection;
120@synthesize factory = _factory;
121@synthesize messageQueue = _messageQueue;
122@synthesize isTurnComplete = _isTurnComplete;
123@synthesize hasReceivedSdp = _hasReceivedSdp;
124@synthesize roomId = _roomId;
125@synthesize clientId = _clientId;
126@synthesize isInitiator = _isInitiator;
127@synthesize iceServers = _iceServers;
128@synthesize webSocketURL = _websocketURL;
129@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000130@synthesize defaultPeerConnectionConstraints =
131 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700132@synthesize isLoopback = _isLoopback;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000133
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000134- (instancetype)init {
sakalc4adacf2017-03-28 01:22:48 -0700135 return [self initWithDelegate:nil];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000136}
137
sakalc4adacf2017-03-28 01:22:48 -0700138- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000139 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000140 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000141 _delegate = delegate;
kthelgasoncc882af2017-01-13 05:59:46 -0800142 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
143 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000144 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000145 }
146 return self;
147}
148
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000149// TODO(tkchin): Provide signaling channel factory interface so we can recreate
150// channel if we need to on network failure. Also, make this the default public
151// constructor.
152- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
153 signalingChannel:(id<ARDSignalingChannel>)channel
154 turnClient:(id<ARDTURNClient>)turnClient
155 delegate:(id<ARDAppClientDelegate>)delegate {
156 NSParameterAssert(rsClient);
157 NSParameterAssert(channel);
158 NSParameterAssert(turnClient);
159 if (self = [super init]) {
160 _roomServerClient = rsClient;
161 _channel = channel;
162 _turnClient = turnClient;
163 _delegate = delegate;
164 [self configure];
165 }
166 return self;
167}
168
169- (void)configure {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000170 _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
Anders Carlssone1500582017-06-15 16:05:13 +0200217 isLoopback:(BOOL)isLoopback {
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;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000222 self.state = kARDAppClientStateConnecting;
223
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200224 ARDVideoDecoderFactory *decoderFactory = [[ARDVideoDecoderFactory alloc] init];
225 ARDVideoEncoderFactory *encoderFactory = [[ARDVideoEncoderFactory alloc] init];
226 encoderFactory.preferredCodec = [settings currentVideoCodecSettingFromStore];
227 _factory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory:encoderFactory
228 decoderFactory:decoderFactory];
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;
Alex Naresta5fbc232017-10-18 18:31:07 +0200312
ivoc14d5dbe2016-07-04 07:06:55 -0700313#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700314 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700315 [_peerConnection stopRtcEventLog];
316#endif
magjedcc8b9062017-07-24 07:32:33 -0700317 [_peerConnection close];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000318 _peerConnection = nil;
Alex Naresta5fbc232017-10-18 18:31:07 +0200319 _bitrateAllocationStrategy = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000320 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800321#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800322 if (kARDAppClientEnableTracing) {
323 RTCStopInternalCapture();
324 }
tkchind1fb26d2016-02-03 01:51:18 -0800325#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000326}
327
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000328#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000329
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000330- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000331 didReceiveMessage:(ARDSignalingMessage *)message {
332 switch (message.type) {
333 case kARDSignalingMessageTypeOffer:
334 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000335 // Offers and answers must be processed before any other message, so we
336 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000337 _hasReceivedSdp = YES;
338 [_messageQueue insertObject:message atIndex:0];
339 break;
340 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700341 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000342 [_messageQueue addObject:message];
343 break;
344 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000345 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000346 [self processSignalingMessage:message];
347 return;
348 }
349 [self drainMessageQueueIfReady];
350}
351
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000352- (void)channel:(id<ARDSignalingChannel>)channel
353 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000354 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000355 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000356 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000357 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000358 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000359 case kARDSignalingChannelStateClosed:
360 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000361 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
362 // completely if the websocket connection fails.
363 [self disconnect];
364 break;
365 }
366}
367
368#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000369// Callbacks for this delegate occur on non-main thread and need to be
370// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000371
372- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700373 didChangeSignalingState:(RTCSignalingState)stateChanged {
374 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000375}
376
377- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700378 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000379 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700380 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000381 (unsigned long)stream.videoTracks.count,
382 (unsigned long)stream.audioTracks.count);
383 if (stream.videoTracks.count) {
384 RTCVideoTrack *videoTrack = stream.videoTracks[0];
385 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
386 }
387 });
388}
389
390- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700391 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700392 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000393}
394
hjon79858f82016-03-13 22:08:26 -0700395- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700396 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000397}
398
399- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700400 didChangeIceConnectionState:(RTCIceConnectionState)newState {
401 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000402 dispatch_async(dispatch_get_main_queue(), ^{
403 [_delegate appClient:self didChangeConnectionState:newState];
404 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000405}
406
407- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700408 didChangeIceGatheringState:(RTCIceGatheringState)newState {
409 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000410}
411
412- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700413 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000414 dispatch_async(dispatch_get_main_queue(), ^{
415 ARDICECandidateMessage *message =
416 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
417 [self sendSignalingMessage:message];
418 });
419}
420
Zeke Chind3325802015-08-14 11:00:02 -0700421- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700422 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
423 dispatch_async(dispatch_get_main_queue(), ^{
424 ARDICECandidateRemovalMessage *message =
425 [[ARDICECandidateRemovalMessage alloc]
426 initWithRemovedCandidates:candidates];
427 [self sendSignalingMessage:message];
428 });
429}
430
431- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700432 didOpenDataChannel:(RTCDataChannel *)dataChannel {
433}
434
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000435#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000436// Callbacks for this delegate occur on non-main thread and need to be
437// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000438
439- (void)peerConnection:(RTCPeerConnection *)peerConnection
440 didCreateSessionDescription:(RTCSessionDescription *)sdp
441 error:(NSError *)error {
442 dispatch_async(dispatch_get_main_queue(), ^{
443 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700444 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000445 [self disconnect];
446 NSDictionary *userInfo = @{
447 NSLocalizedDescriptionKey: @"Failed to create session description.",
448 };
449 NSError *sdpError =
450 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
451 code:kARDAppClientErrorCreateSDP
452 userInfo:userInfo];
453 [_delegate appClient:self didError:sdpError];
454 return;
455 }
hjon79858f82016-03-13 22:08:26 -0700456 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200457 [_peerConnection setLocalDescription:sdp
hjon79858f82016-03-13 22:08:26 -0700458 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200459 ARDAppClient *strongSelf = weakSelf;
460 [strongSelf peerConnection:strongSelf.peerConnection
461 didSetSessionDescriptionWithError:error];
462 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000463 ARDSessionDescriptionMessage *message =
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200464 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000465 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800466 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000467 });
468}
469
470- (void)peerConnection:(RTCPeerConnection *)peerConnection
471 didSetSessionDescriptionWithError:(NSError *)error {
472 dispatch_async(dispatch_get_main_queue(), ^{
473 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700474 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000475 [self disconnect];
476 NSDictionary *userInfo = @{
477 NSLocalizedDescriptionKey: @"Failed to set session description.",
478 };
479 NSError *sdpError =
480 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
481 code:kARDAppClientErrorSetSDP
482 userInfo:userInfo];
483 [_delegate appClient:self didError:sdpError];
484 return;
485 }
486 // If we're answering and we've just set the remote offer we need to create
487 // an answer and set the local description.
488 if (!_isInitiator && !_peerConnection.localDescription) {
489 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700490 __weak ARDAppClient *weakSelf = self;
491 [_peerConnection answerForConstraints:constraints
492 completionHandler:^(RTCSessionDescription *sdp,
493 NSError *error) {
494 ARDAppClient *strongSelf = weakSelf;
495 [strongSelf peerConnection:strongSelf.peerConnection
496 didCreateSessionDescription:sdp
497 error:error];
498 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000499 }
500 });
501}
502
503#pragma mark - Private
504
tkchin204177f2016-06-14 15:03:11 -0700505#if defined(WEBRTC_IOS)
506
507- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
508 NSParameterAssert(fileName.length);
509 NSArray *paths = NSSearchPathForDirectoriesInDomains(
510 NSDocumentDirectory, NSUserDomainMask, YES);
511 NSString *documentsDirPath = paths.firstObject;
512 NSString *filePath =
513 [documentsDirPath stringByAppendingPathComponent:fileName];
514 return filePath;
515}
516
517#endif
518
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000519- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000520 return _clientId.length;
521}
522
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000523// Begins the peer connection connection process if we have both joined a room
524// on the room server and tried to obtain a TURN server. Otherwise does nothing.
525// A peer connection object will be created with a stream that contains local
526// audio and video capture. If this client is the caller, an offer is created as
527// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000528- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000529 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000530 return;
531 }
532 self.state = kARDAppClientStateConnected;
533
534 // Create peer connection.
535 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700536 RTCConfiguration *config = [[RTCConfiguration alloc] init];
537 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700538 _peerConnection = [_factory peerConnectionWithConfiguration:config
539 constraints:constraints
540 delegate:self];
Alex Naresta5fbc232017-10-18 18:31:07 +0200541 _bitrateAllocationStrategy = [ARDBitrateAllocationStrategy
542 createAudioPriorityBitrateAllocationStrategyForPeerConnection:_peerConnection
543 withAudioTrack:kARDAudioTrackId
544 sufficientAudioBitrate:kSufficientAudioBitrate];
545
skvladf3569c82016-04-29 15:30:16 -0700546 // Create AV senders.
Alex Narestb3944f02017-10-13 14:56:18 +0200547 [self createMediaSenders];
Alex Naresta5fbc232017-10-18 18:31:07 +0200548
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000549 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000550 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700551 __weak ARDAppClient *weakSelf = self;
552 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
553 completionHandler:^(RTCSessionDescription *sdp,
554 NSError *error) {
555 ARDAppClient *strongSelf = weakSelf;
556 [strongSelf peerConnection:strongSelf.peerConnection
557 didCreateSessionDescription:sdp
558 error:error];
559 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000560 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000561 // Check if we've received an offer.
562 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000563 }
ivoc14d5dbe2016-07-04 07:06:55 -0700564#if defined(WEBRTC_IOS)
565 // Start event log.
566 if (kARDAppClientEnableRtcEventLog) {
567 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
568 if (![_peerConnection startRtcEventLogWithFilePath:filePath
569 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
570 RTCLogError(@"Failed to start event logging.");
571 }
572 }
peah5085b0c2016-08-25 22:15:14 -0700573
574 // Start aecdump diagnostic recording.
Anders Carlssone1500582017-06-15 16:05:13 +0200575 if ([_settings currentCreateAecDumpSettingFromStore]) {
tkchinfce0e2c2016-08-30 12:58:11 -0700576 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
577 if (![_factory startAecDumpWithFilePath:filePath
578 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
579 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700580 }
581 }
ivoc14d5dbe2016-07-04 07:06:55 -0700582#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000583}
584
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000585// Processes the messages that we've received from the room server and the
586// signaling channel. The offer or answer message must be processed before other
587// signaling messages, however they can arrive out of order. Hence, this method
588// only processes pending messages if there is a peer connection object and
589// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000590- (void)drainMessageQueueIfReady {
591 if (!_peerConnection || !_hasReceivedSdp) {
592 return;
593 }
594 for (ARDSignalingMessage *message in _messageQueue) {
595 [self processSignalingMessage:message];
596 }
597 [_messageQueue removeAllObjects];
598}
599
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000600// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000601- (void)processSignalingMessage:(ARDSignalingMessage *)message {
602 NSParameterAssert(_peerConnection ||
603 message.type == kARDSignalingMessageTypeBye);
604 switch (message.type) {
605 case kARDSignalingMessageTypeOffer:
606 case kARDSignalingMessageTypeAnswer: {
607 ARDSessionDescriptionMessage *sdpMessage =
608 (ARDSessionDescriptionMessage *)message;
609 RTCSessionDescription *description = sdpMessage.sessionDescription;
hjon79858f82016-03-13 22:08:26 -0700610 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200611 [_peerConnection setRemoteDescription:description
hjon79858f82016-03-13 22:08:26 -0700612 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200613 ARDAppClient *strongSelf = weakSelf;
614 [strongSelf peerConnection:strongSelf.peerConnection
615 didSetSessionDescriptionWithError:error];
616 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000617 break;
618 }
619 case kARDSignalingMessageTypeCandidate: {
620 ARDICECandidateMessage *candidateMessage =
621 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700622 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000623 break;
624 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700625 case kARDSignalingMessageTypeCandidateRemoval: {
626 ARDICECandidateRemovalMessage *candidateMessage =
627 (ARDICECandidateRemovalMessage *)message;
628 [_peerConnection removeIceCandidates:candidateMessage.candidates];
629 break;
630 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000631 case kARDSignalingMessageTypeBye:
632 // Other client disconnected.
633 // TODO(tkchin): support waiting in room for next client. For now just
634 // disconnect.
635 [self disconnect];
636 break;
637 }
638}
639
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000640// Sends a signaling message to the other client. The caller will send messages
641// through the room server, whereas the callee will send messages over the
642// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000643- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
644 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000645 __weak ARDAppClient *weakSelf = self;
646 [_roomServerClient sendMessage:message
647 forRoomId:_roomId
648 clientId:_clientId
649 completionHandler:^(ARDMessageResponse *response,
650 NSError *error) {
651 ARDAppClient *strongSelf = weakSelf;
652 if (error) {
653 [strongSelf.delegate appClient:strongSelf didError:error];
654 return;
655 }
656 NSError *messageError =
657 [[strongSelf class] errorForMessageResultType:response.result];
658 if (messageError) {
659 [strongSelf.delegate appClient:strongSelf didError:messageError];
660 return;
661 }
662 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000663 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000664 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000665 }
666}
667
denicija9af2b602016-11-17 00:43:43 -0800668- (void)setMaxBitrateForPeerConnectionVideoSender {
669 for (RTCRtpSender *sender in _peerConnection.senders) {
670 if (sender.track != nil) {
671 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700672 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800673 }
674 }
denicija8c375de2016-11-08 06:28:17 -0800675 }
676}
677
denicija9af2b602016-11-17 00:43:43 -0800678- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
679 if (maxBitrate.intValue <= 0) {
680 return;
681 }
682
683 RTCRtpParameters *parametersToModify = sender.parameters;
684 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
685 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
686 }
687 [sender setParameters:parametersToModify];
688}
689
Alex Narestb3944f02017-10-13 14:56:18 +0200690- (void)createMediaSenders {
tkchinab1293a2016-08-30 12:35:05 -0700691 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
692 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
693 RTCAudioTrack *track = [_factory audioTrackWithSource:source
694 trackId:kARDAudioTrackId];
Alex Narestb3944f02017-10-13 14:56:18 +0200695 RTCMediaStream *stream = [_factory mediaStreamWithStreamId:kARDMediaStreamId];
696 [stream addAudioTrack:track];
697 _localVideoTrack = [self createLocalVideoTrack];
698 if (_localVideoTrack) {
699 [stream addVideoTrack:_localVideoTrack];
700 }
701 [_peerConnection addStream:stream];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700702}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000703
Zeke Chin57cc74e2015-05-05 07:52:31 -0700704- (RTCVideoTrack *)createLocalVideoTrack {
705 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000706 // The iOS simulator doesn't provide any sort of camera capture
707 // support or emulation (http://goo.gl/rHAnC1) so don't bother
708 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700709#if !TARGET_IPHONE_SIMULATOR
Anders Carlssone1500582017-06-15 16:05:13 +0200710 if (![_settings currentAudioOnlySettingFromStore]) {
sakalc522e752017-04-05 12:17:48 -0700711 RTCVideoSource *source = [_factory videoSource];
712 RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
713 [_delegate appClient:self didCreateLocalCapturer:capturer];
haysc913e6452015-10-02 11:44:03 -0700714 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700715 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700716 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700717 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000718#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700719 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000720}
721
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000722#pragma mark - Collider methods
723
724- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000725 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000726 return;
727 }
728 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000729 if (!_channel) {
730 _channel =
731 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
732 restURL:_websocketRestURL
733 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700734 if (_isLoopback) {
735 _loopbackChannel =
736 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
737 restURL:_websocketRestURL];
738 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000739 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000740 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700741 if (_isLoopback) {
742 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
743 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000744}
745
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000746#pragma mark - Defaults
747
tkchinab1293a2016-08-30 12:35:05 -0700748 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
Anders Carlssone1500582017-06-15 16:05:13 +0200749 NSString *valueLevelControl = [_settings currentUseLevelControllerSettingFromStore] ?
750 kRTCMediaConstraintsValueTrue :
751 kRTCMediaConstraintsValueFalse;
tkchinab1293a2016-08-30 12:35:05 -0700752 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700753 RTCMediaConstraints *constraints =
754 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
755 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700756 return constraints;
757}
758
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000759- (RTCMediaConstraints *)defaultAnswerConstraints {
760 return [self defaultOfferConstraints];
761}
762
763- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700764 NSDictionary *mandatoryConstraints = @{
765 @"OfferToReceiveAudio" : @"true",
766 @"OfferToReceiveVideo" : @"true"
767 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000768 RTCMediaConstraints* constraints =
769 [[RTCMediaConstraints alloc]
770 initWithMandatoryConstraints:mandatoryConstraints
771 optionalConstraints:nil];
772 return constraints;
773}
774
775- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000776 if (_defaultPeerConnectionConstraints) {
777 return _defaultPeerConnectionConstraints;
778 }
haysc913e6452015-10-02 11:44:03 -0700779 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700780 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000781 RTCMediaConstraints* constraints =
782 [[RTCMediaConstraints alloc]
783 initWithMandatoryConstraints:nil
784 optionalConstraints:optionalConstraints];
785 return constraints;
786}
787
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000788#pragma mark - Errors
789
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000790+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000791 NSError *error = nil;
792 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000793 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000794 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000795 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000796 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
797 code:kARDAppClientErrorUnknown
798 userInfo:@{
799 NSLocalizedDescriptionKey: @"Unknown error.",
800 }];
801 break;
802 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000803 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000804 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
805 code:kARDAppClientErrorRoomFull
806 userInfo:@{
807 NSLocalizedDescriptionKey: @"Room is full.",
808 }];
809 break;
810 }
811 }
812 return error;
813}
814
815+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
816 NSError *error = nil;
817 switch (resultType) {
818 case kARDMessageResultTypeSuccess:
819 break;
820 case kARDMessageResultTypeUnknown:
821 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
822 code:kARDAppClientErrorUnknown
823 userInfo:@{
824 NSLocalizedDescriptionKey: @"Unknown error.",
825 }];
826 break;
827 case kARDMessageResultTypeInvalidClient:
828 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
829 code:kARDAppClientErrorInvalidClient
830 userInfo:@{
831 NSLocalizedDescriptionKey: @"Invalid client.",
832 }];
833 break;
834 case kARDMessageResultTypeInvalidRoom:
835 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
836 code:kARDAppClientErrorInvalidRoom
837 userInfo:@{
838 NSLocalizedDescriptionKey: @"Invalid room.",
839 }];
840 break;
841 }
842 return error;
843}
844
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000845@end