ARDMainViewController.m (10055B)
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 "ARDMainViewController.h" 12 13 #import <AVFoundation/AVFoundation.h> 14 15 #import "sdk/objc/base/RTCLogging.h" 16 #import "sdk/objc/components/audio/RTCAudioSession.h" 17 #import "sdk/objc/components/audio/RTCAudioSessionConfiguration.h" 18 #import "sdk/objc/helpers/RTCDispatcher.h" 19 20 #import "ARDAppClient.h" 21 #import "ARDMainView.h" 22 #import "ARDSettingsModel.h" 23 #import "ARDSettingsViewController.h" 24 #import "ARDVideoCallViewController.h" 25 26 static NSString *const barButtonImageString = @"ic_settings_black_24dp.png"; 27 28 // Launch argument to be passed to indicate that the app should start loopback 29 // immediatly 30 static NSString *const loopbackLaunchProcessArgument = @"loopback"; 31 32 @interface ARDMainViewController () <ARDMainViewDelegate, 33 ARDVideoCallViewControllerDelegate, 34 RTC_OBJC_TYPE (RTCAudioSessionDelegate)> 35 @property(nonatomic, strong) ARDMainView *mainView; 36 @property(nonatomic, strong) AVAudioPlayer *audioPlayer; 37 @end 38 39 @implementation ARDMainViewController { 40 BOOL _useManualAudio; 41 } 42 43 @synthesize mainView = _mainView; 44 @synthesize audioPlayer = _audioPlayer; 45 46 - (void)viewDidLoad { 47 [super viewDidLoad]; 48 if ([[[NSProcessInfo processInfo] arguments] 49 containsObject:loopbackLaunchProcessArgument]) { 50 [self mainView:nil didInputRoom:@"" isLoopback:YES]; 51 } 52 } 53 54 - (void)loadView { 55 self.title = @"AppRTC Mobile"; 56 _mainView = [[ARDMainView alloc] initWithFrame:CGRectZero]; 57 _mainView.delegate = self; 58 self.view = _mainView; 59 [self addSettingsBarButton]; 60 61 RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *webRTCConfig = 62 [RTC_OBJC_TYPE(RTCAudioSessionConfiguration) webRTCConfiguration]; 63 webRTCConfig.categoryOptions = webRTCConfig.categoryOptions | 64 AVAudioSessionCategoryOptionDefaultToSpeaker; 65 [RTC_OBJC_TYPE(RTCAudioSessionConfiguration) 66 setWebRTCConfiguration:webRTCConfig]; 67 68 RTC_OBJC_TYPE(RTCAudioSession) *session = 69 [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; 70 [session addDelegate:self]; 71 72 [self configureAudioSession]; 73 [self setupAudioPlayer]; 74 } 75 76 - (void)addSettingsBarButton { 77 UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] 78 initWithImage:[UIImage imageNamed:barButtonImageString] 79 style:UIBarButtonItemStylePlain 80 target:self 81 action:@selector(showSettings:)]; 82 self.navigationItem.rightBarButtonItem = settingsButton; 83 } 84 85 + (NSString *)loopbackRoomString { 86 NSString *loopbackRoomString = 87 [[NSUUID UUID].UUIDString stringByReplacingOccurrencesOfString:@"-" 88 withString:@""]; 89 return loopbackRoomString; 90 } 91 92 #pragma mark - ARDMainViewDelegate 93 94 - (void)mainView:(ARDMainView *)mainView 95 didInputRoom:(NSString *)room 96 isLoopback:(BOOL)isLoopback { 97 if (!room.length) { 98 if (isLoopback) { 99 // If this is a loopback call, allow a generated room name. 100 room = [[self class] loopbackRoomString]; 101 } else { 102 [self showAlertWithMessage:@"Missing room name."]; 103 return; 104 } 105 } 106 // Trim whitespaces. 107 NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet]; 108 NSString *trimmedRoom = [room stringByTrimmingCharactersInSet:whitespaceSet]; 109 110 // Check that room name is valid. 111 NSError *error = nil; 112 NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive; 113 NSRegularExpression *regex = 114 [NSRegularExpression regularExpressionWithPattern:@"\\w+" 115 options:options 116 error:&error]; 117 if (error) { 118 [self showAlertWithMessage:error.localizedDescription]; 119 return; 120 } 121 NSRange matchRange = 122 [regex rangeOfFirstMatchInString:trimmedRoom 123 options:0 124 range:NSMakeRange(0, trimmedRoom.length)]; 125 if (matchRange.location == NSNotFound || 126 matchRange.length != trimmedRoom.length) { 127 [self showAlertWithMessage:@"Invalid room name."]; 128 return; 129 } 130 131 ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init]; 132 133 RTC_OBJC_TYPE(RTCAudioSession) *session = 134 [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; 135 session.useManualAudio = 136 [settingsModel currentUseManualAudioConfigSettingFromStore]; 137 session.isAudioEnabled = NO; 138 139 // Kick off the video call. 140 ARDVideoCallViewController *videoCallViewController = 141 [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom 142 isLoopback:isLoopback 143 delegate:self]; 144 videoCallViewController.modalTransitionStyle = 145 UIModalTransitionStyleCrossDissolve; 146 videoCallViewController.modalPresentationStyle = 147 UIModalPresentationFullScreen; 148 [self presentViewController:videoCallViewController 149 animated:YES 150 completion:nil]; 151 } 152 153 - (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView { 154 if (mainView.isAudioLoopPlaying) { 155 [_audioPlayer stop]; 156 } else { 157 [_audioPlayer play]; 158 } 159 mainView.isAudioLoopPlaying = _audioPlayer.playing; 160 } 161 162 #pragma mark - ARDVideoCallViewControllerDelegate 163 164 - (void)viewControllerDidFinish:(ARDVideoCallViewController *)viewController { 165 if (![viewController isBeingDismissed]) { 166 RTCLog(@"Dismissing VC"); 167 [self dismissViewControllerAnimated:YES 168 completion:^{ 169 [self restartAudioPlayerIfNeeded]; 170 }]; 171 } 172 RTC_OBJC_TYPE(RTCAudioSession) *session = 173 [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; 174 session.isAudioEnabled = NO; 175 } 176 177 #pragma mark - RTC_OBJC_TYPE(RTCAudioSessionDelegate) 178 179 - (void)audioSessionDidStartPlayOrRecord: 180 (RTC_OBJC_TYPE(RTCAudioSession) *)session { 181 // Stop playback on main queue and then configure WebRTC. 182 [RTC_OBJC_TYPE(RTCDispatcher) 183 dispatchAsyncOnType:RTCDispatcherTypeMain 184 block:^{ 185 if (self.mainView.isAudioLoopPlaying) { 186 RTCLog(@"Stopping audio loop due to WebRTC start."); 187 [self.audioPlayer stop]; 188 } 189 RTCLog(@"Setting isAudioEnabled to YES."); 190 session.isAudioEnabled = YES; 191 }]; 192 } 193 194 - (void)audioSessionDidStopPlayOrRecord: 195 (RTC_OBJC_TYPE(RTCAudioSession) *)session { 196 // WebRTC is done with the audio session. Restart playback. 197 [RTC_OBJC_TYPE(RTCDispatcher) 198 dispatchAsyncOnType:RTCDispatcherTypeMain 199 block:^{ 200 RTCLog(@"audioSessionDidStopPlayOrRecord"); 201 [self restartAudioPlayerIfNeeded]; 202 }]; 203 } 204 205 #pragma mark - Private 206 - (void)showSettings:(id)sender { 207 ARDSettingsViewController *settingsController = 208 [[ARDSettingsViewController alloc] 209 initWithStyle:UITableViewStyleGrouped 210 settingsModel:[[ARDSettingsModel alloc] init]]; 211 212 UINavigationController *navigationController = [[UINavigationController alloc] 213 initWithRootViewController:settingsController]; 214 [self presentViewControllerAsModal:navigationController]; 215 } 216 217 - (void)presentViewControllerAsModal:(UIViewController *)viewController { 218 [self presentViewController:viewController animated:YES completion:nil]; 219 } 220 221 - (void)configureAudioSession { 222 RTC_OBJC_TYPE(RTCAudioSessionConfiguration) *configuration = 223 [[RTC_OBJC_TYPE(RTCAudioSessionConfiguration) alloc] init]; 224 configuration.category = AVAudioSessionCategoryAmbient; 225 configuration.categoryOptions = AVAudioSessionCategoryOptionDuckOthers; 226 configuration.mode = AVAudioSessionModeDefault; 227 228 RTC_OBJC_TYPE(RTCAudioSession) *session = 229 [RTC_OBJC_TYPE(RTCAudioSession) sharedInstance]; 230 [session lockForConfiguration]; 231 BOOL hasSucceeded = NO; 232 NSError *error = nil; 233 if (session.isActive) { 234 hasSucceeded = [session setConfiguration:configuration error:&error]; 235 } else { 236 hasSucceeded = [session setConfiguration:configuration 237 active:YES 238 error:&error]; 239 } 240 if (!hasSucceeded) { 241 RTCLogError(@"Error setting configuration: %@", error.localizedDescription); 242 } 243 [session unlockForConfiguration]; 244 } 245 246 - (void)setupAudioPlayer { 247 NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:@"mozart" 248 ofType:@"mp3"]; 249 NSURL *audioFileURL = [NSURL URLWithString:audioFilePath]; 250 _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL 251 error:nil]; 252 _audioPlayer.numberOfLoops = -1; 253 _audioPlayer.volume = 1.0; 254 [_audioPlayer prepareToPlay]; 255 } 256 257 - (void)restartAudioPlayerIfNeeded { 258 [self configureAudioSession]; 259 if (_mainView.isAudioLoopPlaying && !self.presentedViewController) { 260 RTCLog(@"Starting audio loop due to WebRTC end."); 261 [_audioPlayer play]; 262 } 263 } 264 265 - (void)showAlertWithMessage:(NSString *)message { 266 UIAlertController *alert = 267 [UIAlertController alertControllerWithTitle:nil 268 message:message 269 preferredStyle:UIAlertControllerStyleAlert]; 270 271 UIAlertAction *defaultAction = 272 [UIAlertAction actionWithTitle:@"OK" 273 style:UIAlertActionStyleDefault 274 handler:^(UIAlertAction *action){ 275 }]; 276 277 [alert addAction:defaultAction]; 278 [self presentViewController:alert animated:YES completion:nil]; 279 } 280 281 @end