Index: webrtc/examples/objc/AppRTCDemo/ARDAppClient.m |
diff --git a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m b/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m |
deleted file mode 100644 |
index 1020621cc1bd2b91340963ac20609688aaacd8f6..0000000000000000000000000000000000000000 |
--- a/webrtc/examples/objc/AppRTCDemo/ARDAppClient.m |
+++ /dev/null |
@@ -1,846 +0,0 @@ |
-/* |
- * Copyright 2014 The WebRTC Project Authors. All rights reserved. |
- * |
- * Use of this source code is governed by a BSD-style license |
- * that can be found in the LICENSE file in the root of the source |
- * tree. An additional intellectual property rights grant can be found |
- * in the file PATENTS. All contributing project authors may |
- * be found in the AUTHORS file in the root of the source tree. |
- */ |
- |
-#import "ARDAppClient+Internal.h" |
- |
-#import "WebRTC/RTCAVFoundationVideoSource.h" |
-#import "WebRTC/RTCAudioTrack.h" |
-#import "WebRTC/RTCConfiguration.h" |
-#import "WebRTC/RTCFileLogger.h" |
-#import "WebRTC/RTCIceServer.h" |
-#import "WebRTC/RTCLogging.h" |
-#import "WebRTC/RTCMediaConstraints.h" |
-#import "WebRTC/RTCMediaStream.h" |
-#import "WebRTC/RTCPeerConnectionFactory.h" |
-#import "WebRTC/RTCRtpSender.h" |
-#import "WebRTC/RTCTracing.h" |
- |
-#import "ARDAppEngineClient.h" |
-#import "ARDCEODTURNClient.h" |
-#import "ARDJoinResponse.h" |
-#import "ARDMessageResponse.h" |
-#import "ARDSDPUtils.h" |
-#import "ARDSignalingMessage.h" |
-#import "ARDUtilities.h" |
-#import "ARDWebSocketChannel.h" |
-#import "RTCIceCandidate+JSON.h" |
-#import "RTCSessionDescription+JSON.h" |
- |
-static NSString * const kARDDefaultSTUNServerUrl = |
- @"stun:stun.l.google.com:19302"; |
-// TODO(tkchin): figure out a better username for CEOD statistics. |
-static NSString * const kARDTurnRequestUrl = |
- @"https://computeengineondemand.appspot.com" |
- @"/turn?username=iapprtc&key=4080218913"; |
- |
-static NSString * const kARDAppClientErrorDomain = @"ARDAppClient"; |
-static NSInteger const kARDAppClientErrorUnknown = -1; |
-static NSInteger const kARDAppClientErrorRoomFull = -2; |
-static NSInteger const kARDAppClientErrorCreateSDP = -3; |
-static NSInteger const kARDAppClientErrorSetSDP = -4; |
-static NSInteger const kARDAppClientErrorInvalidClient = -5; |
-static NSInteger const kARDAppClientErrorInvalidRoom = -6; |
-static NSString * const kARDMediaStreamId = @"ARDAMS"; |
-static NSString * const kARDAudioTrackId = @"ARDAMSa0"; |
-static NSString * const kARDVideoTrackId = @"ARDAMSv0"; |
- |
-// TODO(tkchin): Add these as UI options. |
-static BOOL const kARDAppClientEnableTracing = NO; |
-static BOOL const kARDAppClientEnableRtcEventLog = YES; |
-static int64_t const kARDAppClientAecDumpMaxSizeInBytes = 5e6; // 5 MB. |
-static int64_t const kARDAppClientRtcEventLogMaxSizeInBytes = 5e6; // 5 MB. |
- |
-// We need a proxy to NSTimer because it causes a strong retain cycle. When |
-// using the proxy, |invalidate| must be called before it properly deallocs. |
-@interface ARDTimerProxy : NSObject |
- |
-- (instancetype)initWithInterval:(NSTimeInterval)interval |
- repeats:(BOOL)repeats |
- timerHandler:(void (^)(void))timerHandler; |
-- (void)invalidate; |
- |
-@end |
- |
-@implementation ARDTimerProxy { |
- NSTimer *_timer; |
- void (^_timerHandler)(void); |
-} |
- |
-- (instancetype)initWithInterval:(NSTimeInterval)interval |
- repeats:(BOOL)repeats |
- timerHandler:(void (^)(void))timerHandler { |
- NSParameterAssert(timerHandler); |
- if (self = [super init]) { |
- _timerHandler = timerHandler; |
- _timer = [NSTimer scheduledTimerWithTimeInterval:interval |
- target:self |
- selector:@selector(timerDidFire:) |
- userInfo:nil |
- repeats:repeats]; |
- } |
- return self; |
-} |
- |
-- (void)invalidate { |
- [_timer invalidate]; |
-} |
- |
-- (void)timerDidFire:(NSTimer *)timer { |
- _timerHandler(); |
-} |
- |
-@end |
- |
-@implementation ARDAppClient { |
- RTCFileLogger *_fileLogger; |
- ARDTimerProxy *_statsTimer; |
-} |
- |
-@synthesize shouldGetStats = _shouldGetStats; |
-@synthesize state = _state; |
-@synthesize delegate = _delegate; |
-@synthesize roomServerClient = _roomServerClient; |
-@synthesize channel = _channel; |
-@synthesize loopbackChannel = _loopbackChannel; |
-@synthesize turnClient = _turnClient; |
-@synthesize peerConnection = _peerConnection; |
-@synthesize factory = _factory; |
-@synthesize messageQueue = _messageQueue; |
-@synthesize isTurnComplete = _isTurnComplete; |
-@synthesize hasReceivedSdp = _hasReceivedSdp; |
-@synthesize roomId = _roomId; |
-@synthesize clientId = _clientId; |
-@synthesize isInitiator = _isInitiator; |
-@synthesize iceServers = _iceServers; |
-@synthesize webSocketURL = _websocketURL; |
-@synthesize webSocketRestURL = _websocketRestURL; |
-@synthesize defaultPeerConnectionConstraints = |
- _defaultPeerConnectionConstraints; |
-@synthesize isLoopback = _isLoopback; |
-@synthesize isAudioOnly = _isAudioOnly; |
-@synthesize shouldMakeAecDump = _shouldMakeAecDump; |
-@synthesize shouldUseLevelControl = _shouldUseLevelControl; |
- |
-- (instancetype)init { |
- if (self = [super init]) { |
- _roomServerClient = [[ARDAppEngineClient alloc] init]; |
- NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl]; |
- _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL]; |
- [self configure]; |
- } |
- return self; |
-} |
- |
-- (instancetype)initWithDelegate:(id<ARDAppClientDelegate>)delegate { |
- if (self = [super init]) { |
- _roomServerClient = [[ARDAppEngineClient alloc] init]; |
- _delegate = delegate; |
- NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl]; |
- _turnClient = [[ARDCEODTURNClient alloc] initWithURL:turnRequestURL]; |
- [self configure]; |
- } |
- return self; |
-} |
- |
-// TODO(tkchin): Provide signaling channel factory interface so we can recreate |
-// channel if we need to on network failure. Also, make this the default public |
-// constructor. |
-- (instancetype)initWithRoomServerClient:(id<ARDRoomServerClient>)rsClient |
- signalingChannel:(id<ARDSignalingChannel>)channel |
- turnClient:(id<ARDTURNClient>)turnClient |
- delegate:(id<ARDAppClientDelegate>)delegate { |
- NSParameterAssert(rsClient); |
- NSParameterAssert(channel); |
- NSParameterAssert(turnClient); |
- if (self = [super init]) { |
- _roomServerClient = rsClient; |
- _channel = channel; |
- _turnClient = turnClient; |
- _delegate = delegate; |
- [self configure]; |
- } |
- return self; |
-} |
- |
-- (void)configure { |
- _factory = [[RTCPeerConnectionFactory alloc] init]; |
- _messageQueue = [NSMutableArray array]; |
- _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]]; |
- _fileLogger = [[RTCFileLogger alloc] init]; |
- [_fileLogger start]; |
-} |
- |
-- (void)dealloc { |
- self.shouldGetStats = NO; |
- [self disconnect]; |
-} |
- |
-- (void)setShouldGetStats:(BOOL)shouldGetStats { |
- if (_shouldGetStats == shouldGetStats) { |
- return; |
- } |
- if (shouldGetStats) { |
- __weak ARDAppClient *weakSelf = self; |
- _statsTimer = [[ARDTimerProxy alloc] initWithInterval:1 |
- repeats:YES |
- timerHandler:^{ |
- ARDAppClient *strongSelf = weakSelf; |
- [strongSelf.peerConnection statsForTrack:nil |
- statsOutputLevel:RTCStatsOutputLevelDebug |
- completionHandler:^(NSArray *stats) { |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- ARDAppClient *strongSelf = weakSelf; |
- [strongSelf.delegate appClient:strongSelf didGetStats:stats]; |
- }); |
- }]; |
- }]; |
- } else { |
- [_statsTimer invalidate]; |
- _statsTimer = nil; |
- } |
- _shouldGetStats = shouldGetStats; |
-} |
- |
-- (void)setState:(ARDAppClientState)state { |
- if (_state == state) { |
- return; |
- } |
- _state = state; |
- [_delegate appClient:self didChangeState:_state]; |
-} |
- |
-- (void)connectToRoomWithId:(NSString *)roomId |
- isLoopback:(BOOL)isLoopback |
- isAudioOnly:(BOOL)isAudioOnly |
- shouldMakeAecDump:(BOOL)shouldMakeAecDump |
- shouldUseLevelControl:(BOOL)shouldUseLevelControl { |
- NSParameterAssert(roomId.length); |
- NSParameterAssert(_state == kARDAppClientStateDisconnected); |
- _isLoopback = isLoopback; |
- _isAudioOnly = isAudioOnly; |
- _shouldMakeAecDump = shouldMakeAecDump; |
- _shouldUseLevelControl = shouldUseLevelControl; |
- self.state = kARDAppClientStateConnecting; |
- |
-#if defined(WEBRTC_IOS) |
- if (kARDAppClientEnableTracing) { |
- NSString *filePath = [self documentsFilePathForFileName:@"webrtc-trace.txt"]; |
- RTCStartInternalCapture(filePath); |
- } |
-#endif |
- |
- // Request TURN. |
- __weak ARDAppClient *weakSelf = self; |
- [_turnClient requestServersWithCompletionHandler:^(NSArray *turnServers, |
- NSError *error) { |
- if (error) { |
- RTCLogError("Error retrieving TURN servers: %@", |
- error.localizedDescription); |
- } |
- ARDAppClient *strongSelf = weakSelf; |
- [strongSelf.iceServers addObjectsFromArray:turnServers]; |
- strongSelf.isTurnComplete = YES; |
- [strongSelf startSignalingIfReady]; |
- }]; |
- |
- // Join room on room server. |
- [_roomServerClient joinRoomWithRoomId:roomId |
- isLoopback:isLoopback |
- completionHandler:^(ARDJoinResponse *response, NSError *error) { |
- ARDAppClient *strongSelf = weakSelf; |
- if (error) { |
- [strongSelf.delegate appClient:strongSelf didError:error]; |
- return; |
- } |
- NSError *joinError = |
- [[strongSelf class] errorForJoinResultType:response.result]; |
- if (joinError) { |
- RTCLogError(@"Failed to join room:%@ on room server.", roomId); |
- [strongSelf disconnect]; |
- [strongSelf.delegate appClient:strongSelf didError:joinError]; |
- return; |
- } |
- RTCLog(@"Joined room:%@ on room server.", roomId); |
- strongSelf.roomId = response.roomId; |
- strongSelf.clientId = response.clientId; |
- strongSelf.isInitiator = response.isInitiator; |
- for (ARDSignalingMessage *message in response.messages) { |
- if (message.type == kARDSignalingMessageTypeOffer || |
- message.type == kARDSignalingMessageTypeAnswer) { |
- strongSelf.hasReceivedSdp = YES; |
- [strongSelf.messageQueue insertObject:message atIndex:0]; |
- } else { |
- [strongSelf.messageQueue addObject:message]; |
- } |
- } |
- strongSelf.webSocketURL = response.webSocketURL; |
- strongSelf.webSocketRestURL = response.webSocketRestURL; |
- [strongSelf registerWithColliderIfReady]; |
- [strongSelf startSignalingIfReady]; |
- }]; |
-} |
- |
-- (void)disconnect { |
- if (_state == kARDAppClientStateDisconnected) { |
- return; |
- } |
- if (self.hasJoinedRoomServerRoom) { |
- [_roomServerClient leaveRoomWithRoomId:_roomId |
- clientId:_clientId |
- completionHandler:nil]; |
- } |
- if (_channel) { |
- if (_channel.state == kARDSignalingChannelStateRegistered) { |
- // Tell the other client we're hanging up. |
- ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init]; |
- [_channel sendMessage:byeMessage]; |
- } |
- // Disconnect from collider. |
- _channel = nil; |
- } |
- _clientId = nil; |
- _roomId = nil; |
- _isInitiator = NO; |
- _hasReceivedSdp = NO; |
- _messageQueue = [NSMutableArray array]; |
-#if defined(WEBRTC_IOS) |
- [_factory stopAecDump]; |
- [_peerConnection stopRtcEventLog]; |
-#endif |
- _peerConnection = nil; |
- self.state = kARDAppClientStateDisconnected; |
-#if defined(WEBRTC_IOS) |
- RTCStopInternalCapture(); |
-#endif |
-} |
- |
-#pragma mark - ARDSignalingChannelDelegate |
- |
-- (void)channel:(id<ARDSignalingChannel>)channel |
- didReceiveMessage:(ARDSignalingMessage *)message { |
- switch (message.type) { |
- case kARDSignalingMessageTypeOffer: |
- case kARDSignalingMessageTypeAnswer: |
- // Offers and answers must be processed before any other message, so we |
- // place them at the front of the queue. |
- _hasReceivedSdp = YES; |
- [_messageQueue insertObject:message atIndex:0]; |
- break; |
- case kARDSignalingMessageTypeCandidate: |
- case kARDSignalingMessageTypeCandidateRemoval: |
- [_messageQueue addObject:message]; |
- break; |
- case kARDSignalingMessageTypeBye: |
- // Disconnects can be processed immediately. |
- [self processSignalingMessage:message]; |
- return; |
- } |
- [self drainMessageQueueIfReady]; |
-} |
- |
-- (void)channel:(id<ARDSignalingChannel>)channel |
- didChangeState:(ARDSignalingChannelState)state { |
- switch (state) { |
- case kARDSignalingChannelStateOpen: |
- break; |
- case kARDSignalingChannelStateRegistered: |
- break; |
- case kARDSignalingChannelStateClosed: |
- case kARDSignalingChannelStateError: |
- // TODO(tkchin): reconnection scenarios. Right now we just disconnect |
- // completely if the websocket connection fails. |
- [self disconnect]; |
- break; |
- } |
-} |
- |
-#pragma mark - RTCPeerConnectionDelegate |
-// Callbacks for this delegate occur on non-main thread and need to be |
-// dispatched back to main queue as needed. |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didChangeSignalingState:(RTCSignalingState)stateChanged { |
- RTCLog(@"Signaling state changed: %ld", (long)stateChanged); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didAddStream:(RTCMediaStream *)stream { |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- RTCLog(@"Received %lu video tracks and %lu audio tracks", |
- (unsigned long)stream.videoTracks.count, |
- (unsigned long)stream.audioTracks.count); |
- if (stream.videoTracks.count) { |
- RTCVideoTrack *videoTrack = stream.videoTracks[0]; |
- [_delegate appClient:self didReceiveRemoteVideoTrack:videoTrack]; |
- } |
- }); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didRemoveStream:(RTCMediaStream *)stream { |
- RTCLog(@"Stream was removed."); |
-} |
- |
-- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection { |
- RTCLog(@"WARNING: Renegotiation needed but unimplemented."); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didChangeIceConnectionState:(RTCIceConnectionState)newState { |
- RTCLog(@"ICE state changed: %ld", (long)newState); |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- [_delegate appClient:self didChangeConnectionState:newState]; |
- }); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didChangeIceGatheringState:(RTCIceGatheringState)newState { |
- RTCLog(@"ICE gathering state changed: %ld", (long)newState); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didGenerateIceCandidate:(RTCIceCandidate *)candidate { |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- ARDICECandidateMessage *message = |
- [[ARDICECandidateMessage alloc] initWithCandidate:candidate]; |
- [self sendSignalingMessage:message]; |
- }); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didRemoveIceCandidates:(NSArray<RTCIceCandidate *> *)candidates { |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- ARDICECandidateRemovalMessage *message = |
- [[ARDICECandidateRemovalMessage alloc] |
- initWithRemovedCandidates:candidates]; |
- [self sendSignalingMessage:message]; |
- }); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didOpenDataChannel:(RTCDataChannel *)dataChannel { |
-} |
- |
-#pragma mark - RTCSessionDescriptionDelegate |
-// Callbacks for this delegate occur on non-main thread and need to be |
-// dispatched back to main queue as needed. |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didCreateSessionDescription:(RTCSessionDescription *)sdp |
- error:(NSError *)error { |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- if (error) { |
- RTCLogError(@"Failed to create session description. Error: %@", error); |
- [self disconnect]; |
- NSDictionary *userInfo = @{ |
- NSLocalizedDescriptionKey: @"Failed to create session description.", |
- }; |
- NSError *sdpError = |
- [[NSError alloc] initWithDomain:kARDAppClientErrorDomain |
- code:kARDAppClientErrorCreateSDP |
- userInfo:userInfo]; |
- [_delegate appClient:self didError:sdpError]; |
- return; |
- } |
- // Prefer H264 if available. |
- RTCSessionDescription *sdpPreferringH264 = |
- [ARDSDPUtils descriptionForDescription:sdp |
- preferredVideoCodec:@"H264"]; |
- __weak ARDAppClient *weakSelf = self; |
- [_peerConnection setLocalDescription:sdpPreferringH264 |
- completionHandler:^(NSError *error) { |
- ARDAppClient *strongSelf = weakSelf; |
- [strongSelf peerConnection:strongSelf.peerConnection |
- didSetSessionDescriptionWithError:error]; |
- }]; |
- ARDSessionDescriptionMessage *message = |
- [[ARDSessionDescriptionMessage alloc] |
- initWithDescription:sdpPreferringH264]; |
- [self sendSignalingMessage:message]; |
- }); |
-} |
- |
-- (void)peerConnection:(RTCPeerConnection *)peerConnection |
- didSetSessionDescriptionWithError:(NSError *)error { |
- dispatch_async(dispatch_get_main_queue(), ^{ |
- if (error) { |
- RTCLogError(@"Failed to set session description. Error: %@", error); |
- [self disconnect]; |
- NSDictionary *userInfo = @{ |
- NSLocalizedDescriptionKey: @"Failed to set session description.", |
- }; |
- NSError *sdpError = |
- [[NSError alloc] initWithDomain:kARDAppClientErrorDomain |
- code:kARDAppClientErrorSetSDP |
- userInfo:userInfo]; |
- [_delegate appClient:self didError:sdpError]; |
- return; |
- } |
- // If we're answering and we've just set the remote offer we need to create |
- // an answer and set the local description. |
- if (!_isInitiator && !_peerConnection.localDescription) { |
- RTCMediaConstraints *constraints = [self defaultAnswerConstraints]; |
- __weak ARDAppClient *weakSelf = self; |
- [_peerConnection answerForConstraints:constraints |
- completionHandler:^(RTCSessionDescription *sdp, |
- NSError *error) { |
- ARDAppClient *strongSelf = weakSelf; |
- [strongSelf peerConnection:strongSelf.peerConnection |
- didCreateSessionDescription:sdp |
- error:error]; |
- }]; |
- } |
- }); |
-} |
- |
-#pragma mark - Private |
- |
-#if defined(WEBRTC_IOS) |
- |
-- (NSString *)documentsFilePathForFileName:(NSString *)fileName { |
- NSParameterAssert(fileName.length); |
- NSArray *paths = NSSearchPathForDirectoriesInDomains( |
- NSDocumentDirectory, NSUserDomainMask, YES); |
- NSString *documentsDirPath = paths.firstObject; |
- NSString *filePath = |
- [documentsDirPath stringByAppendingPathComponent:fileName]; |
- return filePath; |
-} |
- |
-#endif |
- |
-- (BOOL)hasJoinedRoomServerRoom { |
- return _clientId.length; |
-} |
- |
-// Begins the peer connection connection process if we have both joined a room |
-// on the room server and tried to obtain a TURN server. Otherwise does nothing. |
-// A peer connection object will be created with a stream that contains local |
-// audio and video capture. If this client is the caller, an offer is created as |
-// well, otherwise the client will wait for an offer to arrive. |
-- (void)startSignalingIfReady { |
- if (!_isTurnComplete || !self.hasJoinedRoomServerRoom) { |
- return; |
- } |
- self.state = kARDAppClientStateConnected; |
- |
- // Create peer connection. |
- RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints]; |
- RTCConfiguration *config = [[RTCConfiguration alloc] init]; |
- config.iceServers = _iceServers; |
- _peerConnection = [_factory peerConnectionWithConfiguration:config |
- constraints:constraints |
- delegate:self]; |
- // Create AV senders. |
- [self createAudioSender]; |
- [self createVideoSender]; |
- if (_isInitiator) { |
- // Send offer. |
- __weak ARDAppClient *weakSelf = self; |
- [_peerConnection offerForConstraints:[self defaultOfferConstraints] |
- completionHandler:^(RTCSessionDescription *sdp, |
- NSError *error) { |
- ARDAppClient *strongSelf = weakSelf; |
- [strongSelf peerConnection:strongSelf.peerConnection |
- didCreateSessionDescription:sdp |
- error:error]; |
- }]; |
- } else { |
- // Check if we've received an offer. |
- [self drainMessageQueueIfReady]; |
- } |
-#if defined(WEBRTC_IOS) |
- // Start event log. |
- if (kARDAppClientEnableRtcEventLog) { |
- NSString *filePath = [self documentsFilePathForFileName:@"webrtc-rtceventlog"]; |
- if (![_peerConnection startRtcEventLogWithFilePath:filePath |
- maxSizeInBytes:kARDAppClientRtcEventLogMaxSizeInBytes]) { |
- RTCLogError(@"Failed to start event logging."); |
- } |
- } |
- |
- // Start aecdump diagnostic recording. |
- if (_shouldMakeAecDump) { |
- NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"]; |
- if (![_factory startAecDumpWithFilePath:filePath |
- maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) { |
- RTCLogError(@"Failed to start aec dump."); |
- } |
- } |
-#endif |
-} |
- |
-// Processes the messages that we've received from the room server and the |
-// signaling channel. The offer or answer message must be processed before other |
-// signaling messages, however they can arrive out of order. Hence, this method |
-// only processes pending messages if there is a peer connection object and |
-// if we have received either an offer or answer. |
-- (void)drainMessageQueueIfReady { |
- if (!_peerConnection || !_hasReceivedSdp) { |
- return; |
- } |
- for (ARDSignalingMessage *message in _messageQueue) { |
- [self processSignalingMessage:message]; |
- } |
- [_messageQueue removeAllObjects]; |
-} |
- |
-// Processes the given signaling message based on its type. |
-- (void)processSignalingMessage:(ARDSignalingMessage *)message { |
- NSParameterAssert(_peerConnection || |
- message.type == kARDSignalingMessageTypeBye); |
- switch (message.type) { |
- case kARDSignalingMessageTypeOffer: |
- case kARDSignalingMessageTypeAnswer: { |
- ARDSessionDescriptionMessage *sdpMessage = |
- (ARDSessionDescriptionMessage *)message; |
- RTCSessionDescription *description = sdpMessage.sessionDescription; |
- // Prefer H264 if available. |
- RTCSessionDescription *sdpPreferringH264 = |
- [ARDSDPUtils descriptionForDescription:description |
- preferredVideoCodec:@"H264"]; |
- __weak ARDAppClient *weakSelf = self; |
- [_peerConnection setRemoteDescription:sdpPreferringH264 |
- completionHandler:^(NSError *error) { |
- ARDAppClient *strongSelf = weakSelf; |
- [strongSelf peerConnection:strongSelf.peerConnection |
- didSetSessionDescriptionWithError:error]; |
- }]; |
- break; |
- } |
- case kARDSignalingMessageTypeCandidate: { |
- ARDICECandidateMessage *candidateMessage = |
- (ARDICECandidateMessage *)message; |
- [_peerConnection addIceCandidate:candidateMessage.candidate]; |
- break; |
- } |
- case kARDSignalingMessageTypeCandidateRemoval: { |
- ARDICECandidateRemovalMessage *candidateMessage = |
- (ARDICECandidateRemovalMessage *)message; |
- [_peerConnection removeIceCandidates:candidateMessage.candidates]; |
- break; |
- } |
- case kARDSignalingMessageTypeBye: |
- // Other client disconnected. |
- // TODO(tkchin): support waiting in room for next client. For now just |
- // disconnect. |
- [self disconnect]; |
- break; |
- } |
-} |
- |
-// Sends a signaling message to the other client. The caller will send messages |
-// through the room server, whereas the callee will send messages over the |
-// signaling channel. |
-- (void)sendSignalingMessage:(ARDSignalingMessage *)message { |
- if (_isInitiator) { |
- __weak ARDAppClient *weakSelf = self; |
- [_roomServerClient sendMessage:message |
- forRoomId:_roomId |
- clientId:_clientId |
- completionHandler:^(ARDMessageResponse *response, |
- NSError *error) { |
- ARDAppClient *strongSelf = weakSelf; |
- if (error) { |
- [strongSelf.delegate appClient:strongSelf didError:error]; |
- return; |
- } |
- NSError *messageError = |
- [[strongSelf class] errorForMessageResultType:response.result]; |
- if (messageError) { |
- [strongSelf.delegate appClient:strongSelf didError:messageError]; |
- return; |
- } |
- }]; |
- } else { |
- [_channel sendMessage:message]; |
- } |
-} |
- |
-- (RTCRtpSender *)createVideoSender { |
- RTCRtpSender *sender = |
- [_peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo |
- streamId:kARDMediaStreamId]; |
- RTCVideoTrack *track = [self createLocalVideoTrack]; |
- if (track) { |
- sender.track = track; |
- [_delegate appClient:self didReceiveLocalVideoTrack:track]; |
- } |
- return sender; |
-} |
- |
-- (RTCRtpSender *)createAudioSender { |
- RTCMediaConstraints *constraints = [self defaultMediaAudioConstraints]; |
- RTCAudioSource *source = [_factory audioSourceWithConstraints:constraints]; |
- RTCAudioTrack *track = [_factory audioTrackWithSource:source |
- trackId:kARDAudioTrackId]; |
- RTCRtpSender *sender = |
- [_peerConnection senderWithKind:kRTCMediaStreamTrackKindAudio |
- streamId:kARDMediaStreamId]; |
- sender.track = track; |
- return sender; |
-} |
- |
-- (RTCVideoTrack *)createLocalVideoTrack { |
- RTCVideoTrack* localVideoTrack = nil; |
- // The iOS simulator doesn't provide any sort of camera capture |
- // support or emulation (http://goo.gl/rHAnC1) so don't bother |
- // trying to open a local stream. |
-#if !TARGET_IPHONE_SIMULATOR |
- if (!_isAudioOnly) { |
- RTCMediaConstraints *mediaConstraints = |
- [self defaultMediaStreamConstraints]; |
- RTCAVFoundationVideoSource *source = |
- [_factory avFoundationVideoSourceWithConstraints:mediaConstraints]; |
- localVideoTrack = |
- [_factory videoTrackWithSource:source |
- trackId:kARDVideoTrackId]; |
- } |
-#endif |
- return localVideoTrack; |
-} |
- |
-#pragma mark - Collider methods |
- |
-- (void)registerWithColliderIfReady { |
- if (!self.hasJoinedRoomServerRoom) { |
- return; |
- } |
- // Open WebSocket connection. |
- if (!_channel) { |
- _channel = |
- [[ARDWebSocketChannel alloc] initWithURL:_websocketURL |
- restURL:_websocketRestURL |
- delegate:self]; |
- if (_isLoopback) { |
- _loopbackChannel = |
- [[ARDLoopbackWebSocketChannel alloc] initWithURL:_websocketURL |
- restURL:_websocketRestURL]; |
- } |
- } |
- [_channel registerForRoomId:_roomId clientId:_clientId]; |
- if (_isLoopback) { |
- [_loopbackChannel registerForRoomId:_roomId clientId:@"LOOPBACK_CLIENT_ID"]; |
- } |
-} |
- |
-#pragma mark - Defaults |
- |
- - (RTCMediaConstraints *)defaultMediaAudioConstraints { |
- NSString *valueLevelControl = _shouldUseLevelControl ? |
- kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse; |
- NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl }; |
- RTCMediaConstraints* constraints = |
- [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints |
- optionalConstraints:nil]; |
- return constraints; |
-} |
- |
-- (RTCMediaConstraints *)defaultMediaStreamConstraints { |
- RTCMediaConstraints* constraints = |
- [[RTCMediaConstraints alloc] |
- initWithMandatoryConstraints:nil |
- optionalConstraints:nil]; |
- return constraints; |
-} |
- |
-- (RTCMediaConstraints *)defaultAnswerConstraints { |
- return [self defaultOfferConstraints]; |
-} |
- |
-- (RTCMediaConstraints *)defaultOfferConstraints { |
- NSDictionary *mandatoryConstraints = @{ |
- @"OfferToReceiveAudio" : @"true", |
- @"OfferToReceiveVideo" : @"true" |
- }; |
- RTCMediaConstraints* constraints = |
- [[RTCMediaConstraints alloc] |
- initWithMandatoryConstraints:mandatoryConstraints |
- optionalConstraints:nil]; |
- return constraints; |
-} |
- |
-- (RTCMediaConstraints *)defaultPeerConnectionConstraints { |
- if (_defaultPeerConnectionConstraints) { |
- return _defaultPeerConnectionConstraints; |
- } |
- NSString *value = _isLoopback ? @"false" : @"true"; |
- NSDictionary *optionalConstraints = @{ @"DtlsSrtpKeyAgreement" : value }; |
- RTCMediaConstraints* constraints = |
- [[RTCMediaConstraints alloc] |
- initWithMandatoryConstraints:nil |
- optionalConstraints:optionalConstraints]; |
- return constraints; |
-} |
- |
-- (RTCIceServer *)defaultSTUNServer { |
- return [[RTCIceServer alloc] initWithURLStrings:@[kARDDefaultSTUNServerUrl] |
- username:@"" |
- credential:@""]; |
-} |
- |
-#pragma mark - Errors |
- |
-+ (NSError *)errorForJoinResultType:(ARDJoinResultType)resultType { |
- NSError *error = nil; |
- switch (resultType) { |
- case kARDJoinResultTypeSuccess: |
- break; |
- case kARDJoinResultTypeUnknown: { |
- error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain |
- code:kARDAppClientErrorUnknown |
- userInfo:@{ |
- NSLocalizedDescriptionKey: @"Unknown error.", |
- }]; |
- break; |
- } |
- case kARDJoinResultTypeFull: { |
- error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain |
- code:kARDAppClientErrorRoomFull |
- userInfo:@{ |
- NSLocalizedDescriptionKey: @"Room is full.", |
- }]; |
- break; |
- } |
- } |
- return error; |
-} |
- |
-+ (NSError *)errorForMessageResultType:(ARDMessageResultType)resultType { |
- NSError *error = nil; |
- switch (resultType) { |
- case kARDMessageResultTypeSuccess: |
- break; |
- case kARDMessageResultTypeUnknown: |
- error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain |
- code:kARDAppClientErrorUnknown |
- userInfo:@{ |
- NSLocalizedDescriptionKey: @"Unknown error.", |
- }]; |
- break; |
- case kARDMessageResultTypeInvalidClient: |
- error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain |
- code:kARDAppClientErrorInvalidClient |
- userInfo:@{ |
- NSLocalizedDescriptionKey: @"Invalid client.", |
- }]; |
- break; |
- case kARDMessageResultTypeInvalidRoom: |
- error = [[NSError alloc] initWithDomain:kARDAppClientErrorDomain |
- code:kARDAppClientErrorInvalidRoom |
- userInfo:@{ |
- NSLocalizedDescriptionKey: @"Invalid room.", |
- }]; |
- break; |
- } |
- return error; |
-} |
- |
-@end |