diff --git a/.gitignore b/.gitignore index 7a1b541..f357ad6 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ xcuserdata # Misc .DS_Store #*.lock + +.idea/ diff --git a/AppRTC/ARTCVideoChatViewController.m b/AppRTC/ARTCVideoChatViewController.m index c2ccf91..aef74cd 100644 --- a/AppRTC/ARTCVideoChatViewController.m +++ b/AppRTC/ARTCVideoChatViewController.m @@ -8,6 +8,7 @@ #import "ARTCVideoChatViewController.h" #import +#import "RTCI420Frame.h" #define SERVER_HOST_URL @"https://apprtc.appspot.com" @@ -37,11 +38,11 @@ - (void)viewDidLoad { //RTCEAGLVideoViewDelegate provides notifications on video frame dimensions [self.remoteView setDelegate:self]; [self.localView setDelegate:self]; - + //Getting Orientation change [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) - name:@"UIDeviceOrientationDidChangeNotification" + name:UIDeviceOrientationDidChangeNotification object:nil]; @@ -70,7 +71,7 @@ - (void)viewWillAppear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UIDeviceOrientationDidChangeNotification" object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; [self disconnect]; } @@ -82,7 +83,7 @@ - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } -- (void)orientationChanged:(NSNotification *)notification{ +- (void)orientationChanged:(NSNotification *)notification { [self videoView:self.localView didChangeVideoSize:self.localVideoSize]; [self videoView:self.remoteView didChangeVideoSize:self.remoteVideoSize]; } @@ -200,6 +201,7 @@ - (void)appClient:(ARDAppClient *)client didReceiveLocalVideoTrack:(RTCVideoTrac - (void)appClient:(ARDAppClient *)client didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack { self.remoteVideoTrack = remoteVideoTrack; + [self.remoteVideoTrack addRenderer:self.remoteView]; [UIView animateWithDuration:0.4f animations:^{ @@ -237,12 +239,12 @@ - (void)videoView:(RTCEAGLVideoView *)videoView didChangeVideoSize:(CGSize)size CGRect videoRect = self.view.bounds; if (self.remoteVideoTrack) { videoRect = CGRectMake(0.0f, 0.0f, self.view.frame.size.width/4.0f, self.view.frame.size.height/4.0f); - if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) { + if ( orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight ) { videoRect = CGRectMake(0.0f, 0.0f, self.view.frame.size.height/4.0f, self.view.frame.size.width/4.0f); } } CGRect videoFrame = AVMakeRectWithAspectRatioInsideRect(aspectRatio, videoRect); - + //Resize the localView accordingly [self.localViewWidthConstraint setConstant:videoFrame.size.width]; [self.localViewHeightConstraint setConstant:videoFrame.size.height]; @@ -253,7 +255,7 @@ - (void)videoView:(RTCEAGLVideoView *)videoView didChangeVideoSize:(CGSize)size [self.localViewBottomConstraint setConstant:containerHeight/2.0f - videoFrame.size.height/2.0f]; //center [self.localViewRightConstraint setConstant:containerWidth/2.0f - videoFrame.size.width/2.0f]; //center } - } else if (videoView == self.remoteView) { + } else if ( videoView == self.remoteView ) { //Resize Remote View self.remoteVideoSize = size; CGSize aspectRatio = CGSizeEqualToSize(size, CGSizeZero) ? defaultAspectRatio : size; @@ -269,7 +271,6 @@ - (void)videoView:(RTCEAGLVideoView *)videoView didChangeVideoSize:(CGSize)size [self.remoteViewBottomConstraint setConstant:containerHeight/2.0f - videoFrame.size.height/2.0f]; [self.remoteViewLeftConstraint setConstant:containerWidth/2.0f - videoFrame.size.width/2.0f]; //center [self.remoteViewRightConstraint setConstant:containerWidth/2.0f - videoFrame.size.width/2.0f]; //center - } [self.view layoutIfNeeded]; }]; diff --git a/AppRTC/Base.lproj/Main.storyboard b/AppRTC/Base.lproj/Main.storyboard index 02194e6..06aa969 100644 --- a/AppRTC/Base.lproj/Main.storyboard +++ b/AppRTC/Base.lproj/Main.storyboard @@ -1,7 +1,7 @@ - + - + @@ -12,15 +12,19 @@ + + + + @@ -41,6 +46,7 @@ + @@ -48,6 +54,7 @@ + @@ -97,6 +106,7 @@ + @@ -135,10 +145,12 @@ + + @@ -150,6 +162,7 @@ + @@ -183,6 +198,7 @@ + @@ -219,6 +238,7 @@ + @@ -268,6 +288,7 @@ + diff --git a/Lib/ARDAppClient.h b/Lib/ARDAppClient.h index 3db1db6..fc07e54 100755 --- a/Lib/ARDAppClient.h +++ b/Lib/ARDAppClient.h @@ -26,29 +26,30 @@ */ #import - #import "RTCVideoTrack.h" typedef NS_ENUM(NSInteger, ARDAppClientState) { - // Disconnected from servers. - kARDAppClientStateDisconnected, - // Connecting to servers. - kARDAppClientStateConnecting, - // Connected to servers. - kARDAppClientStateConnected, + // Disconnected from servers. + kARDAppClientStateDisconnected, + // Connecting to servers. + kARDAppClientStateConnecting, + // Connected to servers. + kARDAppClientStateConnected, }; @class ARDAppClient; +@class AVCaptureDevice; + @protocol ARDAppClientDelegate - (void)appClient:(ARDAppClient *)client - didChangeState:(ARDAppClientState)state; + didChangeState:(ARDAppClientState)state; - (void)appClient:(ARDAppClient *)client - didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack; +didReceiveLocalVideoTrack:(RTCVideoTrack *)localVideoTrack; - (void)appClient:(ARDAppClient *)client - didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack; +didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack; - (void)appClient:(ARDAppClient *)client didError:(NSError *)error; diff --git a/Lib/ARDAppClient.m b/Lib/ARDAppClient.m index 653c4b7..fcf9fe1 100755 --- a/Lib/ARDAppClient.m +++ b/Lib/ARDAppClient.m @@ -49,20 +49,20 @@ // TODO(tkchin): move these to a configuration object. static NSString *kARDRoomServerHostUrl = - @"https://apprtc.appspot.com"; +@"https://apprtc.appspot.com"; static NSString *kARDRoomServerRegisterFormat = - @"%@/join/%@"; +@"%@/join/%@"; static NSString *kARDRoomServerMessageFormat = - @"%@/message/%@/%@"; +@"%@/message/%@/%@"; static NSString *kARDRoomServerByeFormat = - @"%@/leave/%@/%@"; +@"%@/leave/%@/%@"; static NSString *kARDDefaultSTUNServerUrl = - @"stun:stun.l.google.com:19302"; +@"stun:stun.l.google.com:19302"; // TODO(tkchin): figure out a better username for CEOD statistics. static NSString *kARDTurnRequestUrl = - @"https://computeengineondemand.appspot.com" - @"/turn?username=iapprtc&key=4080218913"; +@"https://computeengineondemand.appspot.com" +@"/turn?username=iapprtc&key=4080218913"; static NSString *kARDAppClientErrorDomain = @"ARDAppClient"; static NSInteger kARDAppClientErrorUnknown = -1; @@ -74,7 +74,14 @@ static NSInteger kARDAppClientErrorInvalidRoom = -7; @interface ARDAppClient () +RTCPeerConnectionDelegate, RTCSessionDescriptionDelegate> +{ + UIDeviceOrientation currentOrientation; + BOOL isForeGround; + AVCaptureSession* sessionCurrent; + AVCaptureDevicePosition cameraPosition; + BOOL isMuteVideoIn; +} @property(nonatomic, strong) ARDWebSocketChannel *channel; @property(nonatomic, strong) RTCPeerConnection *peerConnection; @property(nonatomic, strong) RTCPeerConnectionFactory *factory; @@ -116,175 +123,244 @@ @implementation ARDAppClient @synthesize webSocketRestURL = _websocketRestURL; - (instancetype)initWithDelegate:(id)delegate { - if (self = [super init]) { - _delegate = delegate; - _factory = [[RTCPeerConnectionFactory alloc] init]; - _messageQueue = [NSMutableArray array]; - _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]]; - _serverHostUrl = kARDRoomServerHostUrl; - _isSpeakerEnabled = YES; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(orientationChanged:) - name:@"UIDeviceOrientationDidChangeNotification" - object:nil]; - } - return self; + if (self = [super init]) { + _delegate = delegate; + _factory = [[RTCPeerConnectionFactory alloc] init]; + _messageQueue = [NSMutableArray array]; + _iceServers = [NSMutableArray arrayWithObject:[self defaultSTUNServer]]; + _serverHostUrl = kARDRoomServerHostUrl; + currentOrientation = [[UIDevice currentDevice] orientation]; + isForeGround = YES; + _isSpeakerEnabled = YES; + isMuteVideoIn = NO; + cameraPosition = AVCaptureDevicePositionFront; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCaptureSessionStopRunning:) name:AVCaptureSessionDidStopRunningNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleStartCaptureSession:) name:AVCaptureSessionDidStartRunningNotification object:nil]; + + // Init observers to handle going into fore- and back- ground + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackgroundVideo) name:UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterForegroundVideo) name:UIApplicationDidBecomeActiveNotification object:nil]; + } + return self; } - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UIDeviceOrientationDidChangeNotification" object:nil]; - [self disconnect]; +#ifdef DEBUG + NSLog(@"\n\n ===== ARDAppClient dealloc ======== \n\n "); +#endif + [ [NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; + [ [NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStopRunningNotification object:nil ]; + [ [NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureSessionDidStartRunningNotification object:nil ]; + + [ [NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; + [ [NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil ]; + + [self disconnect]; +} + +#pragma mark - + +- (void)handleCaptureSessionStopRunning:(NSNotification *)notification { + if ([NSThread isMainThread]) { + if (isForeGround) { + [self restoreAllMediaStreams]; + } + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + if (isForeGround) { + [self restoreAllMediaStreams]; + } + }); + } +} + +- (void)handleStartCaptureSession:(NSNotification *)notification { + sessionCurrent = notification.object; } - (void)orientationChanged:(NSNotification *)notification { UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; - if (UIDeviceOrientationIsLandscape(orientation) || UIDeviceOrientationIsPortrait(orientation)) { - //Remove current video track - RTCMediaStream *localStream = _peerConnection.localStreams[0]; - [localStream removeVideoTrack:localStream.videoTracks[0]]; - - RTCVideoTrack *localVideoTrack = [self createLocalVideoTrack]; - if (localVideoTrack) { - [localStream addVideoTrack:localVideoTrack]; - [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack]; + if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight || orientation == UIDeviceOrientationPortrait) { + if (currentOrientation == orientation) { + return; } - [_peerConnection removeStream:localStream]; - [_peerConnection addStream:localStream]; + currentOrientation = orientation; + [self cleanUpAllMediaStreams]; + } +} + +- (void)didEnterForegroundVideo { + isForeGround = YES; + [self restoreAllMediaStreams]; +} + +- (void)didEnterBackgroundVideo { + isForeGround = NO; + [self cleanUpAllMediaStreams]; +} + +- (void)restoreAllMediaStreams { + if (self.peerConnection && _peerConnection.localStreams.count == 0 && !isMuteVideoIn) { + RTCMediaStream* localStream = [self createLocalMediaStream]; + [self.peerConnection addStream:localStream]; + } +} + +-(void)cleanUpAllMediaStreams { + //Remove current video track + if (_peerConnection.localStreams.count == 0) { + return; + } + RTCMediaStream *localStream = _peerConnection.localStreams[0]; + if (localStream.videoTracks.count == 0) { + return; + } + [localStream removeVideoTrack:localStream.videoTracks[0]]; + + if (localStream.audioTracks.count > 0) { + RTCAudioTrack* formerAudioTrack = localStream.audioTracks[0]; + [localStream removeAudioTrack:formerAudioTrack]; + } + + [_peerConnection removeStream:localStream]; + if (sessionCurrent) { + [sessionCurrent stopRunning]; } } - (void)setState:(ARDAppClientState)state { - if (_state == state) { - return; - } - _state = state; - [_delegate appClient:self didChangeState:_state]; + if (_state == state) { + return; + } + _state = state; + [_delegate appClient:self didChangeState:_state]; } - (void)connectToRoomWithId:(NSString *)roomId options:(NSDictionary *)options { - NSParameterAssert(roomId.length); - NSParameterAssert(_state == kARDAppClientStateDisconnected); - self.state = kARDAppClientStateConnecting; - - // Request TURN. - __weak ARDAppClient *weakSelf = self; - NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl]; - [self requestTURNServersWithURL:turnRequestURL - completionHandler:^(NSArray *turnServers) { - ARDAppClient *strongSelf = weakSelf; - [strongSelf.iceServers addObjectsFromArray:turnServers]; - strongSelf.isTurnComplete = YES; - [strongSelf startSignalingIfReady]; - }]; - - // Register with room server. - [self registerWithRoomServerForRoomId:roomId - completionHandler:^(ARDRegisterResponse *response) { - ARDAppClient *strongSelf = weakSelf; - if (!response || response.result != kARDRegisterResultTypeSuccess) { - NSLog(@"Failed to register with room server. Result:%d", - (int)response.result); - [strongSelf disconnect]; - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: @"Room is full.", - }; - NSError *error = - [[NSError alloc] initWithDomain:kARDAppClientErrorDomain - code:kARDAppClientErrorRoomFull - userInfo:userInfo]; - [strongSelf.delegate appClient:strongSelf didError:error]; - return; - } - NSLog(@"Registered with room server."); - 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]; - }]; + NSParameterAssert(roomId.length); + NSParameterAssert(_state == kARDAppClientStateDisconnected); + self.state = kARDAppClientStateConnecting; + + // Request TURN. + __weak ARDAppClient *weakSelf = self; + NSURL *turnRequestURL = [NSURL URLWithString:kARDTurnRequestUrl]; + [self requestTURNServersWithURL:turnRequestURL + completionHandler:^(NSArray *turnServers) { + ARDAppClient *strongSelf = weakSelf; + [strongSelf.iceServers addObjectsFromArray:turnServers]; + strongSelf.isTurnComplete = YES; + [strongSelf startSignalingIfReady]; + }]; + + // Register with room server. + [self registerWithRoomServerForRoomId:roomId + completionHandler:^(ARDRegisterResponse *response) { + ARDAppClient *strongSelf = weakSelf; + if (!response || response.result != kARDRegisterResultTypeSuccess) { + NSLog(@"Failed to register with room server. Result:%d", + (int)response.result); + [strongSelf disconnect]; + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: @"Room is full.", + }; + NSError *error = + [[NSError alloc] initWithDomain:kARDAppClientErrorDomain + code:kARDAppClientErrorRoomFull + userInfo:userInfo]; + [strongSelf.delegate appClient:strongSelf didError:error]; + return; + } + NSLog(@"Registered with room server."); + 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.isRegisteredWithRoomServer) { - [self unregisterWithRoomServer]; - } - if (_channel) { - if (_channel.state == kARDWebSocketChannelStateRegistered) { - // Tell the other client we're hanging up. - ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init]; - NSData *byeData = [byeMessage JSONData]; - [_channel sendData:byeData]; + if (_state == kARDAppClientStateDisconnected) { + return; + } + if (self.isRegisteredWithRoomServer) { + [self unregisterWithRoomServer]; + } + if (_channel) { + if (_channel.state == kARDWebSocketChannelStateRegistered) { + // Tell the other client we're hanging up. + ARDByeMessage *byeMessage = [[ARDByeMessage alloc] init]; + NSData *byeData = [byeMessage JSONData]; + [_channel sendData:byeData]; + } + // Disconnect from collider. + _channel = nil; } - // Disconnect from collider. - _channel = nil; - } - _clientId = nil; - _roomId = nil; - _isInitiator = NO; - _hasReceivedSdp = NO; - _messageQueue = [NSMutableArray array]; - _peerConnection = nil; - self.state = kARDAppClientStateDisconnected; + _clientId = nil; + _roomId = nil; + _isInitiator = NO; + _hasReceivedSdp = NO; + _messageQueue = [NSMutableArray array]; + _peerConnection = nil; + self.state = kARDAppClientStateDisconnected; } #pragma mark - ARDWebSocketChannelDelegate - (void)channel:(ARDWebSocketChannel *)channel - didReceiveMessage:(ARDSignalingMessage *)message { - switch (message.type) { - case kARDSignalingMessageTypeOffer: - case kARDSignalingMessageTypeAnswer: - _hasReceivedSdp = YES; - [_messageQueue insertObject:message atIndex:0]; - break; - case kARDSignalingMessageTypeCandidate: - [_messageQueue addObject:message]; - break; - case kARDSignalingMessageTypeBye: - [self processSignalingMessage:message]; - return; - } - [self drainMessageQueueIfReady]; +didReceiveMessage:(ARDSignalingMessage *)message { + switch (message.type) { + case kARDSignalingMessageTypeOffer: + case kARDSignalingMessageTypeAnswer: + _hasReceivedSdp = YES; + [_messageQueue insertObject:message atIndex:0]; + break; + case kARDSignalingMessageTypeCandidate: + [_messageQueue addObject:message]; + break; + case kARDSignalingMessageTypeBye: + [self processSignalingMessage:message]; + return; + } + [self drainMessageQueueIfReady]; } - (void)channel:(ARDWebSocketChannel *)channel - didChangeState:(ARDWebSocketChannelState)state { - switch (state) { - case kARDWebSocketChannelStateOpen: - break; - case kARDWebSocketChannelStateRegistered: - break; - case kARDWebSocketChannelStateClosed: - case kARDWebSocketChannelStateError: - // TODO(tkchin): reconnection scenarios. Right now we just disconnect - // completely if the websocket connection fails. - [self disconnect]; - break; - } + didChangeState:(ARDWebSocketChannelState)state { + switch (state) { + case kARDWebSocketChannelStateOpen: + break; + case kARDWebSocketChannelStateRegistered: + break; + case kARDWebSocketChannelStateClosed: + case kARDWebSocketChannelStateError: + // TODO(tkchin): reconnection scenarios. Right now we just disconnect + // completely if the websocket connection fails. + [self disconnect]; + break; + } } #pragma mark - RTCPeerConnectionDelegate - (void)peerConnection:(RTCPeerConnection *)peerConnection - signalingStateChanged:(RTCSignalingState)stateChanged { - NSLog(@"Signaling state changed: %d", stateChanged); + signalingStateChanged:(RTCSignalingState)stateChanged { + NSLog(@"Signaling state changed: %d", stateChanged); } - (void)peerConnection:(RTCPeerConnection *)peerConnection @@ -303,32 +379,43 @@ - (void)peerConnection:(RTCPeerConnection *)peerConnection } - (void)peerConnection:(RTCPeerConnection *)peerConnection - removedStream:(RTCMediaStream *)stream { - NSLog(@"Stream was removed."); + removedStream:(RTCMediaStream *)stream { + NSLog(@"Stream was removed."); } - (void)peerConnectionOnRenegotiationNeeded: - (RTCPeerConnection *)peerConnection { - NSLog(@"WARNING: Renegotiation needed but unimplemented."); +(RTCPeerConnection *)peerConnection { + NSLog(@"WARNING: Renegotiation needed but unimplemented."); } - (void)peerConnection:(RTCPeerConnection *)peerConnection - iceConnectionChanged:(RTCICEConnectionState)newState { - NSLog(@"ICE state changed: %d", newState); + iceConnectionChanged:(RTCICEConnectionState)newState { + NSLog(@"ICE state changed: %d", newState); + switch (newState) { + case RTCICEConnectionCompleted: + NSLog(@"RTCICEConnectionCompleted"); + break; + case RTCICEConnectionConnected: + NSLog(@"RTCICEConnectionConnected"); + break; + default: + break; + } } - (void)peerConnection:(RTCPeerConnection *)peerConnection - iceGatheringChanged:(RTCICEGatheringState)newState { - NSLog(@"ICE gathering state changed: %d", newState); + iceGatheringChanged:(RTCICEGatheringState)newState { + NSLog(@"ICE gathering state changed: %d", newState); } + - (void)peerConnection:(RTCPeerConnection *)peerConnection gotICECandidate:(RTCICECandidate *)candidate { - dispatch_async(dispatch_get_main_queue(), ^{ - ARDICECandidateMessage *message = + dispatch_async(dispatch_get_main_queue(), ^{ + ARDICECandidateMessage *message = [[ARDICECandidateMessage alloc] initWithCandidate:candidate]; - [self sendSignalingMessage:message]; - }); + [self sendSignalingMessage:message]; + }); } - (void)peerConnection:(RTCPeerConnection*)peerConnection @@ -338,153 +425,152 @@ - (void)peerConnection:(RTCPeerConnection*)peerConnection #pragma mark - RTCSessionDescriptionDelegate - (void)peerConnection:(RTCPeerConnection *)peerConnection - didCreateSessionDescription:(RTCSessionDescription *)sdp - error:(NSError *)error { - dispatch_async(dispatch_get_main_queue(), ^{ - if (error) { - NSLog(@"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; - } - [_peerConnection setLocalDescriptionWithDelegate:self - sessionDescription:sdp]; - ARDSessionDescriptionMessage *message = +didCreateSessionDescription:(RTCSessionDescription *)sdp + error:(NSError *)error { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + NSLog(@"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; + } + [_peerConnection setLocalDescriptionWithDelegate:self + sessionDescription:sdp]; + ARDSessionDescriptionMessage *message = [[ARDSessionDescriptionMessage alloc] initWithDescription:sdp]; - [self sendSignalingMessage:message]; - }); + [self sendSignalingMessage:message]; + }); } - (void)peerConnection:(RTCPeerConnection *)peerConnection - didSetSessionDescriptionWithError:(NSError *)error { - dispatch_async(dispatch_get_main_queue(), ^{ - if (error) { - NSLog(@"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]; - [_peerConnection createAnswerWithDelegate:self - constraints:constraints]; - - } - }); +didSetSessionDescriptionWithError:(NSError *)error { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + NSLog(@"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]; + [_peerConnection createAnswerWithDelegate:self + constraints:constraints]; + + } + }); } #pragma mark - Private - (BOOL)isRegisteredWithRoomServer { - return _clientId.length; + return _clientId.length; } - (void)startSignalingIfReady { - if (!_isTurnComplete || !self.isRegisteredWithRoomServer) { - return; - } - self.state = kARDAppClientStateConnected; - - // Create peer connection. - RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints]; - _peerConnection = [_factory peerConnectionWithICEServers:_iceServers - constraints:constraints - delegate:self]; - RTCMediaStream *localStream = [self createLocalMediaStream]; - [_peerConnection addStream:localStream]; - if (_isInitiator) { - [self sendOffer]; - } else { - [self waitForAnswer]; - } + if (!_isTurnComplete || !self.isRegisteredWithRoomServer) { + return; + } + self.state = kARDAppClientStateConnected; + + // Create peer connection. + RTCMediaConstraints *constraints = [self defaultPeerConnectionConstraints]; + _peerConnection = [_factory peerConnectionWithICEServers:_iceServers + constraints:constraints + delegate:self]; + RTCMediaStream *localStream = [self createLocalMediaStream]; + [_peerConnection addStream:localStream]; + if (_isInitiator) { + [self sendOffer]; + } else { + [self waitForAnswer]; + } } - (void)sendOffer { - [_peerConnection createOfferWithDelegate:self - constraints:[self defaultOfferConstraints]]; + [_peerConnection createOfferWithDelegate:self constraints:[self defaultOfferConstraints]]; } - (void)waitForAnswer { - [self drainMessageQueueIfReady]; + [self drainMessageQueueIfReady]; } - (void)drainMessageQueueIfReady { - if (!_peerConnection || !_hasReceivedSdp) { - return; - } - for (ARDSignalingMessage *message in _messageQueue) { - [self processSignalingMessage:message]; - } - [_messageQueue removeAllObjects]; + if (!_peerConnection || !_hasReceivedSdp) { + return; + } + for (ARDSignalingMessage *message in _messageQueue) { + [self processSignalingMessage:message]; + } + [_messageQueue removeAllObjects]; } - (void)processSignalingMessage:(ARDSignalingMessage *)message { - NSParameterAssert(_peerConnection || - message.type == kARDSignalingMessageTypeBye); - switch (message.type) { - case kARDSignalingMessageTypeOffer: - case kARDSignalingMessageTypeAnswer: { - ARDSessionDescriptionMessage *sdpMessage = - (ARDSessionDescriptionMessage *)message; - RTCSessionDescription *description = sdpMessage.sessionDescription; - [_peerConnection setRemoteDescriptionWithDelegate:self - sessionDescription:description]; - break; - } - case kARDSignalingMessageTypeCandidate: { - ARDICECandidateMessage *candidateMessage = - (ARDICECandidateMessage *)message; - [_peerConnection addICECandidate:candidateMessage.candidate]; - break; + NSParameterAssert(_peerConnection || + message.type == kARDSignalingMessageTypeBye); + switch (message.type) { + case kARDSignalingMessageTypeOffer: + case kARDSignalingMessageTypeAnswer: { + ARDSessionDescriptionMessage *sdpMessage = + (ARDSessionDescriptionMessage *)message; + RTCSessionDescription *description = sdpMessage.sessionDescription; + [_peerConnection setRemoteDescriptionWithDelegate:self + sessionDescription:description]; + break; + } + case kARDSignalingMessageTypeCandidate: { + ARDICECandidateMessage *candidateMessage = + (ARDICECandidateMessage *)message; + [_peerConnection addICECandidate:candidateMessage.candidate]; + break; + } + case kARDSignalingMessageTypeBye: + // Other client disconnected. + // TODO(tkchin): support waiting in room for next client. For now just + // disconnect. + [self disconnect]; + break; } - case kARDSignalingMessageTypeBye: - // Other client disconnected. - // TODO(tkchin): support waiting in room for next client. For now just - // disconnect. - [self disconnect]; - break; - } } - (void)sendSignalingMessage:(ARDSignalingMessage *)message { - if (_isInitiator) { - [self sendSignalingMessageToRoomServer:message completionHandler:nil]; - } else { - [self sendSignalingMessageToCollider:message]; - } + if (_isInitiator) { + [self sendSignalingMessageToRoomServer:message completionHandler:nil]; + } else { + [self sendSignalingMessageToCollider:message]; + } } -- (RTCVideoTrack *)createLocalVideoTrack { +- (RTCVideoTrack *)createLocalVideoTrack:(AVCaptureDevicePosition)mediaType { // 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. // TODO(tkchin): local video capture for OSX. See // https://code.google.com/p/webrtc/issues/detail?id=3417. - + RTCVideoTrack *localVideoTrack = nil; #if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE - + NSString *cameraID = nil; for (AVCaptureDevice *captureDevice in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { - if (captureDevice.position == AVCaptureDevicePositionFront) { + if (captureDevice.position == mediaType) { cameraID = [captureDevice localizedName]; break; } @@ -501,8 +587,8 @@ - (RTCVideoTrack *)createLocalVideoTrack { - (RTCMediaStream *)createLocalMediaStream { RTCMediaStream* localStream = [_factory mediaStreamWithLabel:@"ARDAMS"]; - - RTCVideoTrack *localVideoTrack = [self createLocalVideoTrack]; + + RTCVideoTrack *localVideoTrack = [self createLocalVideoTrack:cameraPosition]; if (localVideoTrack) { [localStream addVideoTrack:localVideoTrack]; [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack]; @@ -514,117 +600,117 @@ - (RTCMediaStream *)createLocalMediaStream { } - (void)requestTURNServersWithURL:(NSURL *)requestURL - completionHandler:(void (^)(NSArray *turnServers))completionHandler { - NSParameterAssert([requestURL absoluteString].length); - NSMutableURLRequest *request = - [NSMutableURLRequest requestWithURL:requestURL]; - // We need to set origin because TURN provider whitelists requests based on - // origin. - [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"]; - [request addValue:self.serverHostUrl forHTTPHeaderField:@"origin"]; - [NSURLConnection sendAsyncRequest:request - completionHandler:^(NSURLResponse *response, - NSData *data, - NSError *error) { - NSArray *turnServers = [NSArray array]; - if (error) { - NSLog(@"Unable to get TURN server."); - completionHandler(turnServers); - return; - } - NSDictionary *dict = [NSDictionary dictionaryWithJSONData:data]; - turnServers = [RTCICEServer serversFromCEODJSONDictionary:dict]; - completionHandler(turnServers); - }]; + completionHandler:(void (^)(NSArray *turnServers))completionHandler { + NSParameterAssert([requestURL absoluteString].length); + NSMutableURLRequest *request = + [NSMutableURLRequest requestWithURL:requestURL]; + // We need to set origin because TURN provider whitelists requests based on + // origin. + [request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"]; + [request addValue:self.serverHostUrl forHTTPHeaderField:@"origin"]; + [NSURLConnection sendAsyncRequest:request + completionHandler:^(NSURLResponse *response, + NSData *data, + NSError *error) { + NSArray *turnServers = [NSArray array]; + if (error) { + NSLog(@"Unable to get TURN server."); + completionHandler(turnServers); + return; + } + NSDictionary *dict = [NSDictionary dictionaryWithJSONData:data]; + turnServers = [RTCICEServer serversFromCEODJSONDictionary:dict]; + completionHandler(turnServers); + }]; } #pragma mark - Room server methods - (void)registerWithRoomServerForRoomId:(NSString *)roomId - completionHandler:(void (^)(ARDRegisterResponse *))completionHandler { - NSString *urlString = - [NSString stringWithFormat:kARDRoomServerRegisterFormat, self.serverHostUrl, roomId]; - NSURL *roomURL = [NSURL URLWithString:urlString]; - NSLog(@"Registering with room server."); - __weak ARDAppClient *weakSelf = self; - [NSURLConnection sendAsyncPostToURL:roomURL - withData:nil - completionHandler:^(BOOL succeeded, NSData *data) { - ARDAppClient *strongSelf = weakSelf; - if (!succeeded) { - NSError *error = [self roomServerNetworkError]; - [strongSelf.delegate appClient:strongSelf didError:error]; - completionHandler(nil); - return; - } - ARDRegisterResponse *response = - [ARDRegisterResponse responseFromJSONData:data]; - completionHandler(response); - }]; + completionHandler:(void (^)(ARDRegisterResponse *))completionHandler { + NSString *urlString = + [NSString stringWithFormat:kARDRoomServerRegisterFormat, self.serverHostUrl, roomId]; + NSURL *roomURL = [NSURL URLWithString:urlString]; + NSLog(@"Registering with room server."); + __weak ARDAppClient *weakSelf = self; + [NSURLConnection sendAsyncPostToURL:roomURL + withData:nil + completionHandler:^(BOOL succeeded, NSData *data) { + ARDAppClient *strongSelf = weakSelf; + if (!succeeded) { + NSError *error = [self roomServerNetworkError]; + [strongSelf.delegate appClient:strongSelf didError:error]; + completionHandler(nil); + return; + } + ARDRegisterResponse *response = + [ARDRegisterResponse responseFromJSONData:data]; + completionHandler(response); + }]; } - (void)sendSignalingMessageToRoomServer:(ARDSignalingMessage *)message - completionHandler:(void (^)(ARDMessageResponse *))completionHandler { - NSData *data = [message JSONData]; - NSString *urlString = - [NSString stringWithFormat: - kARDRoomServerMessageFormat, self.serverHostUrl, _roomId, _clientId]; - NSURL *url = [NSURL URLWithString:urlString]; - NSLog(@"C->RS POST: %@", message); - __weak ARDAppClient *weakSelf = self; - [NSURLConnection sendAsyncPostToURL:url - withData:data - completionHandler:^(BOOL succeeded, NSData *data) { - ARDAppClient *strongSelf = weakSelf; - if (!succeeded) { - NSError *error = [self roomServerNetworkError]; - [strongSelf.delegate appClient:strongSelf didError:error]; - return; - } - ARDMessageResponse *response = - [ARDMessageResponse responseFromJSONData:data]; - NSError *error = nil; - switch (response.result) { - case kARDMessageResultTypeSuccess: - break; - case kARDMessageResultTypeUnknown: - error = - [[NSError alloc] initWithDomain:kARDAppClientErrorDomain - code:kARDAppClientErrorUnknown - userInfo:@{ - NSLocalizedDescriptionKey: @"Unknown error.", - }]; - 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; - }; - if (error) { - [strongSelf.delegate appClient:strongSelf didError:error]; - } - if (completionHandler) { - completionHandler(response); - } - }]; + completionHandler:(void (^)(ARDMessageResponse *))completionHandler { + NSData *data = [message JSONData]; + NSString *urlString = + [NSString stringWithFormat: + kARDRoomServerMessageFormat, self.serverHostUrl, _roomId, _clientId]; + NSURL *url = [NSURL URLWithString:urlString]; + NSLog(@"C->RS POST: %@", message); + __weak ARDAppClient *weakSelf = self; + [NSURLConnection sendAsyncPostToURL:url + withData:data + completionHandler:^(BOOL succeeded, NSData *data) { + ARDAppClient *strongSelf = weakSelf; + if (!succeeded) { + NSError *error = [self roomServerNetworkError]; + [strongSelf.delegate appClient:strongSelf didError:error]; + return; + } + ARDMessageResponse *response = + [ARDMessageResponse responseFromJSONData:data]; + NSError *error = nil; + switch (response.result) { + case kARDMessageResultTypeSuccess: + break; + case kARDMessageResultTypeUnknown: + error = + [[NSError alloc] initWithDomain:kARDAppClientErrorDomain + code:kARDAppClientErrorUnknown + userInfo:@{ + NSLocalizedDescriptionKey: @"Unknown error.", + }]; + 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; + }; + if (error) { + [strongSelf.delegate appClient:strongSelf didError:error]; + } + if (completionHandler) { + completionHandler(response); + } + }]; } - (void)unregisterWithRoomServer { - NSString *urlString = - [NSString stringWithFormat:kARDRoomServerByeFormat, self.serverHostUrl, _roomId, _clientId]; - NSURL *url = [NSURL URLWithString:urlString]; - NSLog(@"C->RS: BYE"); + NSString *urlString = + [NSString stringWithFormat:kARDRoomServerByeFormat, self.serverHostUrl, _roomId, _clientId]; + NSURL *url = [NSURL URLWithString:urlString]; + NSLog(@"C->RS: BYE"); //Make sure to do a POST [NSURLConnection sendAsyncPostToURL:url withData:nil completionHandler:^(BOOL succeeded, NSData *data) { if (succeeded) { @@ -636,76 +722,76 @@ - (void)unregisterWithRoomServer { } - (NSError *)roomServerNetworkError { - NSError *error = - [[NSError alloc] initWithDomain:kARDAppClientErrorDomain - code:kARDAppClientErrorNetwork - userInfo:@{ - NSLocalizedDescriptionKey: @"Room server network error", - }]; - return error; + NSError *error = + [[NSError alloc] initWithDomain:kARDAppClientErrorDomain + code:kARDAppClientErrorNetwork + userInfo:@{ + NSLocalizedDescriptionKey: @"Room server network error", + }]; + return error; } #pragma mark - Collider methods - (void)registerWithColliderIfReady { - if (!self.isRegisteredWithRoomServer) { - return; - } - // Open WebSocket connection. - _channel = - [[ARDWebSocketChannel alloc] initWithURL:_websocketURL - restURL:_websocketRestURL - delegate:self]; - [_channel registerForRoomId:_roomId clientId:_clientId]; + if (!self.isRegisteredWithRoomServer) { + return; + } + // Open WebSocket connection. + _channel = + [[ARDWebSocketChannel alloc] initWithURL:_websocketURL + restURL:_websocketRestURL + delegate:self]; + [_channel registerForRoomId:_roomId clientId:_clientId]; } - (void)sendSignalingMessageToCollider:(ARDSignalingMessage *)message { - NSData *data = [message JSONData]; - [_channel sendData:data]; + NSData *data = [message JSONData]; + [_channel sendData:data]; } #pragma mark - Defaults - (RTCMediaConstraints *)defaultMediaStreamConstraints { - RTCMediaConstraints* constraints = - [[RTCMediaConstraints alloc] - initWithMandatoryConstraints:nil - optionalConstraints:nil]; - return constraints; + RTCMediaConstraints* constraints = + [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:nil + optionalConstraints:nil]; + return constraints; } - (RTCMediaConstraints *)defaultAnswerConstraints { - return [self defaultOfferConstraints]; + return [self defaultOfferConstraints]; } - (RTCMediaConstraints *)defaultOfferConstraints { - NSArray *mandatoryConstraints = @[ - [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"], - [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"] - ]; - RTCMediaConstraints* constraints = - [[RTCMediaConstraints alloc] - initWithMandatoryConstraints:mandatoryConstraints - optionalConstraints:nil]; - return constraints; + NSArray *mandatoryConstraints = @[ + [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"], + [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo" value:@"true"] + ]; + RTCMediaConstraints* constraints = + [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:mandatoryConstraints + optionalConstraints:nil]; + return constraints; } - (RTCMediaConstraints *)defaultPeerConnectionConstraints { - NSArray *optionalConstraints = @[ - [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"] - ]; - RTCMediaConstraints* constraints = - [[RTCMediaConstraints alloc] - initWithMandatoryConstraints:nil - optionalConstraints:optionalConstraints]; - return constraints; + NSArray *optionalConstraints = @[ + [[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"] + ]; + RTCMediaConstraints* constraints = + [[RTCMediaConstraints alloc] + initWithMandatoryConstraints:nil + optionalConstraints:optionalConstraints]; + return constraints; } - (RTCICEServer *)defaultSTUNServer { - NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl]; - return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL - username:@"" - password:@""]; + NSURL *defaultSTUNServerURL = [NSURL URLWithString:kARDDefaultSTUNServerUrl]; + return [[RTCICEServer alloc] initWithURI:defaultSTUNServerURL + username:@"" + password:@""]; } #pragma mark - Audio mute/unmute @@ -729,14 +815,19 @@ - (void)unmuteAudioIn { #pragma mark - Video mute/unmute - (void)muteVideoIn { NSLog(@"video muted"); + isMuteVideoIn = YES; RTCMediaStream *localStream = _peerConnection.localStreams[0]; self.defaultVideoTrack = localStream.videoTracks[0]; [localStream removeVideoTrack:localStream.videoTracks[0]]; [_peerConnection removeStream:localStream]; [_peerConnection addStream:localStream]; + if (sessionCurrent) { + [sessionCurrent stopRunning]; + } } - (void)unmuteVideoIn { NSLog(@"video unmuted"); + isMuteVideoIn = NO; RTCMediaStream* localStream = _peerConnection.localStreams[0]; [localStream addVideoTrack:self.defaultVideoTrack]; [_peerConnection removeStream:localStream]; @@ -744,54 +835,20 @@ - (void)unmuteVideoIn { } #pragma mark - swap camera -- (RTCVideoTrack *)createLocalVideoTrackBackCamera { - RTCVideoTrack *localVideoTrack = nil; -#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE - //AVCaptureDevicePositionFront - NSString *cameraID = nil; - for (AVCaptureDevice *captureDevice in - [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { - if (captureDevice.position == AVCaptureDevicePositionBack) { - cameraID = [captureDevice localizedName]; - break; - } - } - NSAssert(cameraID, @"Unable to get the back camera id"); - - RTCVideoCapturer *capturer = [RTCVideoCapturer capturerWithDeviceName:cameraID]; - RTCMediaConstraints *mediaConstraints = [self defaultMediaStreamConstraints]; - RTCVideoSource *videoSource = [_factory videoSourceWithCapturer:capturer constraints:mediaConstraints]; - localVideoTrack = [_factory videoTrackWithID:@"ARDAMSv0" source:videoSource]; -#endif - return localVideoTrack; -} -- (void)swapCameraToFront{ - RTCMediaStream *localStream = _peerConnection.localStreams[0]; - [localStream removeVideoTrack:localStream.videoTracks[0]]; - - RTCVideoTrack *localVideoTrack = [self createLocalVideoTrack]; - if (localVideoTrack) { - [localStream addVideoTrack:localVideoTrack]; - [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack]; - } - [_peerConnection removeStream:localStream]; - [_peerConnection addStream:localStream]; +- (void)swapCameraToFront{ + [self swapCamera:AVCaptureDevicePositionFront]; } -- (void)swapCameraToBack{ - RTCMediaStream *localStream = _peerConnection.localStreams[0]; - [localStream removeVideoTrack:localStream.videoTracks[0]]; - - RTCVideoTrack *localVideoTrack = [self createLocalVideoTrackBackCamera]; - - if (localVideoTrack) { - [localStream addVideoTrack:localVideoTrack]; - [_delegate appClient:self didReceiveLocalVideoTrack:localVideoTrack]; - } - [_peerConnection removeStream:localStream]; - [_peerConnection addStream:localStream]; + +- (void)swapCameraToBack { + [self swapCamera:AVCaptureDevicePositionBack]; } + - (void)swapCamera:(AVCaptureDevicePosition)camera { + cameraPosition = camera; + [self cleanUpAllMediaStreams]; + } + #pragma mark - enable/disable speaker - (void)enableSpeaker { diff --git a/Pods/Pods.xcodeproj/xcshareddata/xcschemes/AppRTC.xcscheme b/Pods/Pods.xcodeproj/xcshareddata/xcschemes/AppRTC.xcscheme index 9606095..5e8a33c 100644 --- a/Pods/Pods.xcodeproj/xcshareddata/xcschemes/AppRTC.xcscheme +++ b/Pods/Pods.xcodeproj/xcshareddata/xcschemes/AppRTC.xcscheme @@ -1,60 +1,39 @@ - + + buildForRunning = "YES"> +======= BuildableIdentifier = 'primary' BlueprintIdentifier = 'C87D0851BA029AC5972A3840' BlueprintName = 'AppRTC' ReferencedContainer = 'container:Pods.xcodeproj' BuildableName = 'libAppRTC.a'> +>>>>>>> ISBX/master - - - - - - - - - - - - - + + + + +