blob: 04e17ea07f1077776e87bc8287586f22b4db7fb7 [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
Mirko Bonadei19640aa2020-10-19 16:12:43 +020013#import "sdk/objc/api/peerconnection/RTCAudioTrack.h"
14#import "sdk/objc/api/peerconnection/RTCConfiguration.h"
15#import "sdk/objc/api/peerconnection/RTCFileLogger.h"
16#import "sdk/objc/api/peerconnection/RTCIceServer.h"
17#import "sdk/objc/api/peerconnection/RTCMediaConstraints.h"
18#import "sdk/objc/api/peerconnection/RTCMediaStream.h"
19#import "sdk/objc/api/peerconnection/RTCPeerConnectionFactory.h"
20#import "sdk/objc/api/peerconnection/RTCRtpSender.h"
21#import "sdk/objc/api/peerconnection/RTCRtpTransceiver.h"
22#import "sdk/objc/api/peerconnection/RTCTracing.h"
23#import "sdk/objc/api/peerconnection/RTCVideoSource.h"
24#import "sdk/objc/api/peerconnection/RTCVideoTrack.h"
25#import "sdk/objc/base/RTCLogging.h"
26#import "sdk/objc/components/capturer/RTCCameraVideoCapturer.h"
27#import "sdk/objc/components/capturer/RTCFileVideoCapturer.h"
28#import "sdk/objc/components/video_codec/RTCDefaultVideoDecoderFactory.h"
29#import "sdk/objc/components/video_codec/RTCDefaultVideoEncoderFactory.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
Artem Titov36de9df2021-07-26 13:21:35 +020067// using the proxy, `invalidate` must be called before it properly deallocs.
Zeke Chind3325802015-08-14 11:00:02 -070068@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 {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200108 RTC_OBJC_TYPE(RTCFileLogger) * _fileLogger;
Zeke Chind3325802015-08-14 11:00:02 -0700109 ARDTimerProxy *_statsTimer;
sakalc4adacf2017-03-28 01:22:48 -0700110 ARDSettingsModel *_settings;
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200111 RTC_OBJC_TYPE(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];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200175 _fileLogger = [[RTC_OBJC_TYPE(RTCFileLogger) alloc] init];
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700176 [_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;
Jaehyun Ko193f4bf2021-05-20 15:04:02 +0900194 [strongSelf.peerConnection statisticsWithCompletionHandler:^(
195 RTC_OBJC_TYPE(RTCStatisticsReport) * stats) {
hjon79858f82016-03-13 22:08:26 -0700196 dispatch_async(dispatch_get_main_queue(), ^{
197 ARDAppClient *strongSelf = weakSelf;
198 [strongSelf.delegate appClient:strongSelf didGetStats:stats];
199 });
200 }];
Zeke Chind3325802015-08-14 11:00:02 -0700201 }];
202 } else {
203 [_statsTimer invalidate];
204 _statsTimer = nil;
205 }
206 _shouldGetStats = shouldGetStats;
207}
208
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000209- (void)setState:(ARDAppClientState)state {
210 if (_state == state) {
211 return;
212 }
213 _state = state;
Mirko Bonadei924a2752021-04-30 09:25:47 +0000214 [_delegate appClient:self didChangeState:_state];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000215}
216
217- (void)connectToRoomWithId:(NSString *)roomId
sakalc4adacf2017-03-28 01:22:48 -0700218 settings:(ARDSettingsModel *)settings
Anders Carlssone1500582017-06-15 16:05:13 +0200219 isLoopback:(BOOL)isLoopback {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000220 NSParameterAssert(roomId.length);
221 NSParameterAssert(_state == kARDAppClientStateDisconnected);
sakalc4adacf2017-03-28 01:22:48 -0700222 _settings = settings;
haysc913e6452015-10-02 11:44:03 -0700223 _isLoopback = isLoopback;
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000224 self.state = kARDAppClientStateConnecting;
225
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200226 RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) *decoderFactory =
227 [[RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) alloc] init];
228 RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) *encoderFactory =
229 [[RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) alloc] init];
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200230 encoderFactory.preferredCodec = [settings currentVideoCodecSettingFromStore];
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200231 _factory =
232 [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] initWithEncoderFactory:encoderFactory
233 decoderFactory:decoderFactory];
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200234
tkchind1fb26d2016-02-03 01:51:18 -0800235#if defined(WEBRTC_IOS)
236 if (kARDAppClientEnableTracing) {
tkchin204177f2016-06-14 15:03:11 -0700237 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"];
tkchind1fb26d2016-02-03 01:51:18 -0800238 RTCStartInternalCapture(filePath);
239 }
240#endif
241
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000242 // Request TURN.
243 __weak ARDAppClient *weakSelf = self;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000244 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers,
245 NSError *error) {
246 if (error) {
Anders Carlsson358f2e02018-06-04 10:24:37 +0200247 RTCLogError(@"Error retrieving TURN servers: %@", error.localizedDescription);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000248 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000249 ARDAppClient *strongSelf = weakSelf;
250 [strongSelf.iceServers addObjectsFromArray:turnServers];
251 strongSelf.isTurnComplete = YES;
252 [strongSelf startSignalingIfReady];
253 }];
jiayl@webrtc.org27f53172014-12-31 00:26:20 +0000254
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000255 // Join room on room server.
256 [_roomServerClient joinRoomWithRoomId:roomId
haysc913e6452015-10-02 11:44:03 -0700257 isLoopback:isLoopback
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000258 completionHandler:^(ARDJoinResponse *response, NSError *error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000259 ARDAppClient *strongSelf = weakSelf;
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000260 if (error) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000261 [strongSelf.delegate appClient:strongSelf didError:error];
262 return;
263 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000264 NSError *joinError =
265 [[strongSelf class] errorForJoinResultType:response.result];
266 if (joinError) {
tkchinc3f46a92015-07-23 12:50:55 -0700267 RTCLogError(@"Failed to join room:%@ on room server.", roomId);
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000268 [strongSelf disconnect];
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000269 [strongSelf.delegate appClient:strongSelf didError:joinError];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000270 return;
271 }
tkchinc3f46a92015-07-23 12:50:55 -0700272 RTCLog(@"Joined room:%@ on room server.", roomId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000273 strongSelf.roomId = response.roomId;
274 strongSelf.clientId = response.clientId;
275 strongSelf.isInitiator = response.isInitiator;
276 for (ARDSignalingMessage *message in response.messages) {
277 if (message.type == kARDSignalingMessageTypeOffer ||
278 message.type == kARDSignalingMessageTypeAnswer) {
279 strongSelf.hasReceivedSdp = YES;
280 [strongSelf.messageQueue insertObject:message atIndex:0];
281 } else {
282 [strongSelf.messageQueue addObject:message];
283 }
284 }
285 strongSelf.webSocketURL = response.webSocketURL;
286 strongSelf.webSocketRestURL = response.webSocketRestURL;
287 [strongSelf registerWithColliderIfReady];
288 [strongSelf startSignalingIfReady];
289 }];
290}
291
292- (void)disconnect {
293 if (_state == kARDAppClientStateDisconnected) {
294 return;
295 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000296 if (self.hasJoinedRoomServerRoom) {
297 [_roomServerClient leaveRoomWithRoomId:_roomId
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000298 clientId:_clientId
299 completionHandler:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000300 }
301 if (_channel) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000302 if (_channel.state == kARDSignalingChannelStateRegistered) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000303 // Tell the other client we're hanging up.
304 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000305 [_channel sendMessage:byeMessage];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000306 }
307 // Disconnect from collider.
308 _channel = nil;
309 }
310 _clientId = nil;
311 _roomId = nil;
312 _isInitiator = NO;
313 _hasReceivedSdp = NO;
314 _messageQueue = [NSMutableArray array];
sakalc522e752017-04-05 12:17:48 -0700315 _localVideoTrack = nil;
ivoc14d5dbe2016-07-04 07:06:55 -0700316#if defined(WEBRTC_IOS)
tkchinfce0e2c2016-08-30 12:58:11 -0700317 [_factory stopAecDump];
ivoc14d5dbe2016-07-04 07:06:55 -0700318 [_peerConnection stopRtcEventLog];
319#endif
magjedcc8b9062017-07-24 07:32:33 -0700320 [_peerConnection close];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000321 _peerConnection = nil;
322 self.state = kARDAppClientStateDisconnected;
tkchind1fb26d2016-02-03 01:51:18 -0800323#if defined(WEBRTC_IOS)
adam.fedorbcc5d872016-11-07 14:53:28 -0800324 if (kARDAppClientEnableTracing) {
325 RTCStopInternalCapture();
326 }
tkchind1fb26d2016-02-03 01:51:18 -0800327#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000328}
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
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200370#pragma mark - RTC_OBJC_TYPE(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
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200374- (void)peerConnection:(RTC_OBJC_TYPE(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
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200379- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
380 didAddStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream {
Steve Antonaf23b752018-03-01 09:22:48 -0800381 RTCLog(@"Stream with %lu video tracks and %lu audio tracks was added.",
382 (unsigned long)stream.videoTracks.count,
383 (unsigned long)stream.audioTracks.count);
384}
385
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200386- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
387 didStartReceivingOnTransceiver:(RTC_OBJC_TYPE(RTCRtpTransceiver) *)transceiver {
388 RTC_OBJC_TYPE(RTCMediaStreamTrack) *track = transceiver.receiver.track;
Steve Antonaf23b752018-03-01 09:22:48 -0800389 RTCLog(@"Now receiving %@ on track %@.", track.kind, track.trackId);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000390}
391
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200392- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
393 didRemoveStream:(RTC_OBJC_TYPE(RTCMediaStream) *)stream {
tkchinc3f46a92015-07-23 12:50:55 -0700394 RTCLog(@"Stream was removed.");
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000395}
396
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200397- (void)peerConnectionShouldNegotiate:(RTC_OBJC_TYPE(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
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200401- (void)peerConnection:(RTC_OBJC_TYPE(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(), ^{
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800405 [self.delegate appClient:self didChangeConnectionState:newState];
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000406 });
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000407}
408
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200409- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
Jonas Olssoncfddbb72018-11-15 16:52:45 +0100410 didChangeConnectionState:(RTCPeerConnectionState)newState {
411 RTCLog(@"ICE+DTLS state changed: %ld", (long)newState);
412}
413
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200414- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
hjon79858f82016-03-13 22:08:26 -0700415 didChangeIceGatheringState:(RTCIceGatheringState)newState {
416 RTCLog(@"ICE gathering state changed: %ld", (long)newState);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000417}
418
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200419- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
420 didGenerateIceCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)candidate {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000421 dispatch_async(dispatch_get_main_queue(), ^{
422 ARDICECandidateMessage *message =
423 [[ARDICECandidateMessage alloc] initWithCandidate:candidate];
424 [self sendSignalingMessage:message];
425 });
426}
427
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200428- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
429 didRemoveIceCandidates:(NSArray<RTC_OBJC_TYPE(RTCIceCandidate) *> *)candidates {
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700430 dispatch_async(dispatch_get_main_queue(), ^{
431 ARDICECandidateRemovalMessage *message =
432 [[ARDICECandidateRemovalMessage alloc]
433 initWithRemovedCandidates:candidates];
434 [self sendSignalingMessage:message];
435 });
436}
437
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200438- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
439 didChangeLocalCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)local
440 didChangeRemoteCandidate:(RTC_OBJC_TYPE(RTCIceCandidate) *)remote
Alex Drake43faee02019-08-12 16:27:34 -0700441 lastReceivedMs:(int)lastDataReceivedMs
442 didHaveReason:(NSString *)reason {
443 RTCLog(@"ICE candidate pair changed because: %@", reason);
444}
445
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200446- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
447 didOpenDataChannel:(RTC_OBJC_TYPE(RTCDataChannel) *)dataChannel {
Zeke Chind3325802015-08-14 11:00:02 -0700448}
449
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000450#pragma mark - RTCSessionDescriptionDelegate
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000451// Callbacks for this delegate occur on non-main thread and need to be
452// dispatched back to main queue as needed.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000453
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200454- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
455 didCreateSessionDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000456 error:(NSError *)error {
457 dispatch_async(dispatch_get_main_queue(), ^{
458 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700459 RTCLogError(@"Failed to create session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000460 [self disconnect];
461 NSDictionary *userInfo = @{
462 NSLocalizedDescriptionKey: @"Failed to create session description.",
463 };
464 NSError *sdpError =
465 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
466 code:kARDAppClientErrorCreateSDP
467 userInfo:userInfo];
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800468 [self.delegate appClient:self didError:sdpError];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000469 return;
470 }
hjon79858f82016-03-13 22:08:26 -0700471 __weak ARDAppClient *weakSelf = self;
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800472 [self.peerConnection setLocalDescription:sdp
473 completionHandler:^(NSError *error) {
474 ARDAppClient *strongSelf = weakSelf;
475 [strongSelf peerConnection:strongSelf.peerConnection
476 didSetSessionDescriptionWithError:error];
477 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000478 ARDSessionDescriptionMessage *message =
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200479 [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000480 [self sendSignalingMessage:message];
denicija9af2b602016-11-17 00:43:43 -0800481 [self setMaxBitrateForPeerConnectionVideoSender];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000482 });
483}
484
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200485- (void)peerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)peerConnection
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000486 didSetSessionDescriptionWithError:(NSError *)error {
487 dispatch_async(dispatch_get_main_queue(), ^{
488 if (error) {
tkchinc3f46a92015-07-23 12:50:55 -0700489 RTCLogError(@"Failed to set session description. Error: %@", error);
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000490 [self disconnect];
491 NSDictionary *userInfo = @{
492 NSLocalizedDescriptionKey: @"Failed to set session description.",
493 };
494 NSError *sdpError =
495 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
496 code:kARDAppClientErrorSetSDP
497 userInfo:userInfo];
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800498 [self.delegate appClient:self didError:sdpError];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000499 return;
500 }
501 // If we're answering and we've just set the remote offer we need to create
502 // an answer and set the local description.
Jiawei Ou4aeb35b2018-11-09 13:55:45 -0800503 if (!self.isInitiator && !self.peerConnection.localDescription) {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200504 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = [self defaultAnswerConstraints];
hjon79858f82016-03-13 22:08:26 -0700505 __weak ARDAppClient *weakSelf = self;
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200506 [self.peerConnection
507 answerForConstraints:constraints
508 completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * sdp, NSError * error) {
509 ARDAppClient *strongSelf = weakSelf;
510 [strongSelf peerConnection:strongSelf.peerConnection
511 didCreateSessionDescription:sdp
512 error:error];
513 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000514 }
515 });
516}
517
518#pragma mark - Private
519
tkchin204177f2016-06-14 15:03:11 -0700520#if defined(WEBRTC_IOS)
521
522- (NSString *)documentsFilePathForFileName:(NSString *)fileName {
523 NSParameterAssert(fileName.length);
524 NSArray *paths = NSSearchPathForDirectoriesInDomains(
525 NSDocumentDirectory, NSUserDomainMask, YES);
526 NSString *documentsDirPath = paths.firstObject;
527 NSString *filePath =
528 [documentsDirPath stringByAppendingPathComponent:fileName];
529 return filePath;
530}
531
532#endif
533
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000534- (BOOL)hasJoinedRoomServerRoom {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000535 return _clientId.length;
536}
537
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000538// Begins the peer connection connection process if we have both joined a room
539// on the room server and tried to obtain a TURN server. Otherwise does nothing.
540// A peer connection object will be created with a stream that contains local
541// audio and video capture. If this client is the caller, an offer is created as
542// well, otherwise the client will wait for an offer to arrive.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000543- (void)startSignalingIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000544 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000545 return;
546 }
547 self.state = kARDAppClientStateConnected;
548
549 // Create peer connection.
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200550 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = [self defaultPeerConnectionConstraints];
551 RTC_OBJC_TYPE(RTCConfiguration) *config = [[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
552 RTC_OBJC_TYPE(RTCCertificate) *pcert = [RTC_OBJC_TYPE(RTCCertificate)
553 generateCertificateWithParams:@{@"expires" : @100000, @"name" : @"RSASSA-PKCS1-v1_5"}];
Zeke Chinbc7dd7e2015-05-29 14:59:14 -0700554 config.iceServers = _iceServers;
Steve Antonaf23b752018-03-01 09:22:48 -0800555 config.sdpSemantics = RTCSdpSemanticsUnifiedPlan;
Michael Iedemaccee56b2018-07-05 15:28:24 +0200556 config.certificate = pcert;
557
Tze Kwang Chinf3cb49f2016-03-22 10:57:40 -0700558 _peerConnection = [_factory peerConnectionWithConfiguration:config
559 constraints:constraints
560 delegate:self];
skvladf3569c82016-04-29 15:30:16 -0700561 // Create AV senders.
Alex Narestb3944f02017-10-13 14:56:18 +0200562 [self createMediaSenders];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000563 if (_isInitiator) {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000564 // Send offer.
hjon79858f82016-03-13 22:08:26 -0700565 __weak ARDAppClient *weakSelf = self;
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200566 [_peerConnection
567 offerForConstraints:[self defaultOfferConstraints]
568 completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * sdp, NSError * error) {
569 ARDAppClient *strongSelf = weakSelf;
570 [strongSelf peerConnection:strongSelf.peerConnection
571 didCreateSessionDescription:sdp
572 error:error];
573 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000574 } else {
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000575 // Check if we've received an offer.
576 [self drainMessageQueueIfReady];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000577 }
ivoc14d5dbe2016-07-04 07:06:55 -0700578#if defined(WEBRTC_IOS)
579 // Start event log.
580 if (kARDAppClientEnableRtcEventLog) {
581 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"];
582 if (![_peerConnection startRtcEventLogWithFilePath:filePath
583 maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) {
584 RTCLogError(@"Failed to start event logging.");
585 }
586 }
peah5085b0c2016-08-25 22:15:14 -0700587
588 // Start aecdump diagnostic recording.
Anders Carlssone1500582017-06-15 16:05:13 +0200589 if ([_settings currentCreateAecDumpSettingFromStore]) {
tkchinfce0e2c2016-08-30 12:58:11 -0700590 NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
591 if (![_factory startAecDumpWithFilePath:filePath
592 maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
593 RTCLogError(@"Failed to start aec dump.");
peah5085b0c2016-08-25 22:15:14 -0700594 }
595 }
ivoc14d5dbe2016-07-04 07:06:55 -0700596#endif
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000597}
598
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000599// Processes the messages that we've received from the room server and the
600// signaling channel. The offer or answer message must be processed before other
601// signaling messages, however they can arrive out of order. Hence, this method
602// only processes pending messages if there is a peer connection object and
603// if we have received either an offer or answer.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000604- (void)drainMessageQueueIfReady {
605 if (!_peerConnection || !_hasReceivedSdp) {
606 return;
607 }
608 for (ARDSignalingMessage *message in _messageQueue) {
609 [self processSignalingMessage:message];
610 }
611 [_messageQueue removeAllObjects];
612}
613
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000614// Processes the given signaling message based on its type.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000615- (void)processSignalingMessage:(ARDSignalingMessage *)message {
616 NSParameterAssert(_peerConnection ||
617 message.type == kARDSignalingMessageTypeBye);
618 switch (message.type) {
619 case kARDSignalingMessageTypeOffer:
620 case kARDSignalingMessageTypeAnswer: {
621 ARDSessionDescriptionMessage *sdpMessage =
622 (ARDSessionDescriptionMessage *)message;
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200623 RTC_OBJC_TYPE(RTCSessionDescription) *description = sdpMessage.sessionDescription;
hjon79858f82016-03-13 22:08:26 -0700624 __weak ARDAppClient *weakSelf = self;
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200625 [_peerConnection setRemoteDescription:description
hjon79858f82016-03-13 22:08:26 -0700626 completionHandler:^(NSError *error) {
Anders Carlsson6bf43d22017-10-16 13:51:43 +0200627 ARDAppClient *strongSelf = weakSelf;
628 [strongSelf peerConnection:strongSelf.peerConnection
629 didSetSessionDescriptionWithError:error];
630 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000631 break;
632 }
633 case kARDSignalingMessageTypeCandidate: {
634 ARDICECandidateMessage *candidateMessage =
635 (ARDICECandidateMessage *)message;
Yura Yaroshevich2d9f53c2021-03-10 13:03:00 +0300636 __weak ARDAppClient *weakSelf = self;
637 [_peerConnection addIceCandidate:candidateMessage.candidate
638 completionHandler:^(NSError *error) {
639 ARDAppClient *strongSelf = weakSelf;
640 if (error) {
641 [strongSelf.delegate appClient:strongSelf didError:error];
642 }
643 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000644 break;
645 }
Honghai Zhangda2ba4d2016-05-23 11:53:14 -0700646 case kARDSignalingMessageTypeCandidateRemoval: {
647 ARDICECandidateRemovalMessage *candidateMessage =
648 (ARDICECandidateRemovalMessage *)message;
649 [_peerConnection removeIceCandidates:candidateMessage.candidates];
650 break;
651 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000652 case kARDSignalingMessageTypeBye:
653 // Other client disconnected.
654 // TODO(tkchin): support waiting in room for next client. For now just
655 // disconnect.
656 [self disconnect];
657 break;
658 }
659}
660
tkchin@webrtc.org8cc47e92015-03-18 23:38:04 +0000661// Sends a signaling message to the other client. The caller will send messages
662// through the room server, whereas the callee will send messages over the
663// signaling channel.
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000664- (void)sendSignalingMessage:(ARDSignalingMessage *)message {
665 if (_isInitiator) {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000666 __weak ARDAppClient *weakSelf = self;
667 [_roomServerClient sendMessage:message
668 forRoomId:_roomId
669 clientId:_clientId
670 completionHandler:^(ARDMessageResponse *response,
671 NSError *error) {
672 ARDAppClient *strongSelf = weakSelf;
673 if (error) {
674 [strongSelf.delegate appClient:strongSelf didError:error];
675 return;
676 }
677 NSError *messageError =
678 [[strongSelf class] errorForMessageResultType:response.result];
679 if (messageError) {
680 [strongSelf.delegate appClient:strongSelf didError:messageError];
681 return;
682 }
683 }];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000684 } else {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000685 [_channel sendMessage:message];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000686 }
687}
688
denicija9af2b602016-11-17 00:43:43 -0800689- (void)setMaxBitrateForPeerConnectionVideoSender {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200690 for (RTC_OBJC_TYPE(RTCRtpSender) * sender in _peerConnection.senders) {
denicija9af2b602016-11-17 00:43:43 -0800691 if (sender.track != nil) {
692 if ([sender.track.kind isEqualToString:kARDVideoTrackKind]) {
sakalc4adacf2017-03-28 01:22:48 -0700693 [self setMaxBitrate:[_settings currentMaxBitrateSettingFromStore] forVideoSender:sender];
denicija9af2b602016-11-17 00:43:43 -0800694 }
695 }
denicija8c375de2016-11-08 06:28:17 -0800696 }
697}
698
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200699- (void)setMaxBitrate:(NSNumber *)maxBitrate forVideoSender:(RTC_OBJC_TYPE(RTCRtpSender) *)sender {
denicija9af2b602016-11-17 00:43:43 -0800700 if (maxBitrate.intValue <= 0) {
701 return;
702 }
703
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200704 RTC_OBJC_TYPE(RTCRtpParameters) *parametersToModify = sender.parameters;
705 for (RTC_OBJC_TYPE(RTCRtpEncodingParameters) * encoding in parametersToModify.encodings) {
denicija9af2b602016-11-17 00:43:43 -0800706 encoding.maxBitrateBps = @(maxBitrate.intValue * kKbpsMultiplier);
707 }
708 [sender setParameters:parametersToModify];
709}
710
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200711- (RTC_OBJC_TYPE(RTCRtpTransceiver) *)videoTransceiver {
712 for (RTC_OBJC_TYPE(RTCRtpTransceiver) * transceiver in _peerConnection.transceivers) {
Steve Antonaf23b752018-03-01 09:22:48 -0800713 if (transceiver.mediaType == RTCRtpMediaTypeVideo) {
714 return transceiver;
715 }
716 }
717 return nil;
718}
719
Alex Narestb3944f02017-10-13 14:56:18 +0200720- (void)createMediaSenders {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200721 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = [self defaultMediaAudioConstraints];
722 RTC_OBJC_TYPE(RTCAudioSource) *source = [_factory audioSourceWithConstraints:constraints];
723 RTC_OBJC_TYPE(RTCAudioTrack) *track = [_factory audioTrackWithSource:source
724 trackId:kARDAudioTrackId];
Seth Hampson513449e2018-03-06 09:35:56 -0800725 [_peerConnection addTrack:track streamIds:@[ kARDMediaStreamId ]];
Alex Narestb3944f02017-10-13 14:56:18 +0200726 _localVideoTrack = [self createLocalVideoTrack];
727 if (_localVideoTrack) {
Seth Hampson513449e2018-03-06 09:35:56 -0800728 [_peerConnection addTrack:_localVideoTrack streamIds:@[ kARDMediaStreamId ]];
Piasy Xue7e06022018-05-30 23:31:07 +0800729 [_delegate appClient:self didReceiveLocalVideoTrack:_localVideoTrack];
Steve Antonaf23b752018-03-01 09:22:48 -0800730 // We can set up rendering for the remote track right away since the transceiver already has an
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200731 // RTC_OBJC_TYPE(RTCRtpReceiver) with a track. The track will automatically get unmuted and
732 // produce frames once RTP is received.
733 RTC_OBJC_TYPE(RTCVideoTrack) *track =
734 (RTC_OBJC_TYPE(RTCVideoTrack) *)([self videoTransceiver].receiver.track);
Steve Antonaf23b752018-03-01 09:22:48 -0800735 [_delegate appClient:self didReceiveRemoteVideoTrack:track];
Alex Narestb3944f02017-10-13 14:56:18 +0200736 }
Zeke Chin57cc74e2015-05-05 07:52:31 -0700737}
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000738
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200739- (RTC_OBJC_TYPE(RTCVideoTrack) *)createLocalVideoTrack {
Daniela012b56b2017-11-15 13:15:24 +0100740 if ([_settings currentAudioOnlySettingFromStore]) {
741 return nil;
742 }
743
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200744 RTC_OBJC_TYPE(RTCVideoSource) *source = [_factory videoSource];
Daniela012b56b2017-11-15 13:15:24 +0100745
kthelgason314bc5f2016-08-31 10:23:27 -0700746#if !TARGET_IPHONE_SIMULATOR
Anders Carlsson358f2e02018-06-04 10:24:37 +0200747 if (self.isBroadcast) {
748 ARDExternalSampleCapturer *capturer =
749 [[ARDExternalSampleCapturer alloc] initWithDelegate:source];
750 [_delegate appClient:self didCreateLocalExternalSampleCapturer:capturer];
751 } else {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200752 RTC_OBJC_TYPE(RTCCameraVideoCapturer) *capturer =
753 [[RTC_OBJC_TYPE(RTCCameraVideoCapturer) alloc] initWithDelegate:source];
Anders Carlsson358f2e02018-06-04 10:24:37 +0200754 [_delegate appClient:self didCreateLocalCapturer:capturer];
755 }
Daniela012b56b2017-11-15 13:15:24 +0100756#else
757#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0)
758 if (@available(iOS 10, *)) {
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200759 RTC_OBJC_TYPE(RTCFileVideoCapturer) *fileCapturer =
760 [[RTC_OBJC_TYPE(RTCFileVideoCapturer) alloc] initWithDelegate:source];
Daniela012b56b2017-11-15 13:15:24 +0100761 [_delegate appClient:self didCreateLocalFileCapturer:fileCapturer];
haysc913e6452015-10-02 11:44:03 -0700762 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000763#endif
Daniela012b56b2017-11-15 13:15:24 +0100764#endif
765
766 return [_factory videoTrackWithSource:source trackId:kARDVideoTrackId];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000767}
768
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000769#pragma mark - Collider methods
770
771- (void)registerWithColliderIfReady {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000772 if (!self.hasJoinedRoomServerRoom) {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000773 return;
774 }
775 // Open WebSocket connection.
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000776 if (!_channel) {
777 _channel =
778 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL
779 restURL:_websocketRestURL
780 delegate:self];
haysc913e6452015-10-02 11:44:03 -0700781 if (_isLoopback) {
782 _loopbackChannel =
783 [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL
784 restURL:_websocketRestURL];
785 }
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000786 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000787 [_channel registerForRoomId:_roomId clientId:_clientId];
haysc913e6452015-10-02 11:44:03 -0700788 if (_isLoopback) {
789 [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"];
790 }
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000791}
792
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000793#pragma mark - Defaults
794
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200795- (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultMediaAudioConstraints {
796 NSDictionary *mandatoryConstraints = @{};
797 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
798 [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:mandatoryConstraints
799 optionalConstraints:nil];
800 return constraints;
tkchinab1293a2016-08-30 12:35:05 -0700801}
802
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200803- (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultAnswerConstraints {
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000804 return [self defaultOfferConstraints];
805}
806
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200807- (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultOfferConstraints {
hjon79858f82016-03-13 22:08:26 -0700808 NSDictionary *mandatoryConstraints = @{
809 @"OfferToReceiveAudio" : @"true",
810 @"OfferToReceiveVideo" : @"true"
811 };
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200812 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
813 [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:mandatoryConstraints
814 optionalConstraints:nil];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000815 return constraints;
816}
817
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200818- (RTC_OBJC_TYPE(RTCMediaConstraints) *)defaultPeerConnectionConstraints {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000819 if (_defaultPeerConnectionConstraints) {
820 return _defaultPeerConnectionConstraints;
821 }
haysc913e6452015-10-02 11:44:03 -0700822 NSString *value = _isLoopback ? @"false" : @"true";
hjon79858f82016-03-13 22:08:26 -0700823 NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value };
Mirko Bonadeia81e9c82020-05-04 16:14:32 +0200824 RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
825 [[RTC_OBJC_TYPE(RTCMediaConstraints) alloc] initWithMandatoryConstraints:nil
826 optionalConstraints:optionalConstraints];
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000827 return constraints;
828}
829
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000830#pragma mark - Errors
831
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000832+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000833 NSError *error = nil;
834 switch (resultType) {
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000835 case kARDJoinResultTypeSuccess:
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000836 break;
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000837 case kARDJoinResultTypeUnknown: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000838 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
839 code:kARDAppClientErrorUnknown
840 userInfo:@{
841 NSLocalizedDescriptionKey: @"Unknown error.",
842 }];
843 break;
844 }
tkchin@webrtc.org36401ab2015-01-27 21:34:39 +0000845 case kARDJoinResultTypeFull: {
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +0000846 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
847 code:kARDAppClientErrorRoomFull
848 userInfo:@{
849 NSLocalizedDescriptionKey: @"Room is full.",
850 }];
851 break;
852 }
853 }
854 return error;
855}
856
857+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType {
858 NSError *error = nil;
859 switch (resultType) {
860 case kARDMessageResultTypeSuccess:
861 break;
862 case kARDMessageResultTypeUnknown:
863 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
864 code:kARDAppClientErrorUnknown
865 userInfo:@{
866 NSLocalizedDescriptionKey: @"Unknown error.",
867 }];
868 break;
869 case kARDMessageResultTypeInvalidClient:
870 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
871 code:kARDAppClientErrorInvalidClient
872 userInfo:@{
873 NSLocalizedDescriptionKey: @"Invalid client.",
874 }];
875 break;
876 case kARDMessageResultTypeInvalidRoom:
877 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain
878 code:kARDAppClientErrorInvalidRoom
879 userInfo:@{
880 NSLocalizedDescriptionKey: @"Invalid room.",
881 }];
882 break;
883 }
884 return error;
885}
886
tkchin@webrtc.org87776a82014-12-09 19:32:35 +0000887@end