Index: webrtc/modules/audio_device/ios/audio_device_ios.mm |
diff --git a/webrtc/modules/audio_device/ios/audio_device_ios.mm b/webrtc/modules/audio_device/ios/audio_device_ios.mm |
index f6dee5b3cf82ff93862a35669be1878c200e577c..a0ea2a0f455378c68ecce9c79a317646f9224e5c 100644 |
--- a/webrtc/modules/audio_device/ios/audio_device_ios.mm |
+++ b/webrtc/modules/audio_device/ios/audio_device_ios.mm |
@@ -25,17 +25,9 @@ |
#include "webrtc/modules/audio_device/fine_audio_buffer.h" |
#include "webrtc/modules/utility/include/helpers_ios.h" |
-namespace webrtc { |
- |
-// Protects |g_audio_session_users|. |
-static rtc::GlobalLockPod g_lock; |
+#import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" |
-// Counts number of users (=instances of this object) who needs an active |
-// audio session. This variable is used to ensure that we only activate an audio |
-// session for the first user and deactivate it for the last. |
-// Member is static to ensure that the value is counted for all instances |
-// and not per instance. |
-static int g_audio_session_users GUARDED_BY(g_lock) = 0; |
+namespace webrtc { |
#define LOGI() LOG(LS_INFO) << "AudioDeviceIOS::" |
@@ -97,10 +89,10 @@ using ios::CheckAndLogError; |
// Verifies that the current audio session supports input audio and that the |
// required category and mode are enabled. |
-static bool VerifyAudioSession(AVAudioSession* session) { |
+static bool VerifyAudioSession(RTCAudioSession* session) { |
LOG(LS_INFO) << "VerifyAudioSession"; |
// Ensure that the device currently supports audio input. |
- if (!session.isInputAvailable) { |
+ if (!session.inputAvailable) { |
LOG(LS_ERROR) << "No audio input path is available!"; |
return false; |
} |
@@ -121,93 +113,89 @@ static bool VerifyAudioSession(AVAudioSession* session) { |
// Activates an audio session suitable for full duplex VoIP sessions when |
// |activate| is true. Also sets the preferred sample rate and IO buffer |
// duration. Deactivates an active audio session if |activate| is set to false. |
-static bool ActivateAudioSession(AVAudioSession* session, bool activate) |
- EXCLUSIVE_LOCKS_REQUIRED(g_lock) { |
+static bool ActivateAudioSession(RTCAudioSession* session, bool activate) { |
LOG(LS_INFO) << "ActivateAudioSession(" << activate << ")"; |
- @autoreleasepool { |
- NSError* error = nil; |
- BOOL success = NO; |
- |
- if (!activate) { |
- // Deactivate the audio session using an extra option and then return. |
- // AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation is used to |
- // ensure that other audio sessions that were interrupted by our session |
- // can return to their active state. It is recommended for VoIP apps to |
- // use this option. |
- success = [session |
- setActive:NO |
- withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation |
- error:&error]; |
- return CheckAndLogError(success, error); |
- } |
- // Go ahead and active our own audio session since |activate| is true. |
- // Use a category which supports simultaneous recording and playback. |
- // By default, using this category implies that our app’s audio is |
- // nonmixable, hence activating the session will interrupt any other |
- // audio sessions which are also nonmixable. |
- if (session.category != AVAudioSessionCategoryPlayAndRecord) { |
- error = nil; |
- success = [session setCategory:AVAudioSessionCategoryPlayAndRecord |
- withOptions:AVAudioSessionCategoryOptionAllowBluetooth |
- error:&error]; |
- RTC_DCHECK(CheckAndLogError(success, error)); |
- } |
- |
- // Specify mode for two-way voice communication (e.g. VoIP). |
- if (session.mode != AVAudioSessionModeVoiceChat) { |
- error = nil; |
- success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; |
- RTC_DCHECK(CheckAndLogError(success, error)); |
- } |
+ NSError* error = nil; |
+ BOOL success = NO; |
+ |
+ [session lockForConfiguration]; |
+ if (!activate) { |
+ success = [session setActive:NO |
+ error:&error]; |
+ [session unlockForConfiguration]; |
+ return CheckAndLogError(success, error); |
+ } |
- // Set the session's sample rate or the hardware sample rate. |
- // It is essential that we use the same sample rate as stream format |
- // to ensure that the I/O unit does not have to do sample rate conversion. |
+ // Go ahead and active our own audio session since |activate| is true. |
+ // Use a category which supports simultaneous recording and playback. |
+ // By default, using this category implies that our app’s audio is |
+ // nonmixable, hence activating the session will interrupt any other |
+ // audio sessions which are also nonmixable. |
+ if (session.category != AVAudioSessionCategoryPlayAndRecord) { |
error = nil; |
- success = |
- [session setPreferredSampleRate:kPreferredSampleRate error:&error]; |
+ success = [session setCategory:AVAudioSessionCategoryPlayAndRecord |
+ withOptions:AVAudioSessionCategoryOptionAllowBluetooth |
+ error:&error]; |
RTC_DCHECK(CheckAndLogError(success, error)); |
+ } |
- // Set the preferred audio I/O buffer duration, in seconds. |
+ // Specify mode for two-way voice communication (e.g. VoIP). |
+ if (session.mode != AVAudioSessionModeVoiceChat) { |
error = nil; |
- success = [session setPreferredIOBufferDuration:kPreferredIOBufferDuration |
- error:&error]; |
+ success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; |
RTC_DCHECK(CheckAndLogError(success, error)); |
+ } |
- // Activate the audio session. Activation can fail if another active audio |
- // session (e.g. phone call) has higher priority than ours. |
- error = nil; |
- success = [session setActive:YES error:&error]; |
- if (!CheckAndLogError(success, error)) { |
- return false; |
- } |
- |
- // Ensure that the active audio session has the correct category and mode. |
- if (!VerifyAudioSession(session)) { |
- LOG(LS_ERROR) << "Failed to verify audio session category and mode"; |
- return false; |
- } |
+ // Set the session's sample rate or the hardware sample rate. |
+ // It is essential that we use the same sample rate as stream format |
+ // to ensure that the I/O unit does not have to do sample rate conversion. |
+ error = nil; |
+ success = |
+ [session setPreferredSampleRate:kPreferredSampleRate error:&error]; |
+ RTC_DCHECK(CheckAndLogError(success, error)); |
+ |
+ // Set the preferred audio I/O buffer duration, in seconds. |
+ error = nil; |
+ success = [session setPreferredIOBufferDuration:kPreferredIOBufferDuration |
+ error:&error]; |
+ RTC_DCHECK(CheckAndLogError(success, error)); |
+ |
+ // Activate the audio session. Activation can fail if another active audio |
+ // session (e.g. phone call) has higher priority than ours. |
+ error = nil; |
+ success = [session setActive:YES error:&error]; |
+ if (!CheckAndLogError(success, error)) { |
+ [session unlockForConfiguration]; |
+ return false; |
+ } |
- // 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. |
- // We try to use mono in both directions to save resources and format |
- // conversions in the audio unit. Some devices does only support stereo; |
- // e.g. wired headset on iPhone 6. |
- // TODO(henrika): add support for stereo if needed. |
- error = nil; |
- success = |
- [session setPreferredInputNumberOfChannels:kPreferredNumberOfChannels |
- error:&error]; |
- RTC_DCHECK(CheckAndLogError(success, error)); |
- error = nil; |
- success = |
- [session setPreferredOutputNumberOfChannels:kPreferredNumberOfChannels |
- error:&error]; |
- RTC_DCHECK(CheckAndLogError(success, error)); |
- return true; |
+ // Ensure that the active audio session has the correct category and mode. |
+ if (!VerifyAudioSession(session)) { |
+ LOG(LS_ERROR) << "Failed to verify audio session category and mode"; |
+ [session unlockForConfiguration]; |
+ return false; |
} |
+ |
+ // 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. |
+ // We try to use mono in both directions to save resources and format |
+ // conversions in the audio unit. Some devices does only support stereo; |
+ // e.g. wired headset on iPhone 6. |
+ // TODO(henrika): add support for stereo if needed. |
+ error = nil; |
+ success = |
+ [session setPreferredInputNumberOfChannels:kPreferredNumberOfChannels |
+ error:&error]; |
+ RTC_DCHECK(CheckAndLogError(success, error)); |
+ error = nil; |
+ success = |
+ [session setPreferredOutputNumberOfChannels:kPreferredNumberOfChannels |
+ error:&error]; |
+ RTC_DCHECK(CheckAndLogError(success, error)); |
+ [session unlockForConfiguration]; |
+ return true; |
} |
// An application can create more than one ADM and start audio streaming |
@@ -215,24 +203,8 @@ static bool ActivateAudioSession(AVAudioSession* session, bool activate) |
// session once (for the first one) and deactivate it once (for the last). |
static bool ActivateAudioSession() { |
LOGI() << "ActivateAudioSession"; |
- rtc::GlobalLockScope ls(&g_lock); |
- if (g_audio_session_users == 0) { |
- // The system provides an audio session object upon launch of an |
- // application. However, we must initialize the session in order to |
- // handle interruptions. Implicit initialization occurs when obtaining |
- // a reference to the AVAudioSession object. |
- AVAudioSession* session = [AVAudioSession sharedInstance]; |
- // Try to activate the audio session and ask for a set of preferred audio |
- // parameters. |
- if (!ActivateAudioSession(session, true)) { |
- LOG(LS_ERROR) << "Failed to activate the audio session"; |
- return false; |
- } |
- LOG(LS_INFO) << "The audio session is now activated"; |
- } |
- ++g_audio_session_users; |
- LOG(LS_INFO) << "Number of audio session users: " << g_audio_session_users; |
- return true; |
+ RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
+ return ActivateAudioSession(session, true); |
} |
// If more than one object is using the audio session, ensure that only the |
@@ -240,18 +212,8 @@ static bool ActivateAudioSession() { |
// only as needed and deactivate it when you are not using audio". |
static bool DeactivateAudioSession() { |
LOGI() << "DeactivateAudioSession"; |
- rtc::GlobalLockScope ls(&g_lock); |
- if (g_audio_session_users == 1) { |
- AVAudioSession* session = [AVAudioSession sharedInstance]; |
- if (!ActivateAudioSession(session, false)) { |
- LOG(LS_ERROR) << "Failed to deactivate the audio session"; |
- return false; |
- } |
- LOG(LS_INFO) << "Our audio session is now deactivated"; |
- } |
- --g_audio_session_users; |
- LOG(LS_INFO) << "Number of audio session users: " << g_audio_session_users; |
- return true; |
+ RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
+ return ActivateAudioSession(session, false); |
} |
#if !defined(NDEBUG) |
@@ -344,13 +306,6 @@ int32_t AudioDeviceIOS::Terminate() { |
StopPlayout(); |
StopRecording(); |
initialized_ = false; |
- { |
- rtc::GlobalLockScope ls(&g_lock); |
- if (g_audio_session_users != 0) { |
- LOG(LS_WARNING) << "Object is destructed with an active audio session"; |
- } |
- RTC_DCHECK_GE(g_audio_session_users, 0); |
- } |
return 0; |
} |
@@ -456,7 +411,8 @@ int32_t AudioDeviceIOS::StopRecording() { |
int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { |
LOGI() << "SetLoudspeakerStatus(" << enable << ")"; |
- AVAudioSession* session = [AVAudioSession sharedInstance]; |
+ RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
+ [session lockForConfiguration]; |
NSString* category = session.category; |
AVAudioSessionCategoryOptions options = session.categoryOptions; |
// Respect old category options if category is |
@@ -476,12 +432,13 @@ int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { |
withOptions:options |
error:&error]; |
ios::CheckAndLogError(success, error); |
+ [session unlockForConfiguration]; |
return (error == nil) ? 0 : -1; |
} |
int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const { |
LOGI() << "GetLoudspeakerStatus"; |
- AVAudioSession* session = [AVAudioSession sharedInstance]; |
+ RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
AVAudioSessionCategoryOptions options = session.categoryOptions; |
enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; |
return 0; |
@@ -618,7 +575,7 @@ void AudioDeviceIOS::RegisterNotificationObservers() { |
// Only restart audio for a valid route change and if the |
// session sample rate has changed. |
- AVAudioSession* session = [AVAudioSession sharedInstance]; |
+ RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
const double session_sample_rate = session.sampleRate; |
LOG(LS_INFO) << "session sample rate: " << session_sample_rate; |
if (playout_parameters_.sample_rate() != session_sample_rate) { |
@@ -672,7 +629,7 @@ void AudioDeviceIOS::UnregisterNotificationObservers() { |
void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { |
LOGI() << "SetupAudioBuffersForActiveAudioSession"; |
// Verify the current values once the audio session has been activated. |
- AVAudioSession* session = [AVAudioSession sharedInstance]; |
+ RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
LOG(LS_INFO) << " sample rate: " << session.sampleRate; |
LOG(LS_INFO) << " IO buffer duration: " << session.IOBufferDuration; |
LOG(LS_INFO) << " output channels: " << session.outputNumberOfChannels; |
@@ -954,7 +911,7 @@ bool AudioDeviceIOS::InitPlayOrRecord() { |
} |
// Ensure that the active audio session has the correct category and mode. |
- AVAudioSession* session = [AVAudioSession sharedInstance]; |
+ RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
if (!VerifyAudioSession(session)) { |
DeactivateAudioSession(); |
LOG(LS_ERROR) << "Failed to verify audio session category and mode"; |