/* * 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