blob: 497a137b6c16d43b0eb6e33a127770afc8cae138 [file] [log] [blame]
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00001/*
Donald E Curtisa8736442015-08-05 15:48:13 -07002 * Copyright 2014 The WebRTC Project Authors. All rights reserved.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00003 *
Donald E Curtisa8736442015-08-05 15:48:13 -07004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +00009 */
10
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000011#import "ARDAppClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000012
tkchin9eeb6242016-04-27 01:54:20 -070013#import "WebRTC/RTCAVFoundationVideoSource.h"
tkchin9eeb6242016-04-27 01:54:20 -070014#import "WebRTC/RTCAudioTrack.h"
15#import "WebRTC/RTCConfiguration.h"
16#import "WebRTC/RTCFileLogger.h"
17#import "WebRTC/RTCIceServer.h"
18#import "WebRTC/RTCLogging.h"
19#import "WebRTC/RTCMediaConstraints.h"
20#import "WebRTC/RTCMediaStream.h"
21#import "WebRTC/RTCPeerConnectionFactory.h"
skvladf3569c82016-04-29 15:30:16 -070022#import "WebRTC/RTCRtpSender.h"
tkchin204177f2016-06-14 15:03:11 -070023#import "WebRTC/RTCTracing.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000024
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000025#import "ARDAppEngineClient.h"
kthelgasoncc882af2017-01-13 05:59:46 -080026#import "ARDTURNClient+Internal.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000027#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000028#import "ARDMessageResponse.h"
Zeke Chin71f6f442015-06-29 14:34:58 -070029#import "ARDSDPUtils.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000030#import "ARDSignalingMessage.h"
31#import "ARDUtilities.h"
32#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070033#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000034#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070035
kthelgasoncc882af2017-01-13 05:59:46 -080036static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000037
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000038static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
39static NSInteger const kARDAppClientErrorUnknown = -1;
40static NSInteger const kARDAppClientErrorRoomFull = -2;
41static NSInteger const kARDAppClientErrorCreateSDP = -3;
42static NSInteger const kARDAppClientErrorSetSDP = -4;
43static NSInteger const kARDAppClientErrorInvalidClient = -5;
44static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070045static NSString * const kARDMediaStreamId = @"ARDAMS";
46static NSString * const kARDAudioTrackId = @"ARDAMSa0";
47static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080048static NSString * const kARDVideoTrackKind = @"video";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000049
tkchin204177f2016-06-14 15:03:11 -070050// TODO(tkchin): Add these as UI options.
tkchind1fb26d2016-02-03 01:51:18 -080051static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070052static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070053static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070054static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
denicija9af2b602016-11-17 00:43:43 -080055static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080056
Zeke Chind3325802015-08-14 11:00:02 -070057// We need a proxy to NSTimer because it causes a strong retain cycle. When
58// using the proxy, |invalidate| must be called before it properly deallocs.
59@interface ARDTimerProxy : NSObject
60
61- (instancetype)initWithInterval:(NSTimeInterval)interval
62 repeats:(BOOL)repeats
63 timerHandler:(void (^)(void))timerHandler;
64- (void)invalidate;
65
66@end
67
68@implementation ARDTimerProxy {
69 NSTimer *_timer;
70 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070071}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000072
Zeke Chind3325802015-08-14 11:00:02 -070073- (instancetype)initWithInterval:(NSTimeInterval)interval
74 repeats:(BOOL)repeats
75 timerHandler:(void (^)(void))timerHandler {
76 NSParameterAssert(timerHandler);
77 if (self = [super init]) {
78 _timerHandler = timerHandler;
79 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
80 target:self
81 selector:@selector(timerDidFire:)
82 userInfo:nil
83 repeats:repeats];
84 }
85 return self;
86}
87
88- (void)invalidate {
89 [_timer invalidate];
90}
91
92- (void)timerDidFire:(NSTimer *)timer {
93 _timerHandler();
94}
95
96@end
97
98@implementation ARDAppClient {
99 RTCFileLogger *_fileLogger;
100 ARDTimerProxy *_statsTimer;
denicijad17d5362016-11-02 02:56:09 -0700101 RTCMediaConstraints *_cameraConstraints;
denicija8c375de2016-11-08 06:28:17 -0800102 NSNumber *_maxBitrate;
sakal68b5df92017-03-17 09:01:59 -0700103 NSString *_videoCodec;
Zeke Chind3325802015-08-14 11:00:02 -0700104}
105
106@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000107@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700108@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000109@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000110@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700111@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000112@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000113@synthesize peerConnection = _peerConnection;
114@synthesize factory = _factory;
115@synthesize messageQueue = _messageQueue;
116@synthesize isTurnComplete = _isTurnComplete;
117@synthesize hasReceivedSdp = _hasReceivedSdp;
118@synthesize roomId = _roomId;
119@synthesize clientId = _clientId;
120@synthesize isInitiator = _isInitiator;
121@synthesize iceServers = _iceServers;
122@synthesize webSocketURL = _websocketURL;
123@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000124@synthesize defaultPeerConnectionConstraints =
125 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700126@synthesize isLoopback = _isLoopback;
127@synthesize isAudioOnly = _isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700128@synthesize shouldMakeAecDump = _shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700129@synthesize shouldUseLevelControl = _shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000130
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000131- (instancetype)init {
sakal68b5df92017-03-17 09:01:59 -0700132 return [self initWithDelegate:nil preferVideoCodec:@"H264"];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000133}
134
sakal68b5df92017-03-17 09:01:59 -0700135- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate
136 preferVideoCodec:(NSString *)codec {
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;
sakal68b5df92017-03-17 09:01:59 -0700140 _videoCodec = codec;
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
haysc913e6452015-10-02 11:44:03 -0700216 isLoopback:(BOOL)isLoopback
peah5085b0c2016-08-25 22:15:14 -0700217 isAudioOnly:(BOOL)isAudioOnly
tkchinab1293a2016-08-30 12:35:05 -0700218 shouldMakeAecDump:(BOOL)shouldMakeAecDump
219 shouldUseLevelControl:(BOOL)shouldUseLevelControl {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000220 NSParameterAssert(roomId.length);
221 NSParameterAssert(_state == kARDAppClientStateDisconnected);
haysc913e6452015-10-02 11:44:03 -0700222 _isLoopback = isLoopback;
223 _isAudioOnly = isAudioOnly;
peah5085b0c2016-08-25 22:15:14 -0700224 _shouldMakeAecDump = shouldMakeAecDump;
tkchinab1293a2016-08-30 12:35:05 -0700225 _shouldUseLevelControl = shouldUseLevelControl;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000226 self.state = kARDAppClientStateConnecting;
227
tkchind1fb26d2016-02-03 01:51:18 -0800228#if defined(WEBRTC_IOS)
229 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700230 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800231 RTCStartInternalCapture(filePath);
232 }
233#endif
234
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000235 // Request TURN.
236 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000237 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
238 NSError *error) {
239 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700240 RTCLogError("Error retrieving TURN servers: %@",
241 error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000242 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000243 ARDAppClient *strongSelf = weakSelf;
244 [strongSelf.iceServers addObjectsFromArray:turnServers];
245 strongSelf.isTurnComplete = YES;
246 [strongSelf startSignalingIfReady];
247 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000248
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000249 // Join room on room server.
250 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700251 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000252 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000253 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000254 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000255 [strongSelf.delegate appClient:strongSelf didError:error];
256 return;
257 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000258 NSError *joinError =
259 [[strongSelf class] errorForJoinResultType:response.result];
260 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700261 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000262 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000263 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000264 return;
265 }
tkchinc3f46a92015-07-23 12:50:55 -0700266 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000267 strongSelf.roomId = response.roomId;
268 strongSelf.clientId = response.clientId;
269 strongSelf.isInitiator = response.isInitiator;
270 for (ARDSignalingMessage *message in response.messages) {
271 if (message.type == kARDSignalingMessageTypeOffer ||
272 message.type == kARDSignalingMessageTypeAnswer) {
273 strongSelf.hasReceivedSdp = YES;
274 [strongSelf.messageQueue insertObject:message atIndex:0];
275 } else {
276 [strongSelf.messageQueue addObject:message];
277 }
278 }
279 strongSelf.webSocketURL = response.webSocketURL;
280 strongSelf.webSocketRestURL = response.webSocketRestURL;
281 [strongSelf registerWithColliderIfReady];
282 [strongSelf startSignalingIfReady];
283 }];
284}
285
286- (void)disconnect {
287 if (_state == kARDAppClientStateDisconnected) {
288 return;
289 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000290 if (self.hasJoinedRoomServerRoom) {
291 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000292 clientId:_clientId
293 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000294 }
295 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000296 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000297 // Tell the other client we're hanging up.
298 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000299 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000300 }
301 // Disconnect from collider.
302 _channel = nil;
303 }
304 _clientId = nil;
305 _roomId = nil;
306 _isInitiator = NO;
307 _hasReceivedSdp = NO;
308 _messageQueue = [NSMutableArray array];
ivoc14d5dbe2016-07-04 07:06:55 -0700309#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700310 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700311 [_peerConnection stopRtcEventLog];
312#endif
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
denicijad17d5362016-11-02 02:56:09 -0700322- (void)setCameraConstraints:(RTCMediaConstraints *)mediaConstraints {
323 _cameraConstraints = mediaConstraints;
324}
325
denicija8c375de2016-11-08 06:28:17 -0800326- (void)setMaxBitrate:(NSNumber *)maxBitrate {
327 _maxBitrate = maxBitrate;
328}
329
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000330#pragma mark - ARDSignalingChannelDelegate
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000331
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000332- (void)channel:(id<ARDSignalingChannel>)channel
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000333 didReceiveMessage:(ARDSignalingMessage *)message {
334 switch (message.type) {
335 case kARDSignalingMessageTypeOffer:
336 case kARDSignalingMessageTypeAnswer:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000337 // Offers and answers must be processed before any other message, so we
338 // place them at the front of the queue.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000339 _hasReceivedSdp = YES;
340 [_messageQueue insertObject:message atIndex:0];
341 break;
342 case kARDSignalingMessageTypeCandidate:
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700343 case kARDSignalingMessageTypeCandidateRemoval:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000344 [_messageQueue addObject:message];
345 break;
346 case kARDSignalingMessageTypeBye:
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000347 // Disconnects can be processed immediately.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000348 [self processSignalingMessage:message];
349 return;
350 }
351 [self drainMessageQueueIfReady];
352}
353
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000354- (void)channel:(id<ARDSignalingChannel>)channel
355 didChangeState:(ARDSignalingChannelState)state {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000356 switch (state) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000357 case kARDSignalingChannelStateOpen:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000358 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000359 case kARDSignalingChannelStateRegistered:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000360 break;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000361 case kARDSignalingChannelStateClosed:
362 case kARDSignalingChannelStateError:
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000363 // TODO(tkchin): reconnection scenarios. Right now we just disconnect
364 // completely if the websocket connection fails.
365 [self disconnect];
366 break;
367 }
368}
369
370#pragma mark - RTCPeerConnectionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000371// Callbacks for this delegate occur on non-main thread and need to be
372// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000373
374- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700375 didChangeSignalingState:(RTCSignalingState)stateChanged {
376 RTCLog(@"Signaling state changed: %ld", (long)stateChanged);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000377}
378
379- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700380 didAddStream:(RTCMediaStream *)stream {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000381 dispatch_async(dispatch_get_main_queue(), ^{
tkchinc3f46a92015-07-23 12:50:55 -0700382 RTCLog(@"Received %lu video tracks and %lu audio tracks",
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000383 (unsigned long)stream.videoTracks.count,
384 (unsigned long)stream.audioTracks.count);
385 if (stream.videoTracks.count) {
386 RTCVideoTrack *videoTrack = stream.videoTracks[0];
387 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack];
388 }
389 });
390}
391
392- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700393 didRemoveStream:(RTCMediaStream *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700394 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000395}
396
hjon79858f82016-03-13 22:08:26 -0700397- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection {
tkchinc3f46a92015-07-23 12:50:55 -0700398 RTCLog(@"WARNING: Renegotiation needed but unimplemented.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000399}
400
401- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700402 didChangeIceConnectionState:(RTCIceConnectionState)newState {
403 RTCLog(@"ICE state changed: %ld", (long)newState);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000404 dispatch_async(dispatch_get_main_queue(), ^{
405 [_delegate appClient:self didChangeConnectionState:newState];
406 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000407}
408
409- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700410 didChangeIceGatheringState:(RTCIceGatheringState)newState {
411 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000412}
413
414- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700415 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000416 dispatch_async(dispatch_get_main_queue(), ^{
417 ARDICECandidateMessage *message =
418 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
419 [self sendSignalingMessage:message];
420 });
421}
422
Zeke Chind3325802015-08-14 11:00:02 -0700423- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700424 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
425 dispatch_async(dispatch_get_main_queue(), ^{
426 ARDICECandidateRemovalMessage *message =
427 [[ARDICECandidateRemovalMessage alloc]
428 initWithRemovedCandidates:candidates];
429 [self sendSignalingMessage:message];
430 });
431}
432
433- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700434 didOpenDataChannel:(RTCDataChannel *)dataChannel {
435}
436
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000437#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000438// Callbacks for this delegate occur on non-main thread and need to be
439// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000440
441- (void)peerConnection:(RTCPeerConnection *)peerConnection
442 didCreateSessionDescription:(RTCSessionDescription *)sdp
443 error:(NSError *)error {
444 dispatch_async(dispatch_get_main_queue(), ^{
445 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700446 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000447 [self disconnect];
448 NSDictionary *userInfo = @{
449 NSLocalizedDescriptionKey: @"Failed to create session description.",
450 };
451 NSError *sdpError =
452 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
453 code:kARDAppClientErrorCreateSDP
454 userInfo:userInfo];
455 [_delegate appClient:self didError:sdpError];
456 return;
457 }
sakal68b5df92017-03-17 09:01:59 -0700458 // Prefer codec from settings if available.
459 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700460 [ARDSDPUtils descriptionForDescription:sdp
sakal68b5df92017-03-17 09:01:59 -0700461 preferredVideoCodec:_videoCodec];
hjon79858f82016-03-13 22:08:26 -0700462 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700463 [_peerConnection setLocalDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700464 completionHandler:^(NSError *error) {
465 ARDAppClient *strongSelf = weakSelf;
466 [strongSelf peerConnection:strongSelf.peerConnection
467 didSetSessionDescriptionWithError:error];
468 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000469 ARDSessionDescriptionMessage *message =
Zeke Chin71f6f442015-06-29 14:34:58 -0700470 [[ARDSessionDescriptionMessage alloc]
sakal68b5df92017-03-17 09:01:59 -0700471 initWithDescription:sdpPreferringCodec];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000472 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800473 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000474 });
475}
476
477- (void)peerConnection:(RTCPeerConnection *)peerConnection
478 didSetSessionDescriptionWithError:(NSError *)error {
479 dispatch_async(dispatch_get_main_queue(), ^{
480 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700481 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000482 [self disconnect];
483 NSDictionary *userInfo = @{
484 NSLocalizedDescriptionKey: @"Failed to set session description.",
485 };
486 NSError *sdpError =
487 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
488 code:kARDAppClientErrorSetSDP
489 userInfo:userInfo];
490 [_delegate appClient:self didError:sdpError];
491 return;
492 }
493 // If we're answering and we've just set the remote offer we need to create
494 // an answer and set the local description.
495 if (!_isInitiator && !_peerConnection.localDescription) {
496 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700497 __weak ARDAppClient *weakSelf = self;
498 [_peerConnection answerForConstraints:constraints
499 completionHandler:^(RTCSessionDescription *sdp,
500 NSError *error) {
501 ARDAppClient *strongSelf = weakSelf;
502 [strongSelf peerConnection:strongSelf.peerConnection
503 didCreateSessionDescription:sdp
504 error:error];
505 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000506 }
507 });
508}
509
510#pragma mark - Private
511
tkchin204177f2016-06-14 15:03:11 -0700512#if defined(WEBRTC_IOS)
513
514- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
515 NSParameterAssert(fileName.length);
516 NSArray *paths = NSSearchPathForDirectoriesInDomains(
517 NSDocumentDirectory, NSUserDomainMask, YES);
518 NSString *documentsDirPath = paths.firstObject;
519 NSString *filePath =
520 [documentsDirPath stringByAppendingPathComponent:fileName];
521 return filePath;
522}
523
524#endif
525
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000526- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000527 return _clientId.length;
528}
529
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000530// Begins the peer connection connection process if we have both joined a room
531// on the room server and tried to obtain a TURN server. Otherwise does nothing.
532// A peer connection object will be created with a stream that contains local
533// audio and video capture. If this client is the caller, an offer is created as
534// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000535- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000536 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000537 return;
538 }
539 self.state = kARDAppClientStateConnected;
540
541 // Create peer connection.
542 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700543 RTCConfiguration *config = [[RTCConfiguration alloc] init];
544 config.iceServers = _iceServers;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700545 _peerConnection = [_factory peerConnectionWithConfiguration:config
546 constraints:constraints
547 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700548 // Create AV senders.
549 [self createAudioSender];
550 [self createVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000551 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000552 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700553 __weak ARDAppClient *weakSelf = self;
554 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
555 completionHandler:^(RTCSessionDescription *sdp,
556 NSError *error) {
557 ARDAppClient *strongSelf = weakSelf;
558 [strongSelf peerConnection:strongSelf.peerConnection
559 didCreateSessionDescription:sdp
560 error:error];
561 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000562 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000563 // Check if we've received an offer.
564 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000565 }
ivoc14d5dbe2016-07-04 07:06:55 -0700566#if defined(WEBRTC_IOS)
567 // Start event log.
568 if (kARDAppClientEnableRtcEventLog) {
569 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
570 if (![_peerConnection startRtcEventLogWithFilePath:filePath
571 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
572 RTCLogError(@"Failed to start event logging.");
573 }
574 }
peah5085b0c2016-08-25 22:15:14 -0700575
576 // Start aecdump diagnostic recording.
577 if (_shouldMakeAecDump) {
tkchinfce0e2c2016-08-30 12:58:11 -0700578 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
579 if (![_factory startAecDumpWithFilePath:filePath
580 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
581 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700582 }
583 }
ivoc14d5dbe2016-07-04 07:06:55 -0700584#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000585}
586
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000587// Processes the messages that we've received from the room server and the
588// signaling channel. The offer or answer message must be processed before other
589// signaling messages, however they can arrive out of order. Hence, this method
590// only processes pending messages if there is a peer connection object and
591// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000592- (void)drainMessageQueueIfReady {
593 if (!_peerConnection || !_hasReceivedSdp) {
594 return;
595 }
596 for (ARDSignalingMessage *message in _messageQueue) {
597 [self processSignalingMessage:message];
598 }
599 [_messageQueue removeAllObjects];
600}
601
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000602// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000603- (void)processSignalingMessage:(ARDSignalingMessage *)message {
604 NSParameterAssert(_peerConnection ||
605 message.type == kARDSignalingMessageTypeBye);
606 switch (message.type) {
607 case kARDSignalingMessageTypeOffer:
608 case kARDSignalingMessageTypeAnswer: {
609 ARDSessionDescriptionMessage *sdpMessage =
610 (ARDSessionDescriptionMessage *)message;
611 RTCSessionDescription *description = sdpMessage.sessionDescription;
sakal68b5df92017-03-17 09:01:59 -0700612 // Prefer codec from settings if available.
613 RTCSessionDescription *sdpPreferringCodec =
Zeke Chin71f6f442015-06-29 14:34:58 -0700614 [ARDSDPUtils descriptionForDescription:description
sakal68b5df92017-03-17 09:01:59 -0700615 preferredVideoCodec:_videoCodec];
hjon79858f82016-03-13 22:08:26 -0700616 __weak ARDAppClient *weakSelf = self;
sakal68b5df92017-03-17 09:01:59 -0700617 [_peerConnection setRemoteDescription:sdpPreferringCodec
hjon79858f82016-03-13 22:08:26 -0700618 completionHandler:^(NSError *error) {
619 ARDAppClient *strongSelf = weakSelf;
620 [strongSelf peerConnection:strongSelf.peerConnection
621 didSetSessionDescriptionWithError:error];
622 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000623 break;
624 }
625 case kARDSignalingMessageTypeCandidate: {
626 ARDICECandidateMessage *candidateMessage =
627 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700628 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000629 break;
630 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700631 case kARDSignalingMessageTypeCandidateRemoval: {
632 ARDICECandidateRemovalMessage *candidateMessage =
633 (ARDICECandidateRemovalMessage *)message;
634 [_peerConnection removeIceCandidates:candidateMessage.candidates];
635 break;
636 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000637 case kARDSignalingMessageTypeBye:
638 // Other client disconnected.
639 // TODO(tkchin): support waiting in room for next client. For now just
640 // disconnect.
641 [self disconnect];
642 break;
643 }
644}
645
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000646// Sends a signaling message to the other client. The caller will send messages
647// through the room server, whereas the callee will send messages over the
648// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000649- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
650 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000651 __weak ARDAppClient *weakSelf = self;
652 [_roomServerClient sendMessage:message
653 forRoomId:_roomId
654 clientId:_clientId
655 completionHandler:^(ARDMessageResponse *response,
656 NSError *error) {
657 ARDAppClient *strongSelf = weakSelf;
658 if (error) {
659 [strongSelf.delegate appClient:strongSelf didError:error];
660 return;
661 }
662 NSError *messageError =
663 [[strongSelf class] errorForMessageResultType:response.result];
664 if (messageError) {
665 [strongSelf.delegate appClient:strongSelf didError:messageError];
666 return;
667 }
668 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000669 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000670 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000671 }
672}
673
skvladf3569c82016-04-29 15:30:16 -0700674- (RTCRtpSender *)createVideoSender {
675 RTCRtpSender *sender =
676 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
677 streamId:kARDMediaStreamId];
678 RTCVideoTrack *track = [self createLocalVideoTrack];
679 if (track) {
680 sender.track = track;
681 [_delegate appClient:self didReceiveLocalVideoTrack:track];
Zeke Chin57cc74e2015-05-05 07:52:31 -0700682 }
denicija9af2b602016-11-17 00:43:43 -0800683
skvladf3569c82016-04-29 15:30:16 -0700684 return sender;
685}
686
denicija9af2b602016-11-17 00:43:43 -0800687- (void)setMaxBitrateForPeerConnectionVideoSender {
688 for (RTCRtpSender *sender in _peerConnection.senders) {
689 if (sender.track != nil) {
690 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
691 [self setMaxBitrate:_maxBitrate forVideoSender:sender];
692 }
693 }
denicija8c375de2016-11-08 06:28:17 -0800694 }
695}
696
denicija9af2b602016-11-17 00:43:43 -0800697- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
698 if (maxBitrate.intValue <= 0) {
699 return;
700 }
701
702 RTCRtpParameters *parametersToModify = sender.parameters;
703 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
704 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
705 }
706 [sender setParameters:parametersToModify];
707}
708
skvladf3569c82016-04-29 15:30:16 -0700709- (RTCRtpSender *)createAudioSender {
tkchinab1293a2016-08-30 12:35:05 -0700710 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
711 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
712 RTCAudioTrack *track = [_factory audioTrackWithSource:source
713 trackId:kARDAudioTrackId];
skvladf3569c82016-04-29 15:30:16 -0700714 RTCRtpSender *sender =
715 [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio
716 streamId:kARDMediaStreamId];
skvladf3569c82016-04-29 15:30:16 -0700717 sender.track = track;
718 return sender;
Zeke Chin57cc74e2015-05-05 07:52:31 -0700719}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000720
Zeke Chin57cc74e2015-05-05 07:52:31 -0700721- (RTCVideoTrack *)createLocalVideoTrack {
722 RTCVideoTrack* localVideoTrack = nil;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000723 // The iOS simulator doesn't provide any sort of camera capture
724 // support or emulation (http://goo.gl/rHAnC1) so don't bother
725 // trying to open a local stream.
kthelgason314bc5f2016-08-31 10:23:27 -0700726#if !TARGET_IPHONE_SIMULATOR
haysc913e6452015-10-02 11:44:03 -0700727 if (!_isAudioOnly) {
denicijad17d5362016-11-02 02:56:09 -0700728 RTCMediaConstraints *cameraConstraints =
729 [self cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700730 RTCAVFoundationVideoSource *source =
denicijad17d5362016-11-02 02:56:09 -0700731 [_factory avFoundationVideoSourceWithConstraints:cameraConstraints];
haysc913e6452015-10-02 11:44:03 -0700732 localVideoTrack =
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700733 [_factory videoTrackWithSource:source
skvladf3569c82016-04-29 15:30:16 -0700734 trackId:kARDVideoTrackId];
haysc913e6452015-10-02 11:44:03 -0700735 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000736#endif
Zeke Chin57cc74e2015-05-05 07:52:31 -0700737 return localVideoTrack;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000738}
739
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000740#pragma mark - Collider methods
741
742- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000743 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000744 return;
745 }
746 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000747 if (!_channel) {
748 _channel =
749 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
750 restURL:_websocketRestURL
751 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700752 if (_isLoopback) {
753 _loopbackChannel =
754 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
755 restURL:_websocketRestURL];
756 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000757 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000758 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700759 if (_isLoopback) {
760 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
761 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000762}
763
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000764#pragma mark - Defaults
765
tkchinab1293a2016-08-30 12:35:05 -0700766 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
767 NSString *valueLevelControl = _shouldUseLevelControl ?
768 kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
769 NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
denicijad17d5362016-11-02 02:56:09 -0700770 RTCMediaConstraints *constraints =
771 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
772 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700773 return constraints;
774}
775
denicijad17d5362016-11-02 02:56:09 -0700776- (RTCMediaConstraints *)cameraConstraints {
777 return _cameraConstraints;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000778}
779
780- (RTCMediaConstraints *)defaultAnswerConstraints {
781 return [self defaultOfferConstraints];
782}
783
784- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700785 NSDictionary *mandatoryConstraints = @{
786 @"OfferToReceiveAudio" : @"true",
787 @"OfferToReceiveVideo" : @"true"
788 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000789 RTCMediaConstraints* constraints =
790 [[RTCMediaConstraints alloc]
791 initWithMandatoryConstraints:mandatoryConstraints
792 optionalConstraints:nil];
793 return constraints;
794}
795
796- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000797 if (_defaultPeerConnectionConstraints) {
798 return _defaultPeerConnectionConstraints;
799 }
haysc913e6452015-10-02 11:44:03 -0700800 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700801 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000802 RTCMediaConstraints* constraints =
803 [[RTCMediaConstraints alloc]
804 initWithMandatoryConstraints:nil
805 optionalConstraints:optionalConstraints];
806 return constraints;
807}
808
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000809#pragma mark - Errors
810
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000811+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000812 NSError *error = nil;
813 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000814 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000815 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000816 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000817 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
818 code:kARDAppClientErrorUnknown
819 userInfo:@{
820 NSLocalizedDescriptionKey: @"Unknown error.",
821 }];
822 break;
823 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000824 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000825 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
826 code:kARDAppClientErrorRoomFull
827 userInfo:@{
828 NSLocalizedDescriptionKey: @"Room is full.",
829 }];
830 break;
831 }
832 }
833 return error;
834}
835
836+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
837 NSError *error = nil;
838 switch (resultType) {
839 case kARDMessageResultTypeSuccess:
840 break;
841 case kARDMessageResultTypeUnknown:
842 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
843 code:kARDAppClientErrorUnknown
844 userInfo:@{
845 NSLocalizedDescriptionKey: @"Unknown error.",
846 }];
847 break;
848 case kARDMessageResultTypeInvalidClient:
849 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
850 code:kARDAppClientErrorInvalidClient
851 userInfo:@{
852 NSLocalizedDescriptionKey: @"Invalid client.",
853 }];
854 break;
855 case kARDMessageResultTypeInvalidRoom:
856 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
857 code:kARDAppClientErrorInvalidRoom
858 userInfo:@{
859 NSLocalizedDescriptionKey: @"Invalid room.",
860 }];
861 break;
862 }
863 return error;
864}
865
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000866@end