blob: d646c9e85dfe58a7c4eba7a5cd3c111cd30d3458 [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/RTCAudioTrack.h"
sakalc522e752017-04-05 12:17:48 -070014#import "WebRTC/RTCCameraVideoCapturer.h"
tkchin9eeb6242016-04-27 01:54:20 -070015#import "WebRTC/RTCConfiguration.h"
16#import "WebRTC/RTCFileLogger.h"
Daniela012b56b2017-11-15 13:15:24 +010017#import "WebRTC/RTCFileVideoCapturer.h"
tkchin9eeb6242016-04-27 01:54:20 -070018#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"
Steve Antonaf23b752018-03-01 09:22:48 -080024#import "WebRTC/RTCRtpTransceiver.h"
tkchin204177f2016-06-14 15:03:11 -070025#import "WebRTC/RTCTracing.h"
Anders Carlsson1d4c1522017-10-30 13:07:07 +010026#import "WebRTC/RTCVideoCodecFactory.h"
Kári Tristan Helgason0d3c9a32018-02-08 20:17:34 +010027#import "WebRTC/RTCVideoSource.h"
sakalc522e752017-04-05 12:17:48 -070028#import "WebRTC/RTCVideoTrack.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000029
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000030#import "ARDAppEngineClient.h"
Anders Carlsson358f2e02018-06-04 10:24:37 +020031#import "ARDExternalSampleCapturer.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000032#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000033#import "ARDMessageResponse.h"
sakalc4adacf2017-03-28 01:22:48 -070034#import "ARDSettingsModel.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000035#import "ARDSignalingMessage.h"
sakalc4adacf2017-03-28 01:22:48 -070036#import "ARDTURNClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000037#import "ARDUtilities.h"
38#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070039#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000040#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070041
kthelgasoncc882af2017-01-13 05:59:46 -080042static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000043
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000044static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
45static NSInteger const kARDAppClientErrorUnknown = -1;
46static NSInteger const kARDAppClientErrorRoomFull = -2;
47static NSInteger const kARDAppClientErrorCreateSDP = -3;
48static NSInteger const kARDAppClientErrorSetSDP = -4;
49static NSInteger const kARDAppClientErrorInvalidClient = -5;
50static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070051static NSString * const kARDMediaStreamId = @"ARDAMS";
52static NSString * const kARDAudioTrackId = @"ARDAMSa0";
53static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080054static NSString * const kARDVideoTrackKind = @"video";
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;
Zeke Chind3325802015-08-14 11:00:02 -0700109}
110
111@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000112@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700113@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000114@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000115@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700116@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000117@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000118@synthesize peerConnection = _peerConnection;
119@synthesize factory = _factory;
120@synthesize messageQueue = _messageQueue;
121@synthesize isTurnComplete = _isTurnComplete;
122@synthesize hasReceivedSdp = _hasReceivedSdp;
123@synthesize roomId = _roomId;
124@synthesize clientId = _clientId;
125@synthesize isInitiator = _isInitiator;
126@synthesize iceServers = _iceServers;
127@synthesize webSocketURL = _websocketURL;
128@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000129@synthesize defaultPeerConnectionConstraints =
130 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700131@synthesize isLoopback = _isLoopback;
Anders Carlsson358f2e02018-06-04 10:24:37 +0200132@synthesize broadcast = _broadcast;
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 Carlsson1d4c1522017-10-30 13:07:07 +0100224 RTCDefaultVideoDecoderFactory *decoderFactory = [[RTCDefaultVideoDecoderFactory alloc] init];
225 RTCDefaultVideoEncoderFactory *encoderFactory = [[RTCDefaultVideoEncoderFactory alloc] init];
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200226 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) {
Anders Carlsson358f2e02018-06-04 10:24:37 +0200242 RTCLogError(@"Error retrieving TURN servers: %@", error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000243 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000244 ARDAppClient *strongSelf = weakSelf;
245 [strongSelf.iceServers addObjectsFromArray:turnServers];
246 strongSelf.isTurnComplete = YES;
247 [strongSelf startSignalingIfReady];
248 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000249
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000250 // Join room on room server.
251 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700252 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000253 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000254 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000255 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000256 [strongSelf.delegate appClient:strongSelf didError:error];
257 return;
258 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000259 NSError *joinError =
260 [[strongSelf class] errorForJoinResultType:response.result];
261 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700262 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000263 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000264 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000265 return;
266 }
tkchinc3f46a92015-07-23 12:50:55 -0700267 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000268 strongSelf.roomId = response.roomId;
269 strongSelf.clientId = response.clientId;
270 strongSelf.isInitiator = response.isInitiator;
271 for (ARDSignalingMessage *message in response.messages) {
272 if (message.type == kARDSignalingMessageTypeOffer ||
273 message.type == kARDSignalingMessageTypeAnswer) {
274 strongSelf.hasReceivedSdp = YES;
275 [strongSelf.messageQueue insertObject:message atIndex:0];
276 } else {
277 [strongSelf.messageQueue addObject:message];
278 }
279 }
280 strongSelf.webSocketURL = response.webSocketURL;
281 strongSelf.webSocketRestURL = response.webSocketRestURL;
282 [strongSelf registerWithColliderIfReady];
283 [strongSelf startSignalingIfReady];
284 }];
285}
286
287- (void)disconnect {
288 if (_state == kARDAppClientStateDisconnected) {
289 return;
290 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000291 if (self.hasJoinedRoomServerRoom) {
292 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000293 clientId:_clientId
294 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000295 }
296 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000297 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000298 // Tell the other client we're hanging up.
299 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000300 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000301 }
302 // Disconnect from collider.
303 _channel = nil;
304 }
305 _clientId = nil;
306 _roomId = nil;
307 _isInitiator = NO;
308 _hasReceivedSdp = NO;
309 _messageQueue = [NSMutableArray array];
sakalc522e752017-04-05 12:17:48 -0700310 _localVideoTrack = nil;
ivoc14d5dbe2016-07-04 07:06:55 -0700311#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700312 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700313 [_peerConnection stopRtcEventLog];
314#endif
magjedcc8b9062017-07-24 07:32:33 -0700315 [_peerConnection close];
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 {
Steve Antonaf23b752018-03-01 09:22:48 -0800376 RTCLog(@"Stream with %lu video tracks and %lu audio tracks was added.",
377 (unsigned long)stream.videoTracks.count,
378 (unsigned long)stream.audioTracks.count);
379}
380
381- (void)peerConnection:(RTCPeerConnection *)peerConnection
382 didStartReceivingOnTransceiver:(RTCRtpTransceiver *)transceiver {
383 RTCMediaStreamTrack *track = transceiver.receiver.track;
384 RTCLog(@"Now receiving %@ on track %@.", track.kind, track.trackId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000385}
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 }
hjon79858f82016-03-13 22:08:26 -0700453 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200454 [_peerConnection setLocalDescription:sdp
hjon79858f82016-03-13 22:08:26 -0700455 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200456 ARDAppClient *strongSelf = weakSelf;
457 [strongSelf peerConnection:strongSelf.peerConnection
458 didSetSessionDescriptionWithError:error];
459 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000460 ARDSessionDescriptionMessage *message =
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200461 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000462 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800463 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000464 });
465}
466
467- (void)peerConnection:(RTCPeerConnection *)peerConnection
468 didSetSessionDescriptionWithError:(NSError *)error {
469 dispatch_async(dispatch_get_main_queue(), ^{
470 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700471 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000472 [self disconnect];
473 NSDictionary *userInfo = @{
474 NSLocalizedDescriptionKey: @"Failed to set session description.",
475 };
476 NSError *sdpError =
477 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
478 code:kARDAppClientErrorSetSDP
479 userInfo:userInfo];
480 [_delegate appClient:self didError:sdpError];
481 return;
482 }
483 // If we're answering and we've just set the remote offer we need to create
484 // an answer and set the local description.
485 if (!_isInitiator && !_peerConnection.localDescription) {
486 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700487 __weak ARDAppClient *weakSelf = self;
488 [_peerConnection answerForConstraints:constraints
489 completionHandler:^(RTCSessionDescription *sdp,
490 NSError *error) {
491 ARDAppClient *strongSelf = weakSelf;
492 [strongSelf peerConnection:strongSelf.peerConnection
493 didCreateSessionDescription:sdp
494 error:error];
495 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000496 }
497 });
498}
499
500#pragma mark - Private
501
tkchin204177f2016-06-14 15:03:11 -0700502#if defined(WEBRTC_IOS)
503
504- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
505 NSParameterAssert(fileName.length);
506 NSArray *paths = NSSearchPathForDirectoriesInDomains(
507 NSDocumentDirectory, NSUserDomainMask, YES);
508 NSString *documentsDirPath = paths.firstObject;
509 NSString *filePath =
510 [documentsDirPath stringByAppendingPathComponent:fileName];
511 return filePath;
512}
513
514#endif
515
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000516- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000517 return _clientId.length;
518}
519
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000520// Begins the peer connection connection process if we have both joined a room
521// on the room server and tried to obtain a TURN server. Otherwise does nothing.
522// A peer connection object will be created with a stream that contains local
523// audio and video capture. If this client is the caller, an offer is created as
524// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000525- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000526 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000527 return;
528 }
529 self.state = kARDAppClientStateConnected;
530
531 // Create peer connection.
532 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700533 RTCConfiguration *config = [[RTCConfiguration alloc] init];
534 config.iceServers = _iceServers;
Steve Antonaf23b752018-03-01 09:22:48 -0800535 config.sdpSemantics = RTCSdpSemanticsUnifiedPlan;
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700536 _peerConnection = [_factory peerConnectionWithConfiguration:config
537 constraints:constraints
538 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700539 // Create AV senders.
Alex Narestb3944f02017-10-13 14:56:18 +0200540 [self createMediaSenders];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000541 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000542 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700543 __weak ARDAppClient *weakSelf = self;
544 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
545 completionHandler:^(RTCSessionDescription *sdp,
546 NSError *error) {
547 ARDAppClient *strongSelf = weakSelf;
548 [strongSelf peerConnection:strongSelf.peerConnection
549 didCreateSessionDescription:sdp
550 error:error];
551 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000552 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000553 // Check if we've received an offer.
554 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000555 }
ivoc14d5dbe2016-07-04 07:06:55 -0700556#if defined(WEBRTC_IOS)
557 // Start event log.
558 if (kARDAppClientEnableRtcEventLog) {
559 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
560 if (![_peerConnection startRtcEventLogWithFilePath:filePath
561 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
562 RTCLogError(@"Failed to start event logging.");
563 }
564 }
peah5085b0c2016-08-25 22:15:14 -0700565
566 // Start aecdump diagnostic recording.
Anders Carlssone1500582017-06-15 16:05:13 +0200567 if ([_settings currentCreateAecDumpSettingFromStore]) {
tkchinfce0e2c2016-08-30 12:58:11 -0700568 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
569 if (![_factory startAecDumpWithFilePath:filePath
570 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
571 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700572 }
573 }
ivoc14d5dbe2016-07-04 07:06:55 -0700574#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000575}
576
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000577// Processes the messages that we've received from the room server and the
578// signaling channel. The offer or answer message must be processed before other
579// signaling messages, however they can arrive out of order. Hence, this method
580// only processes pending messages if there is a peer connection object and
581// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000582- (void)drainMessageQueueIfReady {
583 if (!_peerConnection || !_hasReceivedSdp) {
584 return;
585 }
586 for (ARDSignalingMessage *message in _messageQueue) {
587 [self processSignalingMessage:message];
588 }
589 [_messageQueue removeAllObjects];
590}
591
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000592// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000593- (void)processSignalingMessage:(ARDSignalingMessage *)message {
594 NSParameterAssert(_peerConnection ||
595 message.type == kARDSignalingMessageTypeBye);
596 switch (message.type) {
597 case kARDSignalingMessageTypeOffer:
598 case kARDSignalingMessageTypeAnswer: {
599 ARDSessionDescriptionMessage *sdpMessage =
600 (ARDSessionDescriptionMessage *)message;
601 RTCSessionDescription *description = sdpMessage.sessionDescription;
hjon79858f82016-03-13 22:08:26 -0700602 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200603 [_peerConnection setRemoteDescription:description
hjon79858f82016-03-13 22:08:26 -0700604 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200605 ARDAppClient *strongSelf = weakSelf;
606 [strongSelf peerConnection:strongSelf.peerConnection
607 didSetSessionDescriptionWithError:error];
608 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000609 break;
610 }
611 case kARDSignalingMessageTypeCandidate: {
612 ARDICECandidateMessage *candidateMessage =
613 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700614 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000615 break;
616 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700617 case kARDSignalingMessageTypeCandidateRemoval: {
618 ARDICECandidateRemovalMessage *candidateMessage =
619 (ARDICECandidateRemovalMessage *)message;
620 [_peerConnection removeIceCandidates:candidateMessage.candidates];
621 break;
622 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000623 case kARDSignalingMessageTypeBye:
624 // Other client disconnected.
625 // TODO(tkchin): support waiting in room for next client. For now just
626 // disconnect.
627 [self disconnect];
628 break;
629 }
630}
631
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000632// Sends a signaling message to the other client. The caller will send messages
633// through the room server, whereas the callee will send messages over the
634// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000635- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
636 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000637 __weak ARDAppClient *weakSelf = self;
638 [_roomServerClient sendMessage:message
639 forRoomId:_roomId
640 clientId:_clientId
641 completionHandler:^(ARDMessageResponse *response,
642 NSError *error) {
643 ARDAppClient *strongSelf = weakSelf;
644 if (error) {
645 [strongSelf.delegate appClient:strongSelf didError:error];
646 return;
647 }
648 NSError *messageError =
649 [[strongSelf class] errorForMessageResultType:response.result];
650 if (messageError) {
651 [strongSelf.delegate appClient:strongSelf didError:messageError];
652 return;
653 }
654 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000655 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000656 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000657 }
658}
659
denicija9af2b602016-11-17 00:43:43 -0800660- (void)setMaxBitrateForPeerConnectionVideoSender {
661 for (RTCRtpSender *sender in _peerConnection.senders) {
662 if (sender.track != nil) {
663 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700664 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800665 }
666 }
denicija8c375de2016-11-08 06:28:17 -0800667 }
668}
669
denicija9af2b602016-11-17 00:43:43 -0800670- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
671 if (maxBitrate.intValue <= 0) {
672 return;
673 }
674
675 RTCRtpParameters *parametersToModify = sender.parameters;
676 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
677 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
678 }
679 [sender setParameters:parametersToModify];
680}
681
Steve Antonaf23b752018-03-01 09:22:48 -0800682- (RTCRtpTransceiver *)videoTransceiver {
683 for (RTCRtpTransceiver *transceiver in _peerConnection.transceivers) {
684 if (transceiver.mediaType == RTCRtpMediaTypeVideo) {
685 return transceiver;
686 }
687 }
688 return nil;
689}
690
Alex Narestb3944f02017-10-13 14:56:18 +0200691- (void)createMediaSenders {
tkchinab1293a2016-08-30 12:35:05 -0700692 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
693 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
694 RTCAudioTrack *track = [_factory audioTrackWithSource:source
695 trackId:kARDAudioTrackId];
Seth Hampson513449e2018-03-06 09:35:56 -0800696 [_peerConnection addTrack:track streamIds:@[ kARDMediaStreamId ]];
Alex Narestb3944f02017-10-13 14:56:18 +0200697 _localVideoTrack = [self createLocalVideoTrack];
698 if (_localVideoTrack) {
Seth Hampson513449e2018-03-06 09:35:56 -0800699 [_peerConnection addTrack:_localVideoTrack streamIds:@[ kARDMediaStreamId ]];
Piasy Xue7e06022018-05-30 23:31:07 +0800700 [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack];
Steve Antonaf23b752018-03-01 09:22:48 -0800701 // We can set up rendering for the remote track right away since the transceiver already has an
702 // RTCRtpReceiver with a track. The track will automatically get unmuted and produce frames
703 // once RTP is received.
704 RTCVideoTrack *track = (RTCVideoTrack *)([self videoTransceiver].receiver.track);
705 [_delegate appClient:self didReceiveRemoteVideoTrack:track];
Alex Narestb3944f02017-10-13 14:56:18 +0200706 }
Zeke Chin57cc74e2015-05-05 07:52:31 -0700707}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000708
Zeke Chin57cc74e2015-05-05 07:52:31 -0700709- (RTCVideoTrack *)createLocalVideoTrack {
Daniela012b56b2017-11-15 13:15:24 +0100710 if ([_settings currentAudioOnlySettingFromStore]) {
711 return nil;
712 }
713
714 RTCVideoSource *source = [_factory videoSource];
715
kthelgason314bc5f2016-08-31 10:23:27 -0700716#if !TARGET_IPHONE_SIMULATOR
Anders Carlsson358f2e02018-06-04 10:24:37 +0200717 if (self.isBroadcast) {
718 ARDExternalSampleCapturer *capturer =
719 [[ARDExternalSampleCapturer alloc] initWithDelegate:source];
720 [_delegate appClient:self didCreateLocalExternalSampleCapturer:capturer];
721 } else {
722 RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
723 [_delegate appClient:self didCreateLocalCapturer:capturer];
724 }
Daniela012b56b2017-11-15 13:15:24 +0100725#else
726#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
727 if (@available(iOS 10, *)) {
728 RTCFileVideoCapturer *fileCapturer = [[RTCFileVideoCapturer alloc] initWithDelegate:source];
729 [_delegate appClient:self didCreateLocalFileCapturer:fileCapturer];
haysc913e6452015-10-02 11:44:03 -0700730 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000731#endif
Daniela012b56b2017-11-15 13:15:24 +0100732#endif
733
734 return [_factory videoTrackWithSource:source trackId:kARDVideoTrackId];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000735}
736
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000737#pragma mark - Collider methods
738
739- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000740 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000741 return;
742 }
743 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000744 if (!_channel) {
745 _channel =
746 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
747 restURL:_websocketRestURL
748 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700749 if (_isLoopback) {
750 _loopbackChannel =
751 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
752 restURL:_websocketRestURL];
753 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000754 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000755 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700756 if (_isLoopback) {
757 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
758 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000759}
760
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000761#pragma mark - Defaults
762
tkchinab1293a2016-08-30 12:35:05 -0700763 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
Sam Zackrisson9e981f02018-02-28 16:20:58 +0100764 NSDictionary *mandatoryConstraints = @{};
denicijad17d5362016-11-02 02:56:09 -0700765 RTCMediaConstraints *constraints =
766 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
767 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700768 return constraints;
769}
770
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000771- (RTCMediaConstraints *)defaultAnswerConstraints {
772 return [self defaultOfferConstraints];
773}
774
775- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700776 NSDictionary *mandatoryConstraints = @{
777 @"OfferToReceiveAudio" : @"true",
778 @"OfferToReceiveVideo" : @"true"
779 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000780 RTCMediaConstraints* constraints =
781 [[RTCMediaConstraints alloc]
782 initWithMandatoryConstraints:mandatoryConstraints
783 optionalConstraints:nil];
784 return constraints;
785}
786
787- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000788 if (_defaultPeerConnectionConstraints) {
789 return _defaultPeerConnectionConstraints;
790 }
haysc913e6452015-10-02 11:44:03 -0700791 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700792 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000793 RTCMediaConstraints* constraints =
794 [[RTCMediaConstraints alloc]
795 initWithMandatoryConstraints:nil
796 optionalConstraints:optionalConstraints];
797 return constraints;
798}
799
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000800#pragma mark - Errors
801
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000802+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000803 NSError *error = nil;
804 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000805 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000806 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000807 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000808 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
809 code:kARDAppClientErrorUnknown
810 userInfo:@{
811 NSLocalizedDescriptionKey: @"Unknown error.",
812 }];
813 break;
814 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000815 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000816 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
817 code:kARDAppClientErrorRoomFull
818 userInfo:@{
819 NSLocalizedDescriptionKey: @"Room is full.",
820 }];
821 break;
822 }
823 }
824 return error;
825}
826
827+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
828 NSError *error = nil;
829 switch (resultType) {
830 case kARDMessageResultTypeSuccess:
831 break;
832 case kARDMessageResultTypeUnknown:
833 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
834 code:kARDAppClientErrorUnknown
835 userInfo:@{
836 NSLocalizedDescriptionKey: @"Unknown error.",
837 }];
838 break;
839 case kARDMessageResultTypeInvalidClient:
840 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
841 code:kARDAppClientErrorInvalidClient
842 userInfo:@{
843 NSLocalizedDescriptionKey: @"Invalid client.",
844 }];
845 break;
846 case kARDMessageResultTypeInvalidRoom:
847 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
848 code:kARDAppClientErrorInvalidRoom
849 userInfo:@{
850 NSLocalizedDescriptionKey: @"Invalid room.",
851 }];
852 break;
853 }
854 return error;
855}
856
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000857@end