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