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 <WebRTC/RTCAudioSession.h> 14#import <WebRTC/RTCCameraVideoCapturer.h> 15#import <WebRTC/RTCDispatcher.h> 16#import <WebRTC/RTCLogging.h> 17#import <WebRTC/RTCMediaConstraints.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 136 didGetStats:(NSArray *)stats { 137 _videoCallView.statsView.stats = stats; 138 [_videoCallView setNeedsLayout]; 139} 140 141- (void)appClient:(ARDAppClient *)client 142 didError:(NSError *)error { 143 NSString *message = 144 [NSString stringWithFormat:@"%@", error.localizedDescription]; 145 [self hangup]; 146 [self showAlertWithMessage:message]; 147} 148 149#pragma mark - ARDVideoCallViewDelegate 150 151- (void)videoCallViewDidHangup:(ARDVideoCallView *)view { 152 [self hangup]; 153} 154 155- (void)videoCallViewDidSwitchCamera:(ARDVideoCallView *)view { 156 // TODO(tkchin): Rate limit this so you can't tap continously on it. 157 // Probably through an animation. 158 [_captureController switchCamera]; 159} 160 161- (void)videoCallViewDidChangeRoute:(ARDVideoCallView *)view { 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 }]; 181} 182 183- (void)videoCallViewDidEnableStats:(ARDVideoCallView *)view { 184 _client.shouldGetStats = YES; 185 _videoCallView.statsView.hidden = NO; 186} 187 188#pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate) 189 190- (void)audioSession:(RTC_OBJC_TYPE(RTCAudioSession) *)audioSession 191 didDetectPlayoutGlitch:(int64_t)totalNumberOfGlitches { 192 RTCLog(@"Audio session detected glitch, total: %lld", totalNumberOfGlitches); 193} 194 195#pragma mark - Private 196 197- (void)setRemoteVideoTrack:(RTC_OBJC_TYPE(RTCVideoTrack) *)remoteVideoTrack { 198 if (_remoteVideoTrack == remoteVideoTrack) { 199 return; 200 } 201 [_remoteVideoTrack removeRenderer:_videoCallView.remoteVideoView]; 202 _remoteVideoTrack = nil; 203 [_videoCallView.remoteVideoView renderFrame:nil]; 204 _remoteVideoTrack = remoteVideoTrack; 205 [_remoteVideoTrack addRenderer:_videoCallView.remoteVideoView]; 206} 207 208- (void)hangup { 209 self.remoteVideoTrack = nil; 210 _videoCallView.localVideoView.captureSession = nil; 211 [_captureController stopCapture]; 212 _captureController = nil; 213 [_fileCaptureController stopCapture]; 214 _fileCaptureController = nil; 215 [_client disconnect]; 216 [_delegate viewControllerDidFinish:self]; 217} 218 219- (NSString *)statusTextForState:(RTCIceConnectionState)state { 220 switch (state) { 221 case RTCIceConnectionStateNew: 222 case RTCIceConnectionStateChecking: 223 return @"Connecting..."; 224 case RTCIceConnectionStateConnected: 225 case RTCIceConnectionStateCompleted: 226 case RTCIceConnectionStateFailed: 227 case RTCIceConnectionStateDisconnected: 228 case RTCIceConnectionStateClosed: 229 case RTCIceConnectionStateCount: 230 return nil; 231 } 232} 233 234- (void)showAlertWithMessage:(NSString*)message { 235 UIAlertController *alert = 236 [UIAlertController alertControllerWithTitle:nil 237 message:message 238 preferredStyle:UIAlertControllerStyleAlert]; 239 240 UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK" 241 style:UIAlertActionStyleDefault 242 handler:^(UIAlertAction *action){ 243 }]; 244 245 [alert addAction:defaultAction]; 246 [self presentViewController:alert animated:YES completion:nil]; 247} 248 249@end 250