blob: ab1e0886289ad9168a399e2aa7b91b9920076f46 [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
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020013#import <WebRTC/RTCAudioTrack.h>
14#import <WebRTC/RTCCameraVideoCapturer.h>
15#import <WebRTC/RTCConfiguration.h>
16#import <WebRTC/RTCDefaultVideoDecoderFactory.h>
17#import <WebRTC/RTCDefaultVideoEncoderFactory.h>
18#import <WebRTC/RTCFileLogger.h>
19#import <WebRTC/RTCFileVideoCapturer.h>
20#import <WebRTC/RTCIceServer.h>
21#import <WebRTC/RTCLogging.h>
22#import <WebRTC/RTCMediaConstraints.h>
23#import <WebRTC/RTCMediaStream.h>
24#import <WebRTC/RTCPeerConnectionFactory.h>
25#import <WebRTC/RTCRtpSender.h>
26#import <WebRTC/RTCRtpTransceiver.h>
27#import <WebRTC/RTCTracing.h>
28#import <WebRTC/RTCVideoSource.h>
29#import <WebRTC/RTCVideoTrack.h>
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000030
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000031#import "ARDAppEngineClient.h"
Anders Carlsson358f2e02018-06-04 10:24:37 +020032#import "ARDExternalSampleCapturer.h"
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +000033#import "ARDJoinResponse.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000034#import "ARDMessageResponse.h"
sakalc4adacf2017-03-28 01:22:48 -070035#import "ARDSettingsModel.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000036#import "ARDSignalingMessage.h"
sakalc4adacf2017-03-28 01:22:48 -070037#import "ARDTURNClient+Internal.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000038#import "ARDUtilities.h"
39#import "ARDWebSocketChannel.h"
hjon79858f82016-03-13 22:08:26 -070040#import "RTCIceCandidate+JSON.h"
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000041#import "RTCSessionDescription+JSON.h"
Zeke Chin57cc74e2015-05-05 07:52:31 -070042
kthelgasoncc882af2017-01-13 05:59:46 -080043static NSString * const kARDIceServerRequestUrl = @"https://appr.tc/params";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000044
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +000045static NSString * const kARDAppClientErrorDomain = @"ARDAppClient";
46static NSInteger const kARDAppClientErrorUnknown = -1;
47static NSInteger const kARDAppClientErrorRoomFull = -2;
48static NSInteger const kARDAppClientErrorCreateSDP = -3;
49static NSInteger const kARDAppClientErrorSetSDP = -4;
50static NSInteger const kARDAppClientErrorInvalidClient = -5;
51static NSInteger const kARDAppClientErrorInvalidRoom = -6;
skvladf3569c82016-04-29 15:30:16 -070052static NSString * const kARDMediaStreamId = @"ARDAMS";
53static NSString * const kARDAudioTrackId = @"ARDAMSa0";
54static NSString * const kARDVideoTrackId = @"ARDAMSv0";
denicija9af2b602016-11-17 00:43:43 -080055static NSString * const kARDVideoTrackKind = @"video";
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000056
tkchin204177f2016-06-14 15:03:11 -070057// TODO(tkchin): Add these as UI options.
Mirko Bonadei3e603ec2018-07-10 14:21:23 +020058#if defined(WEBRTC_IOS)
tkchind1fb26d2016-02-03 01:51:18 -080059static BOOL const kARDAppClientEnableTracing = NO;
tkchin204177f2016-06-14 15:03:11 -070060static BOOL const kARDAppClientEnableRtcEventLog = YES;
tkchinfce0e2c2016-08-30 12:58:11 -070061static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB.
tkchin204177f2016-06-14 15:03:11 -070062static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB.
Mirko Bonadei3e603ec2018-07-10 14:21:23 +020063#endif
denicija9af2b602016-11-17 00:43:43 -080064static int const kKbpsMultiplier = 1000;
tkchind1fb26d2016-02-03 01:51:18 -080065
Zeke Chind3325802015-08-14 11:00:02 -070066// We need a proxy to NSTimer because it causes a strong retain cycle. When
67// using the proxy, |invalidate| must be called before it properly deallocs.
68@interface ARDTimerProxy : NSObject
69
70- (instancetype)initWithInterval:(NSTimeInterval)interval
71 repeats:(BOOL)repeats
72 timerHandler:(void (^)(void))timerHandler;
73- (void)invalidate;
74
75@end
76
77@implementation ARDTimerProxy {
78 NSTimer *_timer;
79 void (^_timerHandler)(void);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070080}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +000081
Zeke Chind3325802015-08-14 11:00:02 -070082- (instancetype)initWithInterval:(NSTimeInterval)interval
83 repeats:(BOOL)repeats
84 timerHandler:(void (^)(void))timerHandler {
85 NSParameterAssert(timerHandler);
86 if (self = [super init]) {
87 _timerHandler = timerHandler;
88 _timer = [NSTimer scheduledTimerWithTimeInterval:interval
89 target:self
90 selector:@selector(timerDidFire:)
91 userInfo:nil
92 repeats:repeats];
93 }
94 return self;
95}
96
97- (void)invalidate {
98 [_timer invalidate];
99}
100
101- (void)timerDidFire:(NSTimer *)timer {
102 _timerHandler();
103}
104
105@end
106
107@implementation ARDAppClient {
108 RTCFileLogger *_fileLogger;
109 ARDTimerProxy *_statsTimer;
sakalc4adacf2017-03-28 01:22:48 -0700110 ARDSettingsModel *_settings;
sakalc522e752017-04-05 12:17:48 -0700111 RTCVideoTrack *_localVideoTrack;
Zeke Chind3325802015-08-14 11:00:02 -0700112}
113
114@synthesize shouldGetStats = _shouldGetStats;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000115@synthesize state = _state;
Zeke Chind3325802015-08-14 11:00:02 -0700116@synthesize delegate = _delegate;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000117@synthesize roomServerClient = _roomServerClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000118@synthesize channel = _channel;
haysc913e6452015-10-02 11:44:03 -0700119@synthesize loopbackChannel = _loopbackChannel;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000120@synthesize turnClient = _turnClient;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000121@synthesize peerConnection = _peerConnection;
122@synthesize factory = _factory;
123@synthesize messageQueue = _messageQueue;
124@synthesize isTurnComplete = _isTurnComplete;
125@synthesize hasReceivedSdp = _hasReceivedSdp;
126@synthesize roomId = _roomId;
127@synthesize clientId = _clientId;
128@synthesize isInitiator = _isInitiator;
129@synthesize iceServers = _iceServers;
130@synthesize webSocketURL = _websocketURL;
131@synthesize webSocketRestURL = _websocketRestURL;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000132@synthesize defaultPeerConnectionConstraints =
133 _defaultPeerConnectionConstraints;
haysc913e6452015-10-02 11:44:03 -0700134@synthesize isLoopback = _isLoopback;
Anders Carlsson358f2e02018-06-04 10:24:37 +0200135@synthesize broadcast = _broadcast;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000136
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000137- (instancetype)init {
sakalc4adacf2017-03-28 01:22:48 -0700138 return [self initWithDelegate:nil];
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000139}
140
sakalc4adacf2017-03-28 01:22:48 -0700141- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000142 if (self = [super init]) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000143 _roomServerClient = [[ARDAppEngineClient alloc] init];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000144 _delegate = delegate;
kthelgasoncc882af2017-01-13 05:59:46 -0800145 NSURL *turnRequestURL = [NSURL URLWithString:kARDIceServerRequestUrl];
146 _turnClient = [[ARDTURNClient alloc] initWithURL:turnRequestURL];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000147 [self configure];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000148 }
149 return self;
150}
151
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000152// TODO(tkchin): Provide signaling channel factory interface so we can recreate
153// channel if we need to on network failure. Also, make this the default public
154// constructor.
155- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient
156 signalingChannel:(id<ARDSignalingChannel>)channel
157 turnClient:(id<ARDTURNClient>)turnClient
158 delegate:(id<ARDAppClientDelegate>)delegate {
159 NSParameterAssert(rsClient);
160 NSParameterAssert(channel);
161 NSParameterAssert(turnClient);
162 if (self = [super init]) {
163 _roomServerClient = rsClient;
164 _channel = channel;
165 _turnClient = turnClient;
166 _delegate = delegate;
167 [self configure];
168 }
169 return self;
170}
171
172- (void)configure {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000173 _messageQueue = [NSMutableArray array];
kthelgasoncc882af2017-01-13 05:59:46 -0800174 _iceServers = [NSMutableArray array];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700175 _fileLogger = [[RTCFileLogger alloc] init];
176 [_fileLogger start];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000177}
178
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000179- (void)dealloc {
Zeke Chind3325802015-08-14 11:00:02 -0700180 self.shouldGetStats = NO;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000181 [self disconnect];
182}
183
Zeke Chind3325802015-08-14 11:00:02 -0700184- (void)setShouldGetStats:(BOOL)shouldGetStats {
185 if (_shouldGetStats == shouldGetStats) {
186 return;
187 }
188 if (shouldGetStats) {
189 __weak ARDAppClient *weakSelf = self;
190 _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1
191 repeats:YES
192 timerHandler:^{
193 ARDAppClient *strongSelf = weakSelf;
hjon79858f82016-03-13 22:08:26 -0700194 [strongSelf.peerConnection statsForTrack:nil
195 statsOutputLevel:RTCStatsOutputLevelDebug
196 completionHandler:^(NSArray *stats) {
197 dispatch_async(dispatch_get_main_queue(), ^{
198 ARDAppClient *strongSelf = weakSelf;
199 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
200 });
201 }];
Zeke Chind3325802015-08-14 11:00:02 -0700202 }];
203 } else {
204 [_statsTimer invalidate];
205 _statsTimer = nil;
206 }
207 _shouldGetStats = shouldGetStats;
208}
209
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000210- (void)setState:(ARDAppClientState)state {
211 if (_state == state) {
212 return;
213 }
214 _state = state;
215 [_delegate appClient:self didChangeState:_state];
216}
217
218- (void)connectToRoomWithId:(NSString *)roomId
sakalc4adacf2017-03-28 01:22:48 -0700219 settings:(ARDSettingsModel *)settings
Anders Carlssone1500582017-06-15 16:05:13 +0200220 isLoopback:(BOOL)isLoopback {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000221 NSParameterAssert(roomId.length);
222 NSParameterAssert(_state == kARDAppClientStateDisconnected);
sakalc4adacf2017-03-28 01:22:48 -0700223 _settings = settings;
haysc913e6452015-10-02 11:44:03 -0700224 _isLoopback = isLoopback;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000225 self.state = kARDAppClientStateConnecting;
226
Anders Carlsson1d4c1522017-10-30 13:07:07 +0100227 RTCDefaultVideoDecoderFactory *decoderFactory = [[RTCDefaultVideoDecoderFactory alloc] init];
228 RTCDefaultVideoEncoderFactory *encoderFactory = [[RTCDefaultVideoEncoderFactory alloc] init];
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200229 encoderFactory.preferredCodec = [settings currentVideoCodecSettingFromStore];
230 _factory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory:encoderFactory
231 decoderFactory:decoderFactory];
232
tkchind1fb26d2016-02-03 01:51:18 -0800233#if defined(WEBRTC_IOS)
234 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700235 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800236 RTCStartInternalCapture(filePath);
237 }
238#endif
239
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000240 // Request TURN.
241 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000242 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
243 NSError *error) {
244 if (error) {
Anders Carlsson358f2e02018-06-04 10:24:37 +0200245 RTCLogError(@"Error retrieving TURN servers: %@", error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000246 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000247 ARDAppClient *strongSelf = weakSelf;
248 [strongSelf.iceServers addObjectsFromArray:turnServers];
249 strongSelf.isTurnComplete = YES;
250 [strongSelf startSignalingIfReady];
251 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000252
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000253 // Join room on room server.
254 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700255 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000256 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000257 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000258 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000259 [strongSelf.delegate appClient:strongSelf didError:error];
260 return;
261 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000262 NSError *joinError =
263 [[strongSelf class] errorForJoinResultType:response.result];
264 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700265 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000266 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000267 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000268 return;
269 }
tkchinc3f46a92015-07-23 12:50:55 -0700270 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000271 strongSelf.roomId = response.roomId;
272 strongSelf.clientId = response.clientId;
273 strongSelf.isInitiator = response.isInitiator;
274 for (ARDSignalingMessage *message in response.messages) {
275 if (message.type == kARDSignalingMessageTypeOffer ||
276 message.type == kARDSignalingMessageTypeAnswer) {
277 strongSelf.hasReceivedSdp = YES;
278 [strongSelf.messageQueue insertObject:message atIndex:0];
279 } else {
280 [strongSelf.messageQueue addObject:message];
281 }
282 }
283 strongSelf.webSocketURL = response.webSocketURL;
284 strongSelf.webSocketRestURL = response.webSocketRestURL;
285 [strongSelf registerWithColliderIfReady];
286 [strongSelf startSignalingIfReady];
287 }];
288}
289
290- (void)disconnect {
291 if (_state == kARDAppClientStateDisconnected) {
292 return;
293 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000294 if (self.hasJoinedRoomServerRoom) {
295 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000296 clientId:_clientId
297 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000298 }
299 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000300 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000301 // Tell the other client we're hanging up.
302 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000303 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000304 }
305 // Disconnect from collider.
306 _channel = nil;
307 }
308 _clientId = nil;
309 _roomId = nil;
310 _isInitiator = NO;
311 _hasReceivedSdp = NO;
312 _messageQueue = [NSMutableArray array];
sakalc522e752017-04-05 12:17:48 -0700313 _localVideoTrack = nil;
ivoc14d5dbe2016-07-04 07:06:55 -0700314#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700315 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700316 [_peerConnection stopRtcEventLog];
317#endif
magjedcc8b9062017-07-24 07:32:33 -0700318 [_peerConnection close];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000319 _peerConnection = nil;
320 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 {
Steve Antonaf23b752018-03-01 09:22:48 -0800379 RTCLog(@"Stream with %lu video tracks and %lu audio tracks was added.",
380 (unsigned long)stream.videoTracks.count,
381 (unsigned long)stream.audioTracks.count);
382}
383
384- (void)peerConnection:(RTCPeerConnection *)peerConnection
385 didStartReceivingOnTransceiver:(RTCRtpTransceiver *)transceiver {
386 RTCMediaStreamTrack *track = transceiver.receiver.track;
387 RTCLog(@"Now receiving %@ on track %@.", track.kind, track.trackId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000388}
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(), ^{
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800403 [self.delegate appClient:self didChangeConnectionState:newState];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000404 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000405}
406
407- (void)peerConnection:(RTCPeerConnection *)peerConnection
Jonas Olssoncfddbb72018-11-15 16:52:45 +0100408 didChangeConnectionState:(RTCPeerConnectionState)newState {
409 RTCLog(@"ICE+DTLS state changed: %ld", (long)newState);
410}
411
412- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700413 didChangeIceGatheringState:(RTCIceGatheringState)newState {
414 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000415}
416
417- (void)peerConnection:(RTCPeerConnection *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700418 didGenerateIceCandidate:(RTCIceCandidate *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000419 dispatch_async(dispatch_get_main_queue(), ^{
420 ARDICECandidateMessage *message =
421 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
422 [self sendSignalingMessage:message];
423 });
424}
425
Zeke Chind3325802015-08-14 11:00:02 -0700426- (void)peerConnection:(RTCPeerConnection *)peerConnection
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700427 didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates {
428 dispatch_async(dispatch_get_main_queue(), ^{
429 ARDICECandidateRemovalMessage *message =
430 [[ARDICECandidateRemovalMessage alloc]
431 initWithRemovedCandidates:candidates];
432 [self sendSignalingMessage:message];
433 });
434}
435
436- (void)peerConnection:(RTCPeerConnection *)peerConnection
Alex Drake43faee02019-08-12 16:27:34 -0700437 didChangeLocalCandidate:(RTCIceCandidate *)local
438 didChangeRemoteCandidate:(RTCIceCandidate *)remote
439 lastReceivedMs:(int)lastDataReceivedMs
440 didHaveReason:(NSString *)reason {
441 RTCLog(@"ICE candidate pair changed because: %@", reason);
442}
443
444- (void)peerConnection:(RTCPeerConnection *)peerConnection
Zeke Chind3325802015-08-14 11:00:02 -0700445 didOpenDataChannel:(RTCDataChannel *)dataChannel {
446}
447
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000448#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000449// Callbacks for this delegate occur on non-main thread and need to be
450// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000451
452- (void)peerConnection:(RTCPeerConnection *)peerConnection
453 didCreateSessionDescription:(RTCSessionDescription *)sdp
454 error:(NSError *)error {
455 dispatch_async(dispatch_get_main_queue(), ^{
456 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700457 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000458 [self disconnect];
459 NSDictionary *userInfo = @{
460 NSLocalizedDescriptionKey: @"Failed to create session description.",
461 };
462 NSError *sdpError =
463 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
464 code:kARDAppClientErrorCreateSDP
465 userInfo:userInfo];
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800466 [self.delegate appClient:self didError:sdpError];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000467 return;
468 }
hjon79858f82016-03-13 22:08:26 -0700469 __weak ARDAppClient *weakSelf = self;
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800470 [self.peerConnection setLocalDescription:sdp
471 completionHandler:^(NSError *error) {
472 ARDAppClient *strongSelf = weakSelf;
473 [strongSelf peerConnection:strongSelf.peerConnection
474 didSetSessionDescriptionWithError:error];
475 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000476 ARDSessionDescriptionMessage *message =
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200477 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000478 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800479 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000480 });
481}
482
483- (void)peerConnection:(RTCPeerConnection *)peerConnection
484 didSetSessionDescriptionWithError:(NSError *)error {
485 dispatch_async(dispatch_get_main_queue(), ^{
486 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700487 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000488 [self disconnect];
489 NSDictionary *userInfo = @{
490 NSLocalizedDescriptionKey: @"Failed to set session description.",
491 };
492 NSError *sdpError =
493 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
494 code:kARDAppClientErrorSetSDP
495 userInfo:userInfo];
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800496 [self.delegate appClient:self didError:sdpError];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000497 return;
498 }
499 // If we're answering and we've just set the remote offer we need to create
500 // an answer and set the local description.
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800501 if (!self.isInitiator && !self.peerConnection.localDescription) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000502 RTCMediaConstraints *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700503 __weak ARDAppClient *weakSelf = self;
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800504 [self.peerConnection answerForConstraints:constraints
505 completionHandler:^(RTCSessionDescription *sdp, NSError *error) {
506 ARDAppClient *strongSelf = weakSelf;
507 [strongSelf peerConnection:strongSelf.peerConnection
508 didCreateSessionDescription:sdp
509 error:error];
510 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000511 }
512 });
513}
514
515#pragma mark - Private
516
tkchin204177f2016-06-14 15:03:11 -0700517#if defined(WEBRTC_IOS)
518
519- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
520 NSParameterAssert(fileName.length);
521 NSArray *paths = NSSearchPathForDirectoriesInDomains(
522 NSDocumentDirectory, NSUserDomainMask, YES);
523 NSString *documentsDirPath = paths.firstObject;
524 NSString *filePath =
525 [documentsDirPath stringByAppendingPathComponent:fileName];
526 return filePath;
527}
528
529#endif
530
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000531- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000532 return _clientId.length;
533}
534
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000535// Begins the peer connection connection process if we have both joined a room
536// on the room server and tried to obtain a TURN server. Otherwise does nothing.
537// A peer connection object will be created with a stream that contains local
538// audio and video capture. If this client is the caller, an offer is created as
539// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000540- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000541 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000542 return;
543 }
544 self.state = kARDAppClientStateConnected;
545
546 // Create peer connection.
547 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700548 RTCConfiguration *config = [[RTCConfiguration alloc] init];
Michael Iedemaccee56b2018-07-05 15:28:24 +0200549 RTCCertificate *pcert = [RTCCertificate generateCertificateWithParams:@{
550 @"expires" : @100000,
551 @"name" : @"RSASSA-PKCS1-v1_5"
552 }];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700553 config.iceServers = _iceServers;
Steve Antonaf23b752018-03-01 09:22:48 -0800554 config.sdpSemantics = RTCSdpSemanticsUnifiedPlan;
Michael Iedemaccee56b2018-07-05 15:28:24 +0200555 config.certificate = pcert;
556
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700557 _peerConnection = [_factory peerConnectionWithConfiguration:config
558 constraints:constraints
559 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700560 // Create AV senders.
Alex Narestb3944f02017-10-13 14:56:18 +0200561 [self createMediaSenders];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000562 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000563 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700564 __weak ARDAppClient *weakSelf = self;
565 [_peerConnection offerForConstraints:[self defaultOfferConstraints]
566 completionHandler:^(RTCSessionDescription *sdp,
567 NSError *error) {
568 ARDAppClient *strongSelf = weakSelf;
569 [strongSelf peerConnection:strongSelf.peerConnection
570 didCreateSessionDescription:sdp
571 error:error];
572 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000573 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000574 // Check if we've received an offer.
575 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000576 }
ivoc14d5dbe2016-07-04 07:06:55 -0700577#if defined(WEBRTC_IOS)
578 // Start event log.
579 if (kARDAppClientEnableRtcEventLog) {
580 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
581 if (![_peerConnection startRtcEventLogWithFilePath:filePath
582 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
583 RTCLogError(@"Failed to start event logging.");
584 }
585 }
peah5085b0c2016-08-25 22:15:14 -0700586
587 // Start aecdump diagnostic recording.
Anders Carlssone1500582017-06-15 16:05:13 +0200588 if ([_settings currentCreateAecDumpSettingFromStore]) {
tkchinfce0e2c2016-08-30 12:58:11 -0700589 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
590 if (![_factory startAecDumpWithFilePath:filePath
591 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
592 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700593 }
594 }
ivoc14d5dbe2016-07-04 07:06:55 -0700595#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000596}
597
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000598// Processes the messages that we've received from the room server and the
599// signaling channel. The offer or answer message must be processed before other
600// signaling messages, however they can arrive out of order. Hence, this method
601// only processes pending messages if there is a peer connection object and
602// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000603- (void)drainMessageQueueIfReady {
604 if (!_peerConnection || !_hasReceivedSdp) {
605 return;
606 }
607 for (ARDSignalingMessage *message in _messageQueue) {
608 [self processSignalingMessage:message];
609 }
610 [_messageQueue removeAllObjects];
611}
612
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000613// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000614- (void)processSignalingMessage:(ARDSignalingMessage *)message {
615 NSParameterAssert(_peerConnection ||
616 message.type == kARDSignalingMessageTypeBye);
617 switch (message.type) {
618 case kARDSignalingMessageTypeOffer:
619 case kARDSignalingMessageTypeAnswer: {
620 ARDSessionDescriptionMessage *sdpMessage =
621 (ARDSessionDescriptionMessage *)message;
622 RTCSessionDescription *description = sdpMessage.sessionDescription;
hjon79858f82016-03-13 22:08:26 -0700623 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200624 [_peerConnection setRemoteDescription:description
hjon79858f82016-03-13 22:08:26 -0700625 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200626 ARDAppClient *strongSelf = weakSelf;
627 [strongSelf peerConnection:strongSelf.peerConnection
628 didSetSessionDescriptionWithError:error];
629 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000630 break;
631 }
632 case kARDSignalingMessageTypeCandidate: {
633 ARDICECandidateMessage *candidateMessage =
634 (ARDICECandidateMessage *)message;
hjon79858f82016-03-13 22:08:26 -0700635 [_peerConnection addIceCandidate:candidateMessage.candidate];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000636 break;
637 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700638 case kARDSignalingMessageTypeCandidateRemoval: {
639 ARDICECandidateRemovalMessage *candidateMessage =
640 (ARDICECandidateRemovalMessage *)message;
641 [_peerConnection removeIceCandidates:candidateMessage.candidates];
642 break;
643 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000644 case kARDSignalingMessageTypeBye:
645 // Other client disconnected.
646 // TODO(tkchin): support waiting in room for next client. For now just
647 // disconnect.
648 [self disconnect];
649 break;
650 }
651}
652
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000653// Sends a signaling message to the other client. The caller will send messages
654// through the room server, whereas the callee will send messages over the
655// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000656- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
657 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000658 __weak ARDAppClient *weakSelf = self;
659 [_roomServerClient sendMessage:message
660 forRoomId:_roomId
661 clientId:_clientId
662 completionHandler:^(ARDMessageResponse *response,
663 NSError *error) {
664 ARDAppClient *strongSelf = weakSelf;
665 if (error) {
666 [strongSelf.delegate appClient:strongSelf didError:error];
667 return;
668 }
669 NSError *messageError =
670 [[strongSelf class] errorForMessageResultType:response.result];
671 if (messageError) {
672 [strongSelf.delegate appClient:strongSelf didError:messageError];
673 return;
674 }
675 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000676 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000677 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000678 }
679}
680
denicija9af2b602016-11-17 00:43:43 -0800681- (void)setMaxBitrateForPeerConnectionVideoSender {
682 for (RTCRtpSender *sender in _peerConnection.senders) {
683 if (sender.track != nil) {
684 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700685 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800686 }
687 }
denicija8c375de2016-11-08 06:28:17 -0800688 }
689}
690
denicija9af2b602016-11-17 00:43:43 -0800691- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTCRtpSender *)sender {
692 if (maxBitrate.intValue <= 0) {
693 return;
694 }
695
696 RTCRtpParameters *parametersToModify = sender.parameters;
697 for (RTCRtpEncodingParameters *encoding in parametersToModify.encodings) {
698 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
699 }
700 [sender setParameters:parametersToModify];
701}
702
Steve Antonaf23b752018-03-01 09:22:48 -0800703- (RTCRtpTransceiver *)videoTransceiver {
704 for (RTCRtpTransceiver *transceiver in _peerConnection.transceivers) {
705 if (transceiver.mediaType == RTCRtpMediaTypeVideo) {
706 return transceiver;
707 }
708 }
709 return nil;
710}
711
Alex Narestb3944f02017-10-13 14:56:18 +0200712- (void)createMediaSenders {
tkchinab1293a2016-08-30 12:35:05 -0700713 RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints];
714 RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints];
715 RTCAudioTrack *track = [_factory audioTrackWithSource:source
716 trackId:kARDAudioTrackId];
Seth Hampson513449e2018-03-06 09:35:56 -0800717 [_peerConnection addTrack:track streamIds:@[ kARDMediaStreamId ]];
Alex Narestb3944f02017-10-13 14:56:18 +0200718 _localVideoTrack = [self createLocalVideoTrack];
719 if (_localVideoTrack) {
Seth Hampson513449e2018-03-06 09:35:56 -0800720 [_peerConnection addTrack:_localVideoTrack streamIds:@[ kARDMediaStreamId ]];
Piasy Xue7e06022018-05-30 23:31:07 +0800721 [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack];
Steve Antonaf23b752018-03-01 09:22:48 -0800722 // We can set up rendering for the remote track right away since the transceiver already has an
723 // RTCRtpReceiver with a track. The track will automatically get unmuted and produce frames
724 // once RTP is received.
725 RTCVideoTrack *track = (RTCVideoTrack *)([self videoTransceiver].receiver.track);
726 [_delegate appClient:self didReceiveRemoteVideoTrack:track];
Alex Narestb3944f02017-10-13 14:56:18 +0200727 }
Zeke Chin57cc74e2015-05-05 07:52:31 -0700728}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000729
Zeke Chin57cc74e2015-05-05 07:52:31 -0700730- (RTCVideoTrack *)createLocalVideoTrack {
Daniela012b56b2017-11-15 13:15:24 +0100731 if ([_settings currentAudioOnlySettingFromStore]) {
732 return nil;
733 }
734
735 RTCVideoSource *source = [_factory videoSource];
736
kthelgason314bc5f2016-08-31 10:23:27 -0700737#if !TARGET_IPHONE_SIMULATOR
Anders Carlsson358f2e02018-06-04 10:24:37 +0200738 if (self.isBroadcast) {
739 ARDExternalSampleCapturer *capturer =
740 [[ARDExternalSampleCapturer alloc] initWithDelegate:source];
741 [_delegate appClient:self didCreateLocalExternalSampleCapturer:capturer];
742 } else {
743 RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
744 [_delegate appClient:self didCreateLocalCapturer:capturer];
745 }
Daniela012b56b2017-11-15 13:15:24 +0100746#else
747#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
748 if (@available(iOS 10, *)) {
749 RTCFileVideoCapturer *fileCapturer = [[RTCFileVideoCapturer alloc] initWithDelegate:source];
750 [_delegate appClient:self didCreateLocalFileCapturer:fileCapturer];
haysc913e6452015-10-02 11:44:03 -0700751 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000752#endif
Daniela012b56b2017-11-15 13:15:24 +0100753#endif
754
755 return [_factory videoTrackWithSource:source trackId:kARDVideoTrackId];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000756}
757
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000758#pragma mark - Collider methods
759
760- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000761 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000762 return;
763 }
764 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000765 if (!_channel) {
766 _channel =
767 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
768 restURL:_websocketRestURL
769 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700770 if (_isLoopback) {
771 _loopbackChannel =
772 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
773 restURL:_websocketRestURL];
774 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000775 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000776 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700777 if (_isLoopback) {
778 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
779 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000780}
781
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000782#pragma mark - Defaults
783
tkchinab1293a2016-08-30 12:35:05 -0700784 - (RTCMediaConstraints *)defaultMediaAudioConstraints {
Sam Zackrisson9e981f02018-02-28 16:20:58 +0100785 NSDictionary *mandatoryConstraints = @{};
denicijad17d5362016-11-02 02:56:09 -0700786 RTCMediaConstraints *constraints =
787 [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
788 optionalConstraints:nil];
tkchinab1293a2016-08-30 12:35:05 -0700789 return constraints;
790}
791
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000792- (RTCMediaConstraints *)defaultAnswerConstraints {
793 return [self defaultOfferConstraints];
794}
795
796- (RTCMediaConstraints *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700797 NSDictionary *mandatoryConstraints = @{
798 @"OfferToReceiveAudio" : @"true",
799 @"OfferToReceiveVideo" : @"true"
800 };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000801 RTCMediaConstraints* constraints =
802 [[RTCMediaConstraints alloc]
803 initWithMandatoryConstraints:mandatoryConstraints
804 optionalConstraints:nil];
805 return constraints;
806}
807
808- (RTCMediaConstraints *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000809 if (_defaultPeerConnectionConstraints) {
810 return _defaultPeerConnectionConstraints;
811 }
haysc913e6452015-10-02 11:44:03 -0700812 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700813 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000814 RTCMediaConstraints* constraints =
815 [[RTCMediaConstraints alloc]
816 initWithMandatoryConstraints:nil
817 optionalConstraints:optionalConstraints];
818 return constraints;
819}
820
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000821#pragma mark - Errors
822
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000823+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000824 NSError *error = nil;
825 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000826 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000827 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000828 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000829 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
830 code:kARDAppClientErrorUnknown
831 userInfo:@{
832 NSLocalizedDescriptionKey: @"Unknown error.",
833 }];
834 break;
835 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000836 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000837 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
838 code:kARDAppClientErrorRoomFull
839 userInfo:@{
840 NSLocalizedDescriptionKey: @"Room is full.",
841 }];
842 break;
843 }
844 }
845 return error;
846}
847
848+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
849 NSError *error = nil;
850 switch (resultType) {
851 case kARDMessageResultTypeSuccess:
852 break;
853 case kARDMessageResultTypeUnknown:
854 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
855 code:kARDAppClientErrorUnknown
856 userInfo:@{
857 NSLocalizedDescriptionKey: @"Unknown error.",
858 }];
859 break;
860 case kARDMessageResultTypeInvalidClient:
861 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
862 code:kARDAppClientErrorInvalidClient
863 userInfo:@{
864 NSLocalizedDescriptionKey: @"Invalid client.",
865 }];
866 break;
867 case kARDMessageResultTypeInvalidRoom:
868 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
869 code:kARDAppClientErrorInvalidRoom
870 userInfo:@{
871 NSLocalizedDescriptionKey: @"Invalid room.",
872 }];
873 break;
874 }
875 return error;
876}
877
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000878@end