Support delayed AudioUnit initialization.
Applications can choose to decide when to give up control of the
AVAudioSession to WebRTC. Otherwise, behavior should be
unchanged.
Adds a toggle to AppRTCDemo so developers can see the different
paths.
BUG=
R=haysc@webrtc.org
Review URL: https://codereview.webrtc.org/1822543002 .
Cr-Commit-Position: refs/heads/master@{#12080}
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m
index 7dda2fc..b169ca4 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDAppDelegate.m
@@ -43,12 +43,6 @@
return YES;
}
-- (void)applicationWillResignActive:(UIApplication *)application {
- ARDMainViewController *viewController =
- (ARDMainViewController *)_window.rootViewController;
- [viewController applicationWillResignActive:application];
-}
-
- (void)applicationWillTerminate:(UIApplication *)application {
RTCShutdownInternalTracer();
RTCCleanupSSL();
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
index a77cc8c..d3a583d 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.h
@@ -15,9 +15,12 @@
@protocol ARDMainViewDelegate <NSObject>
- (void)mainView:(ARDMainView *)mainView
- didInputRoom:(NSString *)room
- isLoopback:(BOOL)isLoopback
- isAudioOnly:(BOOL)isAudioOnly;
+ didInputRoom:(NSString *)room
+ isLoopback:(BOOL)isLoopback
+ isAudioOnly:(BOOL)isAudioOnly
+ shouldDelayAudioConfig:(BOOL)shouldDelayAudioConfig;
+
+- (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView;
@end
@@ -26,5 +29,7 @@
@interface ARDMainView : UIView
@property(nonatomic, weak) id<ARDMainViewDelegate> delegate;
+// Updates the audio loop button as needed.
+@property(nonatomic, assign) BOOL isAudioLoopPlaying;
@end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
index 6f52657..b264763 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m
@@ -10,8 +10,6 @@
#import "ARDMainView.h"
-#import <AVFoundation/AVFoundation.h>
-
#import "UIImage+ARDUtilities.h"
// TODO(tkchin): retrieve status bar height dynamically.
@@ -124,24 +122,17 @@
UILabel *_audioOnlyLabel;
UISwitch *_loopbackSwitch;
UILabel *_loopbackLabel;
+ UISwitch *_audioConfigDelaySwitch;
+ UILabel *_audioConfigDelayLabel;
UIButton *_startCallButton;
UIButton *_audioLoopButton;
- AVAudioPlayer *_audioPlayer;
}
@synthesize delegate = _delegate;
+@synthesize isAudioLoopPlaying = _isAudioLoopPlaying;
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
- NSString *audioFilePath =
- [[NSBundle mainBundle] pathForResource:@"mozart" ofType:@"mp3"];
- NSURL *audioFileURL = [NSURL URLWithString:audioFilePath];
- _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL
- error:nil];
- _audioPlayer.numberOfLoops = -1;
- _audioPlayer.volume = 1.0;
- [_audioPlayer prepareToPlay];
-
_appLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_appLabel.text = @"AppRTCDemo";
_appLabel.font = [UIFont fontWithName:@"Roboto" size:34];
@@ -184,6 +175,18 @@
[_loopbackLabel sizeToFit];
[self addSubview:_loopbackLabel];
+ _audioConfigDelaySwitch = [[UISwitch alloc] initWithFrame:CGRectZero];
+ [_audioConfigDelaySwitch sizeToFit];
+ _audioConfigDelaySwitch.on = YES;
+ [self addSubview:_audioConfigDelaySwitch];
+
+ _audioConfigDelayLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ _audioConfigDelayLabel.text = @"Delay audio config";
+ _audioConfigDelayLabel.font = controlFont;
+ _audioConfigDelayLabel.textColor = controlFontColor;
+ [_audioConfigDelayLabel sizeToFit];
+ [self addSubview:_audioConfigDelayLabel];
+
_startCallButton = [UIButton buttonWithType:UIButtonTypeSystem];
_startCallButton.backgroundColor = [UIColor blueColor];
_startCallButton.layer.cornerRadius = 10;
@@ -223,6 +226,14 @@
return self;
}
+- (void)setIsAudioLoopPlaying:(BOOL)isAudioLoopPlaying {
+ if (_isAudioLoopPlaying == isAudioLoopPlaying) {
+ return;
+ }
+ _isAudioLoopPlaying = isAudioLoopPlaying;
+ [self updateAudioLoopButton];
+}
+
- (void)layoutSubviews {
CGRect bounds = self.bounds;
CGFloat roomTextWidth = bounds.size.width - 2 * kRoomTextFieldMargin;
@@ -264,8 +275,22 @@
_loopbackLabel.center = CGPointMake(loopbackModeLabelCenterX,
CGRectGetMidY(loopbackModeRect));
+ CGFloat audioConfigDelayTop =
+ CGRectGetMaxY(_loopbackSwitch.frame) + kCallControlMargin;
+ CGRect audioConfigDelayRect =
+ CGRectMake(kCallControlMargin * 3,
+ audioConfigDelayTop,
+ _audioConfigDelaySwitch.frame.size.width,
+ _audioConfigDelaySwitch.frame.size.height);
+ _audioConfigDelaySwitch.frame = audioConfigDelayRect;
+ CGFloat audioConfigDelayLabelCenterX = CGRectGetMaxX(audioConfigDelayRect) +
+ kCallControlMargin + _audioConfigDelayLabel.frame.size.width / 2;
+ _audioConfigDelayLabel.center =
+ CGPointMake(audioConfigDelayLabelCenterX,
+ CGRectGetMidY(audioConfigDelayRect));
+
CGFloat audioLoopTop =
- CGRectGetMaxY(loopbackModeRect) + kCallControlMargin * 3;
+ CGRectGetMaxY(audioConfigDelayRect) + kCallControlMargin * 3;
_audioLoopButton.frame = CGRectMake(kCallControlMargin,
audioLoopTop,
_audioLoopButton.frame.size.width,
@@ -282,7 +307,7 @@
#pragma mark - Private
- (void)updateAudioLoopButton {
- if (_audioPlayer.playing) {
+ if (_isAudioLoopPlaying) {
_audioLoopButton.backgroundColor = [UIColor redColor];
[_audioLoopButton setTitle:@"Stop sound"
forState:UIControlStateNormal];
@@ -296,12 +321,7 @@
}
- (void)onToggleAudioLoop:(id)sender {
- if (_audioPlayer.playing) {
- [_audioPlayer stop];
- } else {
- [_audioPlayer play];
- }
- [self updateAudioLoopButton];
+ [_delegate mainViewDidToggleAudioLoop:self];
}
- (void)onStartCall:(id)sender {
@@ -312,9 +332,10 @@
}
room = [room stringByReplacingOccurrencesOfString:@"-" withString:@""];
[_delegate mainView:self
- didInputRoom:room
- isLoopback:_loopbackSwitch.isOn
- isAudioOnly:_audioOnlySwitch.isOn];
+ didInputRoom:room
+ isLoopback:_loopbackSwitch.isOn
+ isAudioOnly:_audioOnlySwitch.isOn
+ shouldDelayAudioConfig:_audioConfigDelaySwitch.isOn];
}
@end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h
index cc38170..e5c92dd 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.h
@@ -11,7 +11,4 @@
#import <UIKit/UIKit.h>
@interface ARDMainViewController : UIViewController
-
-- (void)applicationWillResignActive:(UIApplication *)application;
-
@end
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
index 8de6f6a1..a634952 100644
--- a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainViewController.m
@@ -10,32 +10,44 @@
#import "ARDMainViewController.h"
+#import <AVFoundation/AVFoundation.h>
+
+#import "webrtc/base/objc/RTCDispatcher.h"
+#import "webrtc/base/objc/RTCLogging.h"
+#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
+#import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h"
+
#import "ARDAppClient.h"
#import "ARDMainView.h"
#import "ARDVideoCallViewController.h"
-@interface ARDMainViewController () <ARDMainViewDelegate>
+@interface ARDMainViewController () <
+ ARDMainViewDelegate,
+ RTCAudioSessionDelegate>
@end
-@implementation ARDMainViewController
-
-- (void)loadView {
- ARDMainView *mainView = [[ARDMainView alloc] initWithFrame:CGRectZero];
- mainView.delegate = self;
- self.view = mainView;
+@implementation ARDMainViewController {
+ ARDMainView *_mainView;
+ AVAudioPlayer *_audioPlayer;
+ BOOL _shouldDelayAudioConfig;
}
-- (void)applicationWillResignActive:(UIApplication *)application {
- // Terminate any calls when we aren't active.
- [self dismissViewControllerAnimated:NO completion:nil];
+- (void)loadView {
+ _mainView = [[ARDMainView alloc] initWithFrame:CGRectZero];
+ _mainView.delegate = self;
+ self.view = _mainView;
+
+ [self setupAudioSession];
+ [self setupAudioPlayer];
}
#pragma mark - ARDMainViewDelegate
- (void)mainView:(ARDMainView *)mainView
- didInputRoom:(NSString *)room
- isLoopback:(BOOL)isLoopback
- isAudioOnly:(BOOL)isAudioOnly {
+ didInputRoom:(NSString *)room
+ isLoopback:(BOOL)isLoopback
+ isAudioOnly:(BOOL)isAudioOnly
+ shouldDelayAudioConfig:(BOOL)shouldDelayAudioConfig {
if (!room.length) {
[self showAlertWithMessage:@"Missing room name."];
return;
@@ -65,6 +77,10 @@
return;
}
+ _shouldDelayAudioConfig = shouldDelayAudioConfig;
+ RTCAudioSession *session = [RTCAudioSession sharedInstance];
+ session.shouldDelayAudioConfiguration = _shouldDelayAudioConfig;
+
// Kick off the video call.
ARDVideoCallViewController *videoCallViewController =
[[ARDVideoCallViewController alloc] initForRoom:trimmedRoom
@@ -77,8 +93,82 @@
completion:nil];
}
+- (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView {
+ if (mainView.isAudioLoopPlaying) {
+ [_audioPlayer stop];
+ } else {
+ [_audioPlayer play];
+ }
+ mainView.isAudioLoopPlaying = _audioPlayer.playing;
+}
+
+#pragma mark - RTCAudioSessionDelegate
+
+- (void)audioSessionShouldConfigure:(RTCAudioSession *)session {
+ // Won't get called unless audio config is delayed.
+ // Stop playback on main queue and then configure WebRTC.
+ [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain
+ block:^{
+ if (_mainView.isAudioLoopPlaying) {
+ RTCLog(@"Stopping audio loop due to WebRTC start.");
+ [_audioPlayer stop];
+ }
+ // TODO(tkchin): Shouldn't lock on main queue. Figure out better way to
+ // check audio loop state.
+ [session lockForConfiguration];
+ [session configureWebRTCSession:nil];
+ [session unlockForConfiguration];
+ }];
+}
+
+- (void)audioSessionShouldUnconfigure:(RTCAudioSession *)session {
+ // Won't get called unless audio config is delayed.
+ [session lockForConfiguration];
+ [session unconfigureWebRTCSession:nil];
+ [session unlockForConfiguration];
+}
+
+- (void)audioSessionDidUnconfigure:(RTCAudioSession *)session {
+ // WebRTC is done with the audio session. Restart playback.
+ [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeMain
+ block:^{
+ if (_mainView.isAudioLoopPlaying) {
+ RTCLog(@"Starting audio loop due to WebRTC end.");
+ [_audioPlayer play];
+ }
+ }];
+}
+
#pragma mark - Private
+- (void)setupAudioSession {
+ RTCAudioSessionConfiguration *configuration =
+ [[RTCAudioSessionConfiguration alloc] init];
+ configuration.category = AVAudioSessionCategoryAmbient;
+ configuration.categoryOptions = AVAudioSessionCategoryOptionDuckOthers;
+ configuration.mode = AVAudioSessionModeDefault;
+
+ RTCAudioSession *session = [RTCAudioSession sharedInstance];
+ [session addDelegate:self];
+ [session lockForConfiguration];
+ NSError *error = nil;
+ if (![session setConfiguration:configuration active:YES error:&error]) {
+ RTCLogError(@"Error setting configuration: %@", error.localizedDescription);
+ }
+ [session unlockForConfiguration];
+}
+
+- (void)setupAudioPlayer {
+ NSString *audioFilePath =
+ [[NSBundle mainBundle] pathForResource:@"mozart" ofType:@"mp3"];
+ NSURL *audioFileURL = [NSURL URLWithString:audioFilePath];
+ _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL
+ error:nil];
+ _audioPlayer.numberOfLoops = -1;
+ _audioPlayer.volume = 1.0;
+ [_audioPlayer prepareToPlay];
+}
+
- (void)showAlertWithMessage:(NSString*)message {
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil
message:message