| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 #import "ARDMainViewController.h" | 11 #import "ARDMainViewController.h" |
| 12 | 12 |
| 13 #import <AVFoundation/AVFoundation.h> | 13 #import <AVFoundation/AVFoundation.h> |
| 14 | 14 |
| 15 #import "WebRTC/RTCDispatcher.h" | 15 #import "WebRTC/RTCDispatcher.h" |
| 16 #import "WebRTC/RTCLogging.h" | 16 #import "WebRTC/RTCLogging.h" |
| 17 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" | 17 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" |
| 18 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h" | 18 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h" |
| 19 | 19 |
| 20 #import "ARDAppClient.h" | 20 #import "ARDAppClient.h" |
| 21 #import "ARDMainView.h" | 21 #import "ARDMainView.h" |
| 22 #import "ARDVideoCallViewController.h" | 22 #import "ARDVideoCallViewController.h" |
| 23 | 23 |
| 24 @interface ARDMainViewController () < | 24 @interface ARDMainViewController () < |
| 25 ARDMainViewDelegate, | 25 ARDMainViewDelegate, |
| 26 ARDVideoCallViewControllerDelegate, |
| 26 RTCAudioSessionDelegate> | 27 RTCAudioSessionDelegate> |
| 27 @end | 28 @end |
| 28 | 29 |
| 29 @implementation ARDMainViewController { | 30 @implementation ARDMainViewController { |
| 30 ARDMainView *_mainView; | 31 ARDMainView *_mainView; |
| 31 AVAudioPlayer *_audioPlayer; | 32 AVAudioPlayer *_audioPlayer; |
| 32 BOOL _shouldDelayAudioConfig; | 33 BOOL _useManualAudio; |
| 33 } | 34 } |
| 34 | 35 |
| 35 - (void)loadView { | 36 - (void)loadView { |
| 36 _mainView = [[ARDMainView alloc] initWithFrame:CGRectZero]; | 37 _mainView = [[ARDMainView alloc] initWithFrame:CGRectZero]; |
| 37 _mainView.delegate = self; | 38 _mainView.delegate = self; |
| 38 self.view = _mainView; | 39 self.view = _mainView; |
| 39 | 40 |
| 40 [self setupAudioSession]; | 41 RTCAudioSessionConfiguration *webRTCConfig = |
| 42 [RTCAudioSessionConfiguration webRTCConfiguration]; |
| 43 webRTCConfig.categoryOptions = webRTCConfig.categoryOptions | |
| 44 AVAudioSessionCategoryOptionDefaultToSpeaker; |
| 45 [RTCAudioSessionConfiguration setWebRTCConfiguration:webRTCConfig]; |
| 46 |
| 47 RTCAudioSession *session = [RTCAudioSession sharedInstance]; |
| 48 [session addDelegate:self]; |
| 49 |
| 50 [self configureAudioSession]; |
| 41 [self setupAudioPlayer]; | 51 [self setupAudioPlayer]; |
| 42 } | 52 } |
| 43 | 53 |
| 44 #pragma mark - ARDMainViewDelegate | 54 #pragma mark - ARDMainViewDelegate |
| 45 | 55 |
| 46 - (void)mainView:(ARDMainView *)mainView | 56 - (void)mainView:(ARDMainView *)mainView |
| 47 didInputRoom:(NSString *)room | 57 didInputRoom:(NSString *)room |
| 48 isLoopback:(BOOL)isLoopback | 58 isLoopback:(BOOL)isLoopback |
| 49 isAudioOnly:(BOOL)isAudioOnly | 59 isAudioOnly:(BOOL)isAudioOnly |
| 50 shouldDelayAudioConfig:(BOOL)shouldDelayAudioConfig { | 60 useManualAudio:(BOOL)useManualAudio { |
| 51 if (!room.length) { | 61 if (!room.length) { |
| 52 [self showAlertWithMessage:@"Missing room name."]; | 62 [self showAlertWithMessage:@"Missing room name."]; |
| 53 return; | 63 return; |
| 54 } | 64 } |
| 55 // Trim whitespaces. | 65 // Trim whitespaces. |
| 56 NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet]; | 66 NSCharacterSet *whitespaceSet = [NSCharacterSet whitespaceCharacterSet]; |
| 57 NSString *trimmedRoom = [room stringByTrimmingCharactersInSet:whitespaceSet]; | 67 NSString *trimmedRoom = [room stringByTrimmingCharactersInSet:whitespaceSet]; |
| 58 | 68 |
| 59 // Check that room name is valid. | 69 // Check that room name is valid. |
| 60 NSError *error = nil; | 70 NSError *error = nil; |
| 61 NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive; | 71 NSRegularExpressionOptions options = NSRegularExpressionCaseInsensitive; |
| 62 NSRegularExpression *regex = | 72 NSRegularExpression *regex = |
| 63 [NSRegularExpression regularExpressionWithPattern:@"\\w+" | 73 [NSRegularExpression regularExpressionWithPattern:@"\\w+" |
| 64 options:options | 74 options:options |
| 65 error:&error]; | 75 error:&error]; |
| 66 if (error) { | 76 if (error) { |
| 67 [self showAlertWithMessage:error.localizedDescription]; | 77 [self showAlertWithMessage:error.localizedDescription]; |
| 68 return; | 78 return; |
| 69 } | 79 } |
| 70 NSRange matchRange = | 80 NSRange matchRange = |
| 71 [regex rangeOfFirstMatchInString:trimmedRoom | 81 [regex rangeOfFirstMatchInString:trimmedRoom |
| 72 options:0 | 82 options:0 |
| 73 range:NSMakeRange(0, trimmedRoom.length)]; | 83 range:NSMakeRange(0, trimmedRoom.length)]; |
| 74 if (matchRange.location == NSNotFound || | 84 if (matchRange.location == NSNotFound || |
| 75 matchRange.length != trimmedRoom.length) { | 85 matchRange.length != trimmedRoom.length) { |
| 76 [self showAlertWithMessage:@"Invalid room name."]; | 86 [self showAlertWithMessage:@"Invalid room name."]; |
| 77 return; | 87 return; |
| 78 } | 88 } |
| 79 | 89 |
| 80 _shouldDelayAudioConfig = shouldDelayAudioConfig; | |
| 81 RTCAudioSession *session = [RTCAudioSession sharedInstance]; | 90 RTCAudioSession *session = [RTCAudioSession sharedInstance]; |
| 82 session.shouldDelayAudioConfiguration = _shouldDelayAudioConfig; | 91 session.useManualAudio = useManualAudio; |
| 92 session.isAudioEnabled = NO; |
| 83 | 93 |
| 84 // Kick off the video call. | 94 // Kick off the video call. |
| 85 ARDVideoCallViewController *videoCallViewController = | 95 ARDVideoCallViewController *videoCallViewController = |
| 86 [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom | 96 [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom |
| 87 isLoopback:isLoopback | 97 isLoopback:isLoopback |
| 88 isAudioOnly:isAudioOnly]; | 98 isAudioOnly:isAudioOnly |
| 99 delegate:self]; |
| 89 videoCallViewController.modalTransitionStyle = | 100 videoCallViewController.modalTransitionStyle = |
| 90 UIModalTransitionStyleCrossDissolve; | 101 UIModalTransitionStyleCrossDissolve; |
| 91 [self presentViewController:videoCallViewController | 102 [self presentViewController:videoCallViewController |
| 92 animated:YES | 103 animated:YES |
| 93 completion:nil]; | 104 completion:nil]; |
| 94 } | 105 } |
| 95 | 106 |
| 96 - (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView { | 107 - (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView { |
| 97 if (mainView.isAudioLoopPlaying) { | 108 if (mainView.isAudioLoopPlaying) { |
| 98 [_audioPlayer stop]; | 109 [_audioPlayer stop]; |
| 99 } else { | 110 } else { |
| 100 [_audioPlayer play]; | 111 [_audioPlayer play]; |
| 101 } | 112 } |
| 102 mainView.isAudioLoopPlaying = _audioPlayer.playing; | 113 mainView.isAudioLoopPlaying = _audioPlayer.playing; |
| 103 } | 114 } |
| 104 | 115 |
| 116 #pragma mark - ARDVideoCallViewControllerDelegate |
| 117 |
| 118 - (void)viewControllerDidFinish:(ARDVideoCallViewController *)viewController { |
| 119 if (![viewController isBeingDismissed]) { |
| 120 RTCLog(@"Dismissing VC"); |
| 121 [self dismissViewControllerAnimated:YES completion:^{ |
| 122 [self restartAudioPlayerIfNeeded]; |
| 123 }]; |
| 124 } |
| 125 RTCAudioSession *session = [RTCAudioSession sharedInstance]; |
| 126 session.isAudioEnabled = NO; |
| 127 } |
| 128 |
| 105 #pragma mark - RTCAudioSessionDelegate | 129 #pragma mark - RTCAudioSessionDelegate |
| 106 | 130 |
| 107 - (void)audioSessionShouldConfigure:(RTCAudioSession *)session { | 131 - (void)audioSessionDidStartPlayOrRecord:(RTCAudioSession *)session { |
| 108 // Won't get called unless audio config is delayed. | |
| 109 // Stop playback on main queue and then configure WebRTC. | 132 // Stop playback on main queue and then configure WebRTC. |
| 110 [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain | 133 [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain |
| 111 block:^{ | 134 block:^{ |
| 112 if (_mainView.isAudioLoopPlaying) { | 135 if (_mainView.isAudioLoopPlaying) { |
| 113 RTCLog(@"Stopping audio loop due to WebRTC start."); | 136 RTCLog(@"Stopping audio loop due to WebRTC start."); |
| 114 [_audioPlayer stop]; | 137 [_audioPlayer stop]; |
| 115 } | 138 } |
| 116 // TODO(tkchin): Shouldn't lock on main queue. Figure out better way to | 139 RTCLog(@"Setting isAudioEnabled to YES."); |
| 117 // check audio loop state. | 140 session.isAudioEnabled = YES; |
| 118 [session lockForConfiguration]; | |
| 119 [session configureWebRTCSession:nil]; | |
| 120 [session unlockForConfiguration]; | |
| 121 }]; | 141 }]; |
| 122 } | 142 } |
| 123 | 143 |
| 124 - (void)audioSessionShouldUnconfigure:(RTCAudioSession *)session { | 144 - (void)audioSessionDidStopPlayOrRecord:(RTCAudioSession *)session { |
| 125 // Won't get called unless audio config is delayed. | |
| 126 [session lockForConfiguration]; | |
| 127 [session unconfigureWebRTCSession:nil]; | |
| 128 [session unlockForConfiguration]; | |
| 129 } | |
| 130 | |
| 131 - (void)audioSessionDidUnconfigure:(RTCAudioSession *)session { | |
| 132 // WebRTC is done with the audio session. Restart playback. | 145 // WebRTC is done with the audio session. Restart playback. |
| 133 [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain | 146 [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain |
| 134 block:^{ | 147 block:^{ |
| 135 if (_mainView.isAudioLoopPlaying) { | 148 RTCLog(@"audioSessionDidStopPlayOrRecord"); |
| 136 RTCLog(@"Starting audio loop due to WebRTC end."); | 149 [self restartAudioPlayerIfNeeded]; |
| 137 [_audioPlayer play]; | |
| 138 } | |
| 139 }]; | 150 }]; |
| 140 } | 151 } |
| 141 | 152 |
| 142 #pragma mark - Private | 153 #pragma mark - Private |
| 143 | 154 |
| 144 - (void)setupAudioSession { | 155 - (void)configureAudioSession { |
| 145 RTCAudioSessionConfiguration *configuration = | 156 RTCAudioSessionConfiguration *configuration = |
| 146 [[RTCAudioSessionConfiguration alloc] init]; | 157 [[RTCAudioSessionConfiguration alloc] init]; |
| 147 configuration.category = AVAudioSessionCategoryAmbient; | 158 configuration.category = AVAudioSessionCategoryAmbient; |
| 148 configuration.categoryOptions = AVAudioSessionCategoryOptionDuckOthers; | 159 configuration.categoryOptions = AVAudioSessionCategoryOptionDuckOthers; |
| 149 configuration.mode = AVAudioSessionModeDefault; | 160 configuration.mode = AVAudioSessionModeDefault; |
| 150 | 161 |
| 151 RTCAudioSession *session = [RTCAudioSession sharedInstance]; | 162 RTCAudioSession *session = [RTCAudioSession sharedInstance]; |
| 152 [session addDelegate:self]; | |
| 153 [session lockForConfiguration]; | 163 [session lockForConfiguration]; |
| 164 BOOL hasSucceeded = NO; |
| 154 NSError *error = nil; | 165 NSError *error = nil; |
| 155 if (![session setConfiguration:configuration active:YES error:&error]) { | 166 if (session.isActive) { |
| 167 hasSucceeded = [session setConfiguration:configuration error:&error]; |
| 168 } else { |
| 169 hasSucceeded = [session setConfiguration:configuration |
| 170 active:YES |
| 171 error:&error]; |
| 172 } |
| 173 if (!hasSucceeded) { |
| 156 RTCLogError(@"Error setting configuration: %@", error.localizedDescription); | 174 RTCLogError(@"Error setting configuration: %@", error.localizedDescription); |
| 157 } | 175 } |
| 158 [session unlockForConfiguration]; | 176 [session unlockForConfiguration]; |
| 159 } | 177 } |
| 160 | 178 |
| 161 - (void)setupAudioPlayer { | 179 - (void)setupAudioPlayer { |
| 162 NSString *audioFilePath = | 180 NSString *audioFilePath = |
| 163 [[NSBundle mainBundle] pathForResource:@"mozart" ofType:@"mp3"]; | 181 [[NSBundle mainBundle] pathForResource:@"mozart" ofType:@"mp3"]; |
| 164 NSURL *audioFileURL = [NSURL URLWithString:audioFilePath]; | 182 NSURL *audioFileURL = [NSURL URLWithString:audioFilePath]; |
| 165 _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL | 183 _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL |
| 166 error:nil]; | 184 error:nil]; |
| 167 _audioPlayer.numberOfLoops = -1; | 185 _audioPlayer.numberOfLoops = -1; |
| 168 _audioPlayer.volume = 1.0; | 186 _audioPlayer.volume = 1.0; |
| 169 [_audioPlayer prepareToPlay]; | 187 [_audioPlayer prepareToPlay]; |
| 170 } | 188 } |
| 171 | 189 |
| 190 - (void)restartAudioPlayerIfNeeded { |
| 191 if (_mainView.isAudioLoopPlaying && !self.presentedViewController) { |
| 192 RTCLog(@"Starting audio loop due to WebRTC end."); |
| 193 [self configureAudioSession]; |
| 194 [_audioPlayer play]; |
| 195 } |
| 196 } |
| 197 |
| 172 - (void)showAlertWithMessage:(NSString*)message { | 198 - (void)showAlertWithMessage:(NSString*)message { |
| 173 UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil | 199 UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil |
| 174 message:message | 200 message:message |
| 175 delegate:nil | 201 delegate:nil |
| 176 cancelButtonTitle:@"OK" | 202 cancelButtonTitle:@"OK" |
| 177 otherButtonTitles:nil]; | 203 otherButtonTitles:nil]; |
| 178 [alertView show]; | 204 [alertView show]; |
| 179 } | 205 } |
| 180 | 206 |
| 181 @end | 207 @end |
| OLD | NEW |