OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2014 Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 #import "ARDAppClient+Internal.h" | |
29 | |
30 #if defined(WEBRTC_IOS) | |
31 #import "RTCAVFoundationVideoSource.h" | |
32 #endif | |
33 #import "RTCFileLogger.h" | |
34 #import "RTCICEServer.h" | |
35 #import "RTCLogging.h" | |
36 #import "RTCMediaConstraints.h" | |
37 #import "RTCMediaStream.h" | |
38 #import "RTCPair.h" | |
39 #import "RTCPeerConnectionInterface.h" | |
40 #import "RTCVideoCapturer.h" | |
41 | |
42 #import "ARDAppEngineClient.h" | |
43 #import "ARDCEODTURNClient.h" | |
44 #import "ARDJoinResponse.h" | |
45 #import "ARDMessageResponse.h" | |
46 #import "ARDSDPUtils.h" | |
47 #import "ARDSignalingMessage.h" | |
48 #import "ARDUtilities.h" | |
49 #import "ARDWebSocketChannel.h" | |
50 #import "RTCICECandidate+JSON.h" | |
51 #import "RTCSessionDescription+JSON.h" | |
52 | |
53 | |
54 static NSString * const kARDDefaultSTUNServerUrl = | |
55 @"stun:stun.l.google.com:19302"; | |
56 // TODO(tkchin): figure out a better username for CEOD statistics. | |
57 static NSString * const kARDTurnRequestUrl = | |
58 @"https://computeengineondemand.appspot.com" | |
59 @"/turn?username=iapprtc&key=4080218913"; | |
60 | |
61 static NSString * const kARDAppClientErrorDomain = @"ARDAppClient"; | |
62 static NSInteger const kARDAppClientErrorUnknown = -1; | |
63 static NSInteger const kARDAppClientErrorRoomFull = -2; | |
64 static NSInteger const kARDAppClientErrorCreateSDP = -3; | |
65 static NSInteger const kARDAppClientErrorSetSDP = -4; | |
66 static NSInteger const kARDAppClientErrorInvalidClient = -5; | |
67 static NSInteger const kARDAppClientErrorInvalidRoom = -6; | |
68 | |
69 @implementation ARDAppClient { | |
70 RTCFileLogger *_fileLogger; | |
71 } | |
72 | |
73 @synthesize delegate = _delegate; | |
74 @synthesize state = _state; | |
75 @synthesize roomServerClient = _roomServerClient; | |
76 @synthesize channel = _channel; | |
77 @synthesize turnClient = _turnClient; | |
78 @synthesize peerConnection = _peerConnection; | |
79 @synthesize factory = _factory; | |
80 @synthesize messageQueue = _messageQueue; | |
81 @synthesize isTurnComplete = _isTurnComplete; | |
82 @synthesize hasReceivedSdp = _hasReceivedSdp; | |
83 @synthesize roomId = _roomId; | |
84 @synthesize clientId = _clientId; | |
85 @synthesize isInitiator = _isInitiator; | |
86 @synthesize iceServers = _iceServers; | |
87 @synthesize webSocketURL = _websocketURL; | |
88 @synthesize webSocketRestURL = _websocketRestURL; | |
89 @synthesize defaultPeerConnectionConstraints = | |
90 _defaultPeerConnectionConstraints; | |
91 | |
92 - (instancetype)init { | |
93 if (self = [super init]) { | |
94 _roomServerClient = [[ARDAppEngineClient alloc] init]; | |
95 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl]; | |
96 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL]; | |
97 [self configure]; | |
98 } | |
99 return self; | |
100 } | |
101 | |
102 - (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate { | |
103 if (self = [super init]) { | |
104 _roomServerClient = [[ARDAppEngineClient alloc] init]; | |
105 _delegate = delegate; | |
106 NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl]; | |
107 _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL]; | |
108 [self configure]; | |
109 } | |
110 return self; | |
111 } | |
112 | |
113 // TODO(tkchin): Provide signaling channel factory interface so we can recreate | |
114 // channel if we need to on network failure. Also, make this the default public | |
115 // constructor. | |
116 - (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient | |
117 signalingChannel:(id<ARDSignalingChannel>)channel | |
118 turnClient:(id<ARDTURNClient>)turnClient | |
119 delegate:(id<ARDAppClientDelegate>)delegate { | |
120 NSParameterAssert(rsClient); | |
121 NSParameterAssert(channel); | |
122 NSParameterAssert(turnClient); | |
123 if (self = [super init]) { | |
124 _roomServerClient = rsClient; | |
125 _channel = channel; | |
126 _turnClient = turnClient; | |
127 _delegate = delegate; | |
128 [self configure]; | |
129 } | |
130 return self; | |
131 } | |
132 | |
133 - (void)configure { | |
134 _factory = [[RTCPeerConnectionFactory alloc] init]; | |
135 _messageQueue = [NSMutableArray array]; | |
136 _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]]; | |
137 _fileLogger = [[RTCFileLogger alloc] init]; | |
138 [_fileLogger start]; | |
139 } | |
140 | |
141 - (void)dealloc { | |
142 [self disconnect]; | |
143 } | |
144 | |
145 - (void)setState:(ARDAppClientState)state { | |
146 if (_state == state) { | |
147 return; | |
148 } | |
149 _state = state; | |
150 [_delegate appClient:self didChangeState:_state]; | |
151 } | |
152 | |
153 - (void)connectToRoomWithId:(NSString *)roomId | |
154 options:(NSDictionary *)options { | |
155 NSParameterAssert(roomId.length); | |
156 NSParameterAssert(_state == kARDAppClientStateDisconnected); | |
157 self.state = kARDAppClientStateConnecting; | |
158 | |
159 // Request TURN. | |
160 __weak ARDAppClient *weakSelf = self; | |
161 [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers, | |
162 NSError *error) { | |
163 if (error) { | |
164 RTCLogError("Error retrieving TURN servers: %@", | |
165 error.localizedDescription); | |
166 } | |
167 ARDAppClient *strongSelf = weakSelf; | |
168 [strongSelf.iceServers addObjectsFromArray:turnServers]; | |
169 strongSelf.isTurnComplete = YES; | |
170 [strongSelf startSignalingIfReady]; | |
171 }]; | |
172 | |
173 // Join room on room server. | |
174 [_roomServerClient joinRoomWithRoomId:roomId | |
175 completionHandler:^(ARDJoinResponse *response, NSError *error) { | |
176 ARDAppClient *strongSelf = weakSelf; | |
177 if (error) { | |
178 [strongSelf.delegate appClient:strongSelf didError:error]; | |
179 return; | |
180 } | |
181 NSError *joinError = | |
182 [[strongSelf class] errorForJoinResultType:response.result]; | |
183 if (joinError) { | |
184 RTCLogError(@"Failed to join room:%@ on room server.", roomId); | |
185 [strongSelf disconnect]; | |
186 [strongSelf.delegate appClient:strongSelf didError:joinError]; | |
187 return; | |
188 } | |
189 RTCLog(@"Joined room:%@ on room server.", roomId); | |
190 strongSelf.roomId = response.roomId; | |
191 strongSelf.clientId = response.clientId; | |
192 strongSelf.isInitiator = response.isInitiator; | |
193 for (ARDSignalingMessage *message in response.messages) { | |
194 if (message.type == kARDSignalingMessageTypeOffer || | |
195 message.type == kARDSignalingMessageTypeAnswer) { | |
196 strongSelf.hasReceivedSdp = YES; | |
197 [strongSelf.messageQueue insertObject:message atIndex:0]; | |
198 } else { | |
199 [strongSelf.messageQueue addObject:message]; | |
200 } | |
201 } | |
202 strongSelf.webSocketURL = response.webSocketURL; | |
203 strongSelf.webSocketRestURL = response.webSocketRestURL; | |
204 [strongSelf registerWithColliderIfReady]; | |
205 [strongSelf startSignalingIfReady]; | |
206 }]; | |
207 } | |
208 | |
209 - (void)disconnect { | |
210 if (_state == kARDAppClientStateDisconnected) { | |
211 return; | |
212 } | |
213 if (self.hasJoinedRoomServerRoom) { | |
214 [_roomServerClient leaveRoomWithRoomId:_roomId | |
215 clientId:_clientId | |
216 completionHandler:nil]; | |
217 } | |
218 if (_channel) { | |
219 if (_channel.state == kARDSignalingChannelStateRegistered) { | |
220 // Tell the other client we're hanging up. | |
221 ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init]; | |
222 [_channel sendMessage:byeMessage]; | |
223 } | |
224 // Disconnect from collider. | |
225 _channel = nil; | |
226 } | |
227 _clientId = nil; | |
228 _roomId = nil; | |
229 _isInitiator = NO; | |
230 _hasReceivedSdp = NO; | |
231 _messageQueue = [NSMutableArray array]; | |
232 _peerConnection = nil; | |
233 self.state = kARDAppClientStateDisconnected; | |
234 } | |
235 | |
236 #pragma mark - ARDSignalingChannelDelegate | |
237 | |
238 - (void)channel:(id<ARDSignalingChannel>)channel | |
239 didReceiveMessage:(ARDSignalingMessage *)message { | |
240 switch (message.type) { | |
241 case kARDSignalingMessageTypeOffer: | |
242 case kARDSignalingMessageTypeAnswer: | |
243 // Offers and answers must be processed before any other message, so we | |
244 // place them at the front of the queue. | |
245 _hasReceivedSdp = YES; | |
246 [_messageQueue insertObject:message atIndex:0]; | |
247 break; | |
248 case kARDSignalingMessageTypeCandidate: | |
249 [_messageQueue addObject:message]; | |
250 break; | |
251 case kARDSignalingMessageTypeBye: | |
252 // Disconnects can be processed immediately. | |
253 [self processSignalingMessage:message]; | |
254 return; | |
255 } | |
256 [self drainMessageQueueIfReady]; | |
257 } | |
258 | |
259 - (void)channel:(id<ARDSignalingChannel>)channel | |
260 didChangeState:(ARDSignalingChannelState)state { | |
261 switch (state) { | |
262 case kARDSignalingChannelStateOpen: | |
263 break; | |
264 case kARDSignalingChannelStateRegistered: | |
265 break; | |
266 case kARDSignalingChannelStateClosed: | |
267 case kARDSignalingChannelStateError: | |
268 // TODO(tkchin): reconnection scenarios. Right now we just disconnect | |
269 // completely if the websocket connection fails. | |
270 [self disconnect]; | |
271 break; | |
272 } | |
273 } | |
274 | |
275 #pragma mark - RTCPeerConnectionDelegate | |
276 // Callbacks for this delegate occur on non-main thread and need to be | |
277 // dispatched back to main queue as needed. | |
278 | |
279 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
280 signalingStateChanged:(RTCSignalingState)stateChanged { | |
281 RTCLog(@"Signaling state changed: %d", stateChanged); | |
282 } | |
283 | |
284 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
285 addedStream:(RTCMediaStream *)stream { | |
286 dispatch_async(dispatch_get_main_queue(), ^{ | |
287 RTCLog(@"Received %lu video tracks and %lu audio tracks", | |
288 (unsigned long)stream.videoTracks.count, | |
289 (unsigned long)stream.audioTracks.count); | |
290 if (stream.videoTracks.count) { | |
291 RTCVideoTrack *videoTrack = stream.videoTracks[0]; | |
292 [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack]; | |
293 } | |
294 }); | |
295 } | |
296 | |
297 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
298 removedStream:(RTCMediaStream *)stream { | |
299 RTCLog(@"Stream was removed."); | |
300 } | |
301 | |
302 - (void)peerConnectionOnRenegotiationNeeded: | |
303 (RTCPeerConnection *)peerConnection { | |
304 RTCLog(@"WARNING: Renegotiation needed but unimplemented."); | |
305 } | |
306 | |
307 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
308 iceConnectionChanged:(RTCICEConnectionState)newState { | |
309 RTCLog(@"ICE state changed: %d", newState); | |
310 dispatch_async(dispatch_get_main_queue(), ^{ | |
311 [_delegate appClient:self didChangeConnectionState:newState]; | |
312 }); | |
313 } | |
314 | |
315 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
316 iceGatheringChanged:(RTCICEGatheringState)newState { | |
317 RTCLog(@"ICE gathering state changed: %d", newState); | |
318 } | |
319 | |
320 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
321 gotICECandidate:(RTCICECandidate *)candidate { | |
322 dispatch_async(dispatch_get_main_queue(), ^{ | |
323 ARDICECandidateMessage *message = | |
324 [[ARDICECandidateMessage alloc] initWithCandidate:candidate]; | |
325 [self sendSignalingMessage:message]; | |
326 }); | |
327 } | |
328 | |
329 - (void)peerConnection:(RTCPeerConnection*)peerConnection | |
330 didOpenDataChannel:(RTCDataChannel*)dataChannel { | |
331 } | |
332 | |
333 #pragma mark - RTCSessionDescriptionDelegate | |
334 // Callbacks for this delegate occur on non-main thread and need to be | |
335 // dispatched back to main queue as needed. | |
336 | |
337 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
338 didCreateSessionDescription:(RTCSessionDescription *)sdp | |
339 error:(NSError *)error { | |
340 dispatch_async(dispatch_get_main_queue(), ^{ | |
341 if (error) { | |
342 RTCLogError(@"Failed to create session description. Error: %@", error); | |
343 [self disconnect]; | |
344 NSDictionary *userInfo = @{ | |
345 NSLocalizedDescriptionKey: @"Failed to create session description.", | |
346 }; | |
347 NSError *sdpError = | |
348 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain | |
349 code:kARDAppClientErrorCreateSDP | |
350 userInfo:userInfo]; | |
351 [_delegate appClient:self didError:sdpError]; | |
352 return; | |
353 } | |
354 // Prefer H264 if available. | |
355 RTCSessionDescription *sdpPreferringH264 = | |
356 [ARDSDPUtils descriptionForDescription:sdp | |
357 preferredVideoCodec:@"H264"]; | |
358 [_peerConnection setLocalDescriptionWithDelegate:self | |
359 sessionDescription:sdpPreferringH264]; | |
360 ARDSessionDescriptionMessage *message = | |
361 [[ARDSessionDescriptionMessage alloc] | |
362 initWithDescription:sdpPreferringH264]; | |
363 [self sendSignalingMessage:message]; | |
364 }); | |
365 } | |
366 | |
367 - (void)peerConnection:(RTCPeerConnection *)peerConnection | |
368 didSetSessionDescriptionWithError:(NSError *)error { | |
369 dispatch_async(dispatch_get_main_queue(), ^{ | |
370 if (error) { | |
371 RTCLogError(@"Failed to set session description. Error: %@", error); | |
372 [self disconnect]; | |
373 NSDictionary *userInfo = @{ | |
374 NSLocalizedDescriptionKey: @"Failed to set session description.", | |
375 }; | |
376 NSError *sdpError = | |
377 [[NSError alloc] initWithDomain:kARDAppClientErrorDomain | |
378 code:kARDAppClientErrorSetSDP | |
379 userInfo:userInfo]; | |
380 [_delegate appClient:self didError:sdpError]; | |
381 return; | |
382 } | |
383 // If we're answering and we've just set the remote offer we need to create | |
384 // an answer and set the local description. | |
385 if (!_isInitiator && !_peerConnection.localDescription) { | |
386 RTCMediaConstraints *constraints = [self defaultAnswerConstraints]; | |
387 [_peerConnection createAnswerWithDelegate:self | |
388 constraints:constraints]; | |
389 | |
390 } | |
391 }); | |
392 } | |
393 | |
394 #pragma mark - Private | |
395 | |
396 - (BOOL)hasJoinedRoomServerRoom { | |
397 return _clientId.length; | |
398 } | |
399 | |
400 // Begins the peer connection connection process if we have both joined a room | |
401 // on the room server and tried to obtain a TURN server. Otherwise does nothing. | |
402 // A peer connection object will be created with a stream that contains local | |
403 // audio and video capture. If this client is the caller, an offer is created as | |
404 // well, otherwise the client will wait for an offer to arrive. | |
405 - (void)startSignalingIfReady { | |
406 if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) { | |
407 return; | |
408 } | |
409 self.state = kARDAppClientStateConnected; | |
410 | |
411 // Create peer connection. | |
412 RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints]; | |
413 RTCConfiguration *config = [[RTCConfiguration alloc] init]; | |
414 config.iceServers = _iceServers; | |
415 _peerConnection = [_factory peerConnectionWithConfiguration:config | |
416 constraints:constraints | |
417 delegate:self]; | |
418 // Create AV media stream and add it to the peer connection. | |
419 RTCMediaStream *localStream = [self createLocalMediaStream]; | |
420 [_peerConnection addStream:localStream]; | |
421 if (_isInitiator) { | |
422 // Send offer. | |
423 [_peerConnection createOfferWithDelegate:self | |
424 constraints:[self defaultOfferConstraints]]; | |
425 } else { | |
426 // Check if we've received an offer. | |
427 [self drainMessageQueueIfReady]; | |
428 } | |
429 } | |
430 | |
431 // Processes the messages that we've received from the room server and the | |
432 // signaling channel. The offer or answer message must be processed before other | |
433 // signaling messages, however they can arrive out of order. Hence, this method | |
434 // only processes pending messages if there is a peer connection object and | |
435 // if we have received either an offer or answer. | |
436 - (void)drainMessageQueueIfReady { | |
437 if (!_peerConnection || !_hasReceivedSdp) { | |
438 return; | |
439 } | |
440 for (ARDSignalingMessage *message in _messageQueue) { | |
441 [self processSignalingMessage:message]; | |
442 } | |
443 [_messageQueue removeAllObjects]; | |
444 } | |
445 | |
446 // Processes the given signaling message based on its type. | |
447 - (void)processSignalingMessage:(ARDSignalingMessage *)message { | |
448 NSParameterAssert(_peerConnection || | |
449 message.type == kARDSignalingMessageTypeBye); | |
450 switch (message.type) { | |
451 case kARDSignalingMessageTypeOffer: | |
452 case kARDSignalingMessageTypeAnswer: { | |
453 ARDSessionDescriptionMessage *sdpMessage = | |
454 (ARDSessionDescriptionMessage *)message; | |
455 RTCSessionDescription *description = sdpMessage.sessionDescription; | |
456 // Prefer H264 if available. | |
457 RTCSessionDescription *sdpPreferringH264 = | |
458 [ARDSDPUtils descriptionForDescription:description | |
459 preferredVideoCodec:@"H264"]; | |
460 [_peerConnection setRemoteDescriptionWithDelegate:self | |
461 sessionDescription:sdpPreferringH264]; | |
462 break; | |
463 } | |
464 case kARDSignalingMessageTypeCandidate: { | |
465 ARDICECandidateMessage *candidateMessage = | |
466 (ARDICECandidateMessage *)message; | |
467 [_peerConnection addICECandidate:candidateMessage.candidate]; | |
468 break; | |
469 } | |
470 case kARDSignalingMessageTypeBye: | |
471 // Other client disconnected. | |
472 // TODO(tkchin): support waiting in room for next client. For now just | |
473 // disconnect. | |
474 [self disconnect]; | |
475 break; | |
476 } | |
477 } | |
478 | |
479 // Sends a signaling message to the other client. The caller will send messages | |
480 // through the room server, whereas the callee will send messages over the | |
481 // signaling channel. | |
482 - (void)sendSignalingMessage:(ARDSignalingMessage *)message { | |
483 if (_isInitiator) { | |
484 __weak ARDAppClient *weakSelf = self; | |
485 [_roomServerClient sendMessage:message | |
486 forRoomId:_roomId | |
487 clientId:_clientId | |
488 completionHandler:^(ARDMessageResponse *response, | |
489 NSError *error) { | |
490 ARDAppClient *strongSelf = weakSelf; | |
491 if (error) { | |
492 [strongSelf.delegate appClient:strongSelf didError:error]; | |
493 return; | |
494 } | |
495 NSError *messageError = | |
496 [[strongSelf class] errorForMessageResultType:response.result]; | |
497 if (messageError) { | |
498 [strongSelf.delegate appClient:strongSelf didError:messageError]; | |
499 return; | |
500 } | |
501 }]; | |
502 } else { | |
503 [_channel sendMessage:message]; | |
504 } | |
505 } | |
506 | |
507 - (RTCMediaStream *)createLocalMediaStream { | |
508 RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"]; | |
509 RTCVideoTrack* localVideoTrack = [self createLocalVideoTrack]; | |
510 if (localVideoTrack) { | |
511 [localStream addVideoTrack:localVideoTrack]; | |
512 [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack]; | |
513 } | |
514 [localStream addAudioTrack:[_factory audioTrackWithID:@"ARDAMSa0"]]; | |
515 return localStream; | |
516 } | |
517 | |
518 - (RTCVideoTrack *)createLocalVideoTrack { | |
519 RTCVideoTrack* localVideoTrack = nil; | |
520 // The iOS simulator doesn't provide any sort of camera capture | |
521 // support or emulation (http://goo.gl/rHAnC1) so don't bother | |
522 // trying to open a local stream. | |
523 // TODO(tkchin): local video capture for OSX. See | |
524 // https://code.google.com/p/webrtc/issues/detail?id=3417. | |
525 #if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE | |
526 RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints]; | |
527 RTCAVFoundationVideoSource *source = | |
528 [[RTCAVFoundationVideoSource alloc] initWithFactory:_factory | |
529 constraints:mediaConstraints]; | |
530 localVideoTrack = | |
531 [[RTCVideoTrack alloc] initWithFactory:_factory | |
532 source:source | |
533 trackId:@"ARDAMSv0"]; | |
534 #endif | |
535 return localVideoTrack; | |
536 } | |
537 | |
538 #pragma mark - Collider methods | |
539 | |
540 - (void)registerWithColliderIfReady { | |
541 if (!self.hasJoinedRoomServerRoom) { | |
542 return; | |
543 } | |
544 // Open WebSocket connection. | |
545 if (!_channel) { | |
546 _channel = | |
547 [[ARDWebSocketChannel alloc] initWithURL:_websocketURL | |
548 restURL:_websocketRestURL | |
549 delegate:self]; | |
550 } | |
551 [_channel registerForRoomId:_roomId clientId:_clientId]; | |
552 } | |
553 | |
554 #pragma mark - Defaults | |
555 | |
556 - (RTCMediaConstraints *)defaultMediaStreamConstraints { | |
557 RTCMediaConstraints* constraints = | |
558 [[RTCMediaConstraints alloc] | |
559 initWithMandatoryConstraints:nil | |
560 optionalConstraints:nil]; | |
561 return constraints; | |
562 } | |
563 | |
564 - (RTCMediaConstraints *)defaultAnswerConstraints { | |
565 return [self defaultOfferConstraints]; | |
566 } | |
567 | |
568 - (RTCMediaConstraints *)defaultOfferConstraints { | |
569 NSArray *mandatoryConstraints = @[ | |
570 [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"], | |
571 [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"] | |
572 ]; | |
573 RTCMediaConstraints* constraints = | |
574 [[RTCMediaConstraints alloc] | |
575 initWithMandatoryConstraints:mandatoryConstraints | |
576 optionalConstraints:nil]; | |
577 return constraints; | |
578 } | |
579 | |
580 - (RTCMediaConstraints *)defaultPeerConnectionConstraints { | |
581 if (_defaultPeerConnectionConstraints) { | |
582 return _defaultPeerConnectionConstraints; | |
583 } | |
584 NSArray *optionalConstraints = @[ | |
585 [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"] | |
586 ]; | |
587 RTCMediaConstraints* constraints = | |
588 [[RTCMediaConstraints alloc] | |
589 initWithMandatoryConstraints:nil | |
590 optionalConstraints:optionalConstraints]; | |
591 return constraints; | |
592 } | |
593 | |
594 - (RTCICEServer *)defaultSTUNServer { | |
595 NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl]; | |
596 return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL | |
597 username:@"" | |
598 password:@""]; | |
599 } | |
600 | |
601 #pragma mark - Errors | |
602 | |
603 + (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType { | |
604 NSError *error = nil; | |
605 switch (resultType) { | |
606 case kARDJoinResultTypeSuccess: | |
607 break; | |
608 case kARDJoinResultTypeUnknown: { | |
609 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain | |
610 code:kARDAppClientErrorUnknown | |
611 userInfo:@{ | |
612 NSLocalizedDescriptionKey: @"Unknown error.", | |
613 }]; | |
614 break; | |
615 } | |
616 case kARDJoinResultTypeFull: { | |
617 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain | |
618 code:kARDAppClientErrorRoomFull | |
619 userInfo:@{ | |
620 NSLocalizedDescriptionKey: @"Room is full.", | |
621 }]; | |
622 break; | |
623 } | |
624 } | |
625 return error; | |
626 } | |
627 | |
628 + (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType { | |
629 NSError *error = nil; | |
630 switch (resultType) { | |
631 case kARDMessageResultTypeSuccess: | |
632 break; | |
633 case kARDMessageResultTypeUnknown: | |
634 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain | |
635 code:kARDAppClientErrorUnknown | |
636 userInfo:@{ | |
637 NSLocalizedDescriptionKey: @"Unknown error.", | |
638 }]; | |
639 break; | |
640 case kARDMessageResultTypeInvalidClient: | |
641 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain | |
642 code:kARDAppClientErrorInvalidClient | |
643 userInfo:@{ | |
644 NSLocalizedDescriptionKey: @"Invalid client.", | |
645 }]; | |
646 break; | |
647 case kARDMessageResultTypeInvalidRoom: | |
648 error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain | |
649 code:kARDAppClientErrorInvalidRoom | |
650 userInfo:@{ | |
651 NSLocalizedDescriptionKey: @"Invalid room.", | |
652 }]; | |
653 break; | |
654 } | |
655 return error; | |
656 } | |
657 | |
658 @end | |
OLD | NEW |