1/* 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#import "ARDVideoCallViewController.h" 12 13#import "sdk/objc/api/peerconnection/RTCMediaConstraints.h" 14#import "sdk/objc/base/RTCLogging.h" 15#import "sdk/objc/components/audio/RTCAudioSession.h" 16#import "sdk/objc/components/capturer/RTCCameraVideoCapturer.h" 17#import "sdk/objc/helpers/RTCDispatcher.h" 18 19#import "ARDAppClient.h" 20#import "ARDCaptureController.h" 21#import "ARDFileCaptureController.h" 22#import "ARDSettingsModel.h" 23#import "ARDVideoCallView.h" 24 25@interface ARDVideoCallViewController () <ARDAppClientDelegate, 26 ARDVideoCallViewDelegate, 27 RTC_OBJC_TYPE (RTCAudioSessionDelegate)> 28@property(nonatomic, strong) RTC_OBJC_TYPE(RTCVideoTrack) * remoteVideoTrack; 29@property(nonatomic, readonly) ARDVideoCallView *videoCallView; 30@property(nonatomic, assign) AVAudioSessionPortOverride portOverride; 31@end 32 33@implementation ARDVideoCallViewController { 34 ARDAppClient *_client; 35 RTC_OBJC_TYPE(RTCVideoTrack) * _remoteVideoTrack; 36 ARDCaptureController *_captureController; 37 ARDFileCaptureController *_fileCaptureController NS_AVAILABLE_IOS(10); 38} 39 40@synthesize videoCallView = _videoCallView; 41@synthesize remoteVideoTrack = _remoteVideoTrack; 42@synthesize delegate = _delegate; 43@synthesize portOverride = _portOverride; 44 45- (instancetype)initForRoom:(NSString *)room 46 isLoopback:(BOOL)isLoopback 47 delegate:(id<ARDVideoCallViewControllerDelegate>)delegate { 48 if (self = [super init]) { 49 ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; 50 _delegate = delegate; 51 52 _client = [[ARDAppClient alloc] initWithDelegate:self]; 53 [_client connectToRoomWithId:room settings:settingsModel isLoopback:isLoopback]; 54 } 55 return self; 56} 57 58- (void)loadView { 59 _videoCallView = [[ARDVideoCallView alloc] initWithFrame:CGRectZero]; 60 _videoCallView.delegate = self; 61 _videoCallView.statusLabel.text = 62 [self statusTextForState:RTCIceConnectionStateNew]; 63 self.view = _videoCallView; 64 65 RTC_OBJC_TYPE(RTCAudioSession) *session = [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; 66 [session addDelegate:self]; 67} 68 69- (UIInterfaceOrientationMask)supportedInterfaceOrientations { 70 return UIInterfaceOrientationMaskAll; 71} 72 73#pragma mark - ARDAppClientDelegate 74 75- (void)appClient:(ARDAppClient *)client 76 didChangeState:(ARDAppClientState)state { 77 switch (state) { 78 case kARDAppClientStateConnected: 79 RTCLog(@"Client connected."); 80 break; 81 case kARDAppClientStateConnecting: 82 RTCLog(@"Client connecting."); 83 break; 84 case kARDAppClientStateDisconnected: 85 RTCLog(@"Client disconnected."); 86 [self hangup]; 87 break; 88 } 89} 90 91- (void)appClient:(ARDAppClient *)client 92 didChangeConnectionState:(RTCIceConnectionState)state { 93 RTCLog(@"ICE state changed: %ld", (long)state); 94 __weak ARDVideoCallViewController *weakSelf = self; 95 dispatch_async(dispatch_get_main_queue(), ^{ 96 ARDVideoCallViewController *strongSelf = weakSelf; 97 strongSelf.videoCallView.statusLabel.text = 98 [strongSelf statusTextForState:state]; 99 }); 100} 101 102- (void)appClient:(ARDAppClient *)client 103 didCreateLocalCapturer:(RTC_OBJC_TYPE(RTCCameraVideoCapturer) *)localCapturer { 104 _videoCallView.localVideoView.captureSession = localCapturer.captureSession; 105 ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; 106 _captureController = 107 [[ARDCaptureController alloc] initWithCapturer:localCapturer settings:settingsModel]; 108 [_captureController startCapture]; 109} 110 111- (void)appClient:(ARDAppClient *)client 112 didCreateLocalFileCapturer:(RTC_OBJC_TYPE(RTCFileVideoCapturer) *)fileCapturer { 113#if defined(__IPHONE_11_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) 114 if (@available(iOS 10, *)) { 115 _fileCaptureController = [[ARDFileCaptureController alloc] initWithCapturer:fileCapturer]; 116 [_fileCaptureController startCapture]; 117 } 118#endif 119} 120 121- (void)appClient:(ARDAppClient *)client 122 didReceiveLocalVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)localVideoTrack { 123} 124 125- (void)appClient:(ARDAppClient *)client 126 didReceiveRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack { 127 self.remoteVideoTrack = remoteVideoTrack; 128 __weak ARDVideoCallViewController *weakSelf = self; 129 dispatch_async(dispatch_get_main_queue(), ^{ 130 ARDVideoCallViewController *strongSelf = weakSelf; 131 strongSelf.videoCallView.statusLabel.hidden = YES; 132 }); 133} 134 135- (void)appClient:(ARDAppClient *)client didGetStats:(RTC_OBJC_TYPE(RTCStatisticsReport) *)stats { 136 _videoCallView.statsView.stats = stats; 137 [_videoCallView setNeedsLayout]; 138} 139 140- (void)appClient:(ARDAppClient *)client 141 didError:(NSError *)error { 142 NSString *message = 143 [NSString stringWithFormat:@"%@", error.localizedDescription]; 144 [self hangup]; 145 [self showAlertWithMessage:message]; 146} 147 148#pragma mark - ARDVideoCallViewDelegate 149 150- (void)videoCallViewDidHangup:(ARDVideoCallView *)view { 151 [self hangup]; 152} 153 154- (void)videoCallView:(ARDVideoCallView *)view 155 shouldSwitchCameraWithCompletion:(void (^)(NSError *))completion { 156 [_captureController switchCamera:completion]; 157} 158 159- (void)videoCallView:(ARDVideoCallView *)view 160 shouldChangeRouteWithCompletion:(void (^)(void))completion { 161 NSParameterAssert(completion); 162 AVAudioSessionPortOverride override = AVAudioSessionPortOverrideNone; 163 if (_portOverride == AVAudioSessionPortOverrideNone) { 164 override = AVAudioSessionPortOverrideSpeaker; 165 } 166 [RTC_OBJC_TYPE(RTCDispatcher) dispatchAsyncOnType:RTCDispatcherTypeAudioSession 167 block:^{ 168 RTC_OBJC_TYPE(RTCAudioSession) *session = 169 [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; 170 [session lockForConfiguration]; 171 NSError *error = nil; 172 if ([session overrideOutputAudioPort:override 173 error:&error]) { 174 self.portOverride = override; 175 } else { 176 RTCLogError(@"Error overriding output port: %@", 177 error.localizedDescription); 178 } 179 [session unlockForConfiguration]; 180 completion(); 181 }]; 182} 183 184- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view { 185 _client.shouldGetStats = YES; 186 _videoCallView.statsView.hidden = NO; 187} 188 189#pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate) 190 191- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession 192 didDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches { 193 RTCLog(@"Audio session detected glitch, total: %lld", totalNumberOfGlitches); 194} 195 196#pragma mark - Private 197 198- (void)setRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack { 199 if (_remoteVideoTrack == remoteVideoTrack) { 200 return; 201 } 202 [_remoteVideoTrack removeRenderer:_videoCallView.remoteVideoView]; 203 _remoteVideoTrack = nil; 204 [_videoCallView.remoteVideoView renderFrame:nil]; 205 _remoteVideoTrack = remoteVideoTrack; 206 [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView]; 207} 208 209- (void)hangup { 210 self.remoteVideoTrack = nil; 211 _videoCallView.localVideoView.captureSession = nil; 212 [_captureController stopCapture]; 213 _captureController = nil; 214 [_fileCaptureController stopCapture]; 215 _fileCaptureController = nil; 216 [_client disconnect]; 217 [_delegate viewControllerDidFinish:self]; 218} 219 220- (NSString *)statusTextForState:(RTCIceConnectionState)state { 221 switch (state) { 222 case RTCIceConnectionStateNew: 223 case RTCIceConnectionStateChecking: 224 return @"Connecting..."; 225 case RTCIceConnectionStateConnected: 226 case RTCIceConnectionStateCompleted: 227 case RTCIceConnectionStateFailed: 228 case RTCIceConnectionStateDisconnected: 229 case RTCIceConnectionStateClosed: 230 case RTCIceConnectionStateCount: 231 return nil; 232 } 233} 234 235- (void)showAlertWithMessage:(NSString*)message { 236 UIAlertController *alert = 237 [UIAlertController alertControllerWithTitle:nil 238 message:message 239 preferredStyle:UIAlertControllerStyleAlert]; 240 241 UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK" 242 style:UIAlertActionStyleDefault 243 handler:^(UIAlertAction *action){ 244 }]; 245 246 [alert addAction:defaultAction]; 247 [self presentViewController:alert animated:YES completion:nil]; 248} 249 250@end 251