blob: 83320b62f08d52a5f4bf5c23a78a30b7d8dcfb54 [file] [log] [blame]
/*
* Copyright 2016 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h"
#import "webrtc/base/objc/RTCLogging.h"
#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession+Private.h"
#import "webrtc/modules/audio_device/ios/objc/RTCAudioSessionConfiguration.h"
@implementation RTCAudioSession (Configuration)
- (BOOL)setConfiguration:(RTCAudioSessionConfiguration *)configuration
active:(BOOL)active
error:(NSError **)outError {
if (![self checkLock:outError]) {
return NO;
}
// Provide an error even if there isn't one so we can log it. We will not
// return immediately on error in this function and instead try to set
// everything we can.
NSError *error = nil;
if (self.category != configuration.category ||
self.categoryOptions != configuration.categoryOptions) {
NSError *categoryError = nil;
if (![self setCategory:configuration.category
withOptions:configuration.categoryOptions
error:&categoryError]) {
RTCLogError(@"Failed to set category: %@",
categoryError.localizedDescription);
error = categoryError;
}
}
if (self.mode != configuration.mode) {
NSError *modeError = nil;
if (![self setMode:configuration.mode error:&modeError]) {
RTCLogError(@"Failed to set mode: %@",
modeError.localizedDescription);
error = modeError;
}
}
// self.sampleRate is accurate only if the audio session is active.
if (!self.isActive || self.sampleRate != configuration.sampleRate) {
NSError *sampleRateError = nil;
if (![self setPreferredSampleRate:configuration.sampleRate
error:&sampleRateError]) {
RTCLogError(@"Failed to set preferred sample rate: %@",
sampleRateError.localizedDescription);
error = sampleRateError;
}
}
// self.IOBufferDuration is accurate only if the audio session is active.
if (!self.isActive ||
self.IOBufferDuration != configuration.ioBufferDuration) {
NSError *bufferDurationError = nil;
if (![self setPreferredIOBufferDuration:configuration.ioBufferDuration
error:&bufferDurationError]) {
RTCLogError(@"Failed to set preferred IO buffer duration: %@",
bufferDurationError.localizedDescription);
error = bufferDurationError;
}
}
NSError *activeError = nil;
if (![self setActive:active error:&activeError]) {
RTCLogError(@"Failed to setActive to %d: %@",
active, activeError.localizedDescription);
error = activeError;
}
if (self.isActive) {
// Try to set the preferred number of hardware audio channels. These calls
// must be done after setting the audio session’s category and mode and
// activating the session.
NSInteger inputNumberOfChannels = configuration.inputNumberOfChannels;
if (self.inputNumberOfChannels != inputNumberOfChannels) {
NSError *inputChannelsError = nil;
if (![self setPreferredInputNumberOfChannels:inputNumberOfChannels
error:&inputChannelsError]) {
RTCLogError(@"Failed to set preferred input number of channels: %@",
inputChannelsError.localizedDescription);
error = inputChannelsError;
}
}
NSInteger outputNumberOfChannels = configuration.outputNumberOfChannels;
if (self.outputNumberOfChannels != outputNumberOfChannels) {
NSError *outputChannelsError = nil;
if (![self setPreferredOutputNumberOfChannels:outputNumberOfChannels
error:&outputChannelsError]) {
RTCLogError(@"Failed to set preferred output number of channels: %@",
outputChannelsError.localizedDescription);
error = outputChannelsError;
}
}
}
if (outError) {
*outError = error;
}
return error == nil;
}
- (BOOL)configureWebRTCSession:(NSError **)outError {
if (![self checkLock:outError]) {
return NO;
}
RTCLog(@"Configuring audio session for WebRTC.");
// Provide an error even if there isn't one so we can log it.
BOOL hasSucceeded = YES;
NSError *error = nil;
RTCAudioSessionConfiguration *currentConfig =
[RTCAudioSessionConfiguration currentConfiguration];
RTCAudioSessionConfiguration *webRTCConfig =
[RTCAudioSessionConfiguration webRTCConfiguration];
if (![self setConfiguration:webRTCConfig active:YES error:&error]) {
RTCLogError(@"Failed to set WebRTC audio configuration: %@",
error.localizedDescription);
// Attempt to restore previous state.
[self setConfiguration:currentConfig active:NO error:nil];
hasSucceeded = NO;
} else if (![self isConfiguredForWebRTC]) {
// Ensure that the active audio session has the correct category and mode.
// This should never happen - this means that we succeeded earlier but
// somehow the settings didn't apply.
RTCLogError(@"Failed to configure audio session.");
// Attempt to restore previous state.
[self setConfiguration:currentConfig active:NO error:nil];
error =
[[NSError alloc] initWithDomain:kRTCAudioSessionErrorDomain
code:kRTCAudioSessionErrorConfiguration
userInfo:nil];
hasSucceeded = NO;
}
if (outError) {
*outError = error;
}
return hasSucceeded;
}
#pragma mark - Private
- (BOOL)isConfiguredForWebRTC {
// Ensure that the device currently supports audio input.
if (!self.inputAvailable) {
RTCLogError(@"No audio input path is available!");
return NO;
}
// Only check a minimal list of requirements for whether we have
// what we want.
RTCAudioSessionConfiguration *currentConfig =
[RTCAudioSessionConfiguration currentConfiguration];
RTCAudioSessionConfiguration *webRTCConfig =
[RTCAudioSessionConfiguration webRTCConfiguration];
if (![currentConfig.category isEqualToString:webRTCConfig.category]) {
RTCLog(@"Current category %@ does not match %@",
currentConfig.category,
webRTCConfig.category);
return NO;
}
if (![currentConfig.mode isEqualToString:webRTCConfig.mode]) {
RTCLog(@"Current mode %@ does not match %@",
currentConfig.mode,
webRTCConfig.mode);
return NO;
}
return YES;
}
@end