| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 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 #if !defined(__has_feature) || !__has_feature(objc_arc) | 11 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 12 #error "This file requires ARC support." | 12 #error "This file requires ARC support." |
| 13 #endif | 13 #endif |
| 14 | 14 |
| 15 #import <AVFoundation/AVFoundation.h> | 15 #import <AVFoundation/AVFoundation.h> |
| 16 #import <Foundation/Foundation.h> | 16 #import <Foundation/Foundation.h> |
| 17 | 17 |
| 18 #include "webrtc/modules/audio_device/ios/audio_device_ios.h" | 18 #include "webrtc/modules/audio_device/ios/audio_device_ios.h" |
| 19 | 19 |
| 20 #include "webrtc/base/atomicops.h" | 20 #include "webrtc/base/atomicops.h" |
| 21 #include "webrtc/base/checks.h" | 21 #include "webrtc/base/checks.h" |
| 22 #include "webrtc/base/criticalsection.h" | 22 #include "webrtc/base/criticalsection.h" |
| 23 #include "webrtc/base/logging.h" | 23 #include "webrtc/base/logging.h" |
| 24 #include "webrtc/base/thread_annotations.h" | 24 #include "webrtc/base/thread_annotations.h" |
| 25 #include "webrtc/modules/audio_device/fine_audio_buffer.h" | 25 #include "webrtc/modules/audio_device/fine_audio_buffer.h" |
| 26 #include "webrtc/modules/utility/include/helpers_ios.h" | 26 #include "webrtc/modules/utility/include/helpers_ios.h" |
| 27 | 27 |
| 28 #import "webrtc/modules/audio_device/ios/objc/RTCAudioSession.h" |
| 29 |
| 28 namespace webrtc { | 30 namespace webrtc { |
| 29 | 31 |
| 30 // Protects |g_audio_session_users|. | |
| 31 static rtc::GlobalLockPod g_lock; | |
| 32 | |
| 33 // Counts number of users (=instances of this object) who needs an active | |
| 34 // audio session. This variable is used to ensure that we only activate an audio | |
| 35 // session for the first user and deactivate it for the last. | |
| 36 // Member is static to ensure that the value is counted for all instances | |
| 37 // and not per instance. | |
| 38 static int g_audio_session_users GUARDED_BY(g_lock) = 0; | |
| 39 | |
| 40 #define LOGI() LOG(LS_INFO) << "AudioDeviceIOS::" | 32 #define LOGI() LOG(LS_INFO) << "AudioDeviceIOS::" |
| 41 | 33 |
| 42 #define LOG_AND_RETURN_IF_ERROR(error, message) \ | 34 #define LOG_AND_RETURN_IF_ERROR(error, message) \ |
| 43 do { \ | 35 do { \ |
| 44 OSStatus err = error; \ | 36 OSStatus err = error; \ |
| 45 if (err) { \ | 37 if (err) { \ |
| 46 LOG(LS_ERROR) << message << ": " << err; \ | 38 LOG(LS_ERROR) << message << ": " << err; \ |
| 47 return false; \ | 39 return false; \ |
| 48 } \ | 40 } \ |
| 49 } while (0) | 41 } while (0) |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 // ADM instances. A fall-back solution is to allow multiple sequential calls | 82 // ADM instances. A fall-back solution is to allow multiple sequential calls |
| 91 // with as small delay between each. This factor sets the max number of allowed | 83 // with as small delay between each. This factor sets the max number of allowed |
| 92 // initialization attempts. | 84 // initialization attempts. |
| 93 const int kMaxNumberOfAudioUnitInitializeAttempts = 5; | 85 const int kMaxNumberOfAudioUnitInitializeAttempts = 5; |
| 94 | 86 |
| 95 | 87 |
| 96 using ios::CheckAndLogError; | 88 using ios::CheckAndLogError; |
| 97 | 89 |
| 98 // Verifies that the current audio session supports input audio and that the | 90 // Verifies that the current audio session supports input audio and that the |
| 99 // required category and mode are enabled. | 91 // required category and mode are enabled. |
| 100 static bool VerifyAudioSession(AVAudioSession* session) { | 92 static bool VerifyAudioSession(RTCAudioSession* session) { |
| 101 LOG(LS_INFO) << "VerifyAudioSession"; | 93 LOG(LS_INFO) << "VerifyAudioSession"; |
| 102 // Ensure that the device currently supports audio input. | 94 // Ensure that the device currently supports audio input. |
| 103 if (!session.isInputAvailable) { | 95 if (!session.inputAvailable) { |
| 104 LOG(LS_ERROR) << "No audio input path is available!"; | 96 LOG(LS_ERROR) << "No audio input path is available!"; |
| 105 return false; | 97 return false; |
| 106 } | 98 } |
| 107 | 99 |
| 108 // Ensure that the required category and mode are actually activated. | 100 // Ensure that the required category and mode are actually activated. |
| 109 if (![session.category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) { | 101 if (![session.category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) { |
| 110 LOG(LS_ERROR) | 102 LOG(LS_ERROR) |
| 111 << "Failed to set category to AVAudioSessionCategoryPlayAndRecord"; | 103 << "Failed to set category to AVAudioSessionCategoryPlayAndRecord"; |
| 112 return false; | 104 return false; |
| 113 } | 105 } |
| 114 if (![session.mode isEqualToString:AVAudioSessionModeVoiceChat]) { | 106 if (![session.mode isEqualToString:AVAudioSessionModeVoiceChat]) { |
| 115 LOG(LS_ERROR) << "Failed to set mode to AVAudioSessionModeVoiceChat"; | 107 LOG(LS_ERROR) << "Failed to set mode to AVAudioSessionModeVoiceChat"; |
| 116 return false; | 108 return false; |
| 117 } | 109 } |
| 118 return true; | 110 return true; |
| 119 } | 111 } |
| 120 | 112 |
| 121 // Activates an audio session suitable for full duplex VoIP sessions when | 113 // Activates an audio session suitable for full duplex VoIP sessions when |
| 122 // |activate| is true. Also sets the preferred sample rate and IO buffer | 114 // |activate| is true. Also sets the preferred sample rate and IO buffer |
| 123 // duration. Deactivates an active audio session if |activate| is set to false. | 115 // duration. Deactivates an active audio session if |activate| is set to false. |
| 124 static bool ActivateAudioSession(AVAudioSession* session, bool activate) | 116 static bool ActivateAudioSession(RTCAudioSession* session, bool activate) { |
| 125 EXCLUSIVE_LOCKS_REQUIRED(g_lock) { | |
| 126 LOG(LS_INFO) << "ActivateAudioSession(" << activate << ")"; | 117 LOG(LS_INFO) << "ActivateAudioSession(" << activate << ")"; |
| 127 @autoreleasepool { | |
| 128 NSError* error = nil; | |
| 129 BOOL success = NO; | |
| 130 | 118 |
| 131 if (!activate) { | 119 NSError* error = nil; |
| 132 // Deactivate the audio session using an extra option and then return. | 120 BOOL success = NO; |
| 133 // AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation is used to | |
| 134 // ensure that other audio sessions that were interrupted by our session | |
| 135 // can return to their active state. It is recommended for VoIP apps to | |
| 136 // use this option. | |
| 137 success = [session | |
| 138 setActive:NO | |
| 139 withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation | |
| 140 error:&error]; | |
| 141 return CheckAndLogError(success, error); | |
| 142 } | |
| 143 | 121 |
| 144 // Go ahead and active our own audio session since |activate| is true. | 122 [session lockForConfiguration]; |
| 145 // Use a category which supports simultaneous recording and playback. | 123 if (!activate) { |
| 146 // By default, using this category implies that our app’s audio is | 124 success = [session setActive:NO |
| 147 // nonmixable, hence activating the session will interrupt any other | 125 error:&error]; |
| 148 // audio sessions which are also nonmixable. | 126 [session unlockForConfiguration]; |
| 149 if (session.category != AVAudioSessionCategoryPlayAndRecord) { | 127 return CheckAndLogError(success, error); |
| 150 error = nil; | 128 } |
| 151 success = [session setCategory:AVAudioSessionCategoryPlayAndRecord | |
| 152 withOptions:AVAudioSessionCategoryOptionAllowBluetooth | |
| 153 error:&error]; | |
| 154 RTC_DCHECK(CheckAndLogError(success, error)); | |
| 155 } | |
| 156 | 129 |
| 157 // Specify mode for two-way voice communication (e.g. VoIP). | 130 // Go ahead and active our own audio session since |activate| is true. |
| 158 if (session.mode != AVAudioSessionModeVoiceChat) { | 131 // Use a category which supports simultaneous recording and playback. |
| 159 error = nil; | 132 // By default, using this category implies that our app’s audio is |
| 160 success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; | 133 // nonmixable, hence activating the session will interrupt any other |
| 161 RTC_DCHECK(CheckAndLogError(success, error)); | 134 // audio sessions which are also nonmixable. |
| 162 } | 135 if (session.category != AVAudioSessionCategoryPlayAndRecord) { |
| 136 error = nil; |
| 137 success = [session setCategory:AVAudioSessionCategoryPlayAndRecord |
| 138 withOptions:AVAudioSessionCategoryOptionAllowBluetooth |
| 139 error:&error]; |
| 140 RTC_DCHECK(CheckAndLogError(success, error)); |
| 141 } |
| 163 | 142 |
| 164 // Set the session's sample rate or the hardware sample rate. | 143 // Specify mode for two-way voice communication (e.g. VoIP). |
| 165 // It is essential that we use the same sample rate as stream format | 144 if (session.mode != AVAudioSessionModeVoiceChat) { |
| 166 // to ensure that the I/O unit does not have to do sample rate conversion. | |
| 167 error = nil; | 145 error = nil; |
| 168 success = | 146 success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; |
| 169 [session setPreferredSampleRate:kPreferredSampleRate error:&error]; | |
| 170 RTC_DCHECK(CheckAndLogError(success, error)); | 147 RTC_DCHECK(CheckAndLogError(success, error)); |
| 148 } |
| 171 | 149 |
| 172 // Set the preferred audio I/O buffer duration, in seconds. | 150 // Set the session's sample rate or the hardware sample rate. |
| 173 error = nil; | 151 // It is essential that we use the same sample rate as stream format |
| 174 success = [session setPreferredIOBufferDuration:kPreferredIOBufferDuration | 152 // to ensure that the I/O unit does not have to do sample rate conversion. |
| 175 error:&error]; | 153 error = nil; |
| 176 RTC_DCHECK(CheckAndLogError(success, error)); | 154 success = |
| 155 [session setPreferredSampleRate:kPreferredSampleRate error:&error]; |
| 156 RTC_DCHECK(CheckAndLogError(success, error)); |
| 177 | 157 |
| 178 // Activate the audio session. Activation can fail if another active audio | 158 // Set the preferred audio I/O buffer duration, in seconds. |
| 179 // session (e.g. phone call) has higher priority than ours. | 159 error = nil; |
| 180 error = nil; | 160 success = [session setPreferredIOBufferDuration:kPreferredIOBufferDuration |
| 181 success = [session setActive:YES error:&error]; | 161 error:&error]; |
| 182 if (!CheckAndLogError(success, error)) { | 162 RTC_DCHECK(CheckAndLogError(success, error)); |
| 183 return false; | |
| 184 } | |
| 185 | 163 |
| 186 // Ensure that the active audio session has the correct category and mode. | 164 // Activate the audio session. Activation can fail if another active audio |
| 187 if (!VerifyAudioSession(session)) { | 165 // session (e.g. phone call) has higher priority than ours. |
| 188 LOG(LS_ERROR) << "Failed to verify audio session category and mode"; | 166 error = nil; |
| 189 return false; | 167 success = [session setActive:YES error:&error]; |
| 190 } | 168 if (!CheckAndLogError(success, error)) { |
| 169 [session unlockForConfiguration]; |
| 170 return false; |
| 171 } |
| 191 | 172 |
| 192 // Try to set the preferred number of hardware audio channels. These calls | 173 // Ensure that the active audio session has the correct category and mode. |
| 193 // must be done after setting the audio session’s category and mode and | 174 if (!VerifyAudioSession(session)) { |
| 194 // activating the session. | 175 LOG(LS_ERROR) << "Failed to verify audio session category and mode"; |
| 195 // We try to use mono in both directions to save resources and format | 176 [session unlockForConfiguration]; |
| 196 // conversions in the audio unit. Some devices does only support stereo; | 177 return false; |
| 197 // e.g. wired headset on iPhone 6. | |
| 198 // TODO(henrika): add support for stereo if needed. | |
| 199 error = nil; | |
| 200 success = | |
| 201 [session setPreferredInputNumberOfChannels:kPreferredNumberOfChannels | |
| 202 error:&error]; | |
| 203 RTC_DCHECK(CheckAndLogError(success, error)); | |
| 204 error = nil; | |
| 205 success = | |
| 206 [session setPreferredOutputNumberOfChannels:kPreferredNumberOfChannels | |
| 207 error:&error]; | |
| 208 RTC_DCHECK(CheckAndLogError(success, error)); | |
| 209 return true; | |
| 210 } | 178 } |
| 179 |
| 180 // Try to set the preferred number of hardware audio channels. These calls |
| 181 // must be done after setting the audio session’s category and mode and |
| 182 // activating the session. |
| 183 // We try to use mono in both directions to save resources and format |
| 184 // conversions in the audio unit. Some devices does only support stereo; |
| 185 // e.g. wired headset on iPhone 6. |
| 186 // TODO(henrika): add support for stereo if needed. |
| 187 error = nil; |
| 188 success = |
| 189 [session setPreferredInputNumberOfChannels:kPreferredNumberOfChannels |
| 190 error:&error]; |
| 191 RTC_DCHECK(CheckAndLogError(success, error)); |
| 192 error = nil; |
| 193 success = |
| 194 [session setPreferredOutputNumberOfChannels:kPreferredNumberOfChannels |
| 195 error:&error]; |
| 196 RTC_DCHECK(CheckAndLogError(success, error)); |
| 197 [session unlockForConfiguration]; |
| 198 return true; |
| 211 } | 199 } |
| 212 | 200 |
| 213 // An application can create more than one ADM and start audio streaming | 201 // An application can create more than one ADM and start audio streaming |
| 214 // for all of them. It is essential that we only activate the app's audio | 202 // for all of them. It is essential that we only activate the app's audio |
| 215 // session once (for the first one) and deactivate it once (for the last). | 203 // session once (for the first one) and deactivate it once (for the last). |
| 216 static bool ActivateAudioSession() { | 204 static bool ActivateAudioSession() { |
| 217 LOGI() << "ActivateAudioSession"; | 205 LOGI() << "ActivateAudioSession"; |
| 218 rtc::GlobalLockScope ls(&g_lock); | 206 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 219 if (g_audio_session_users == 0) { | 207 return ActivateAudioSession(session, true); |
| 220 // The system provides an audio session object upon launch of an | |
| 221 // application. However, we must initialize the session in order to | |
| 222 // handle interruptions. Implicit initialization occurs when obtaining | |
| 223 // a reference to the AVAudioSession object. | |
| 224 AVAudioSession* session = [AVAudioSession sharedInstance]; | |
| 225 // Try to activate the audio session and ask for a set of preferred audio | |
| 226 // parameters. | |
| 227 if (!ActivateAudioSession(session, true)) { | |
| 228 LOG(LS_ERROR) << "Failed to activate the audio session"; | |
| 229 return false; | |
| 230 } | |
| 231 LOG(LS_INFO) << "The audio session is now activated"; | |
| 232 } | |
| 233 ++g_audio_session_users; | |
| 234 LOG(LS_INFO) << "Number of audio session users: " << g_audio_session_users; | |
| 235 return true; | |
| 236 } | 208 } |
| 237 | 209 |
| 238 // If more than one object is using the audio session, ensure that only the | 210 // If more than one object is using the audio session, ensure that only the |
| 239 // last object deactivates. Apple recommends: "activate your audio session | 211 // last object deactivates. Apple recommends: "activate your audio session |
| 240 // only as needed and deactivate it when you are not using audio". | 212 // only as needed and deactivate it when you are not using audio". |
| 241 static bool DeactivateAudioSession() { | 213 static bool DeactivateAudioSession() { |
| 242 LOGI() << "DeactivateAudioSession"; | 214 LOGI() << "DeactivateAudioSession"; |
| 243 rtc::GlobalLockScope ls(&g_lock); | 215 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 244 if (g_audio_session_users == 1) { | 216 return ActivateAudioSession(session, false); |
| 245 AVAudioSession* session = [AVAudioSession sharedInstance]; | |
| 246 if (!ActivateAudioSession(session, false)) { | |
| 247 LOG(LS_ERROR) << "Failed to deactivate the audio session"; | |
| 248 return false; | |
| 249 } | |
| 250 LOG(LS_INFO) << "Our audio session is now deactivated"; | |
| 251 } | |
| 252 --g_audio_session_users; | |
| 253 LOG(LS_INFO) << "Number of audio session users: " << g_audio_session_users; | |
| 254 return true; | |
| 255 } | 217 } |
| 256 | 218 |
| 257 #if !defined(NDEBUG) | 219 #if !defined(NDEBUG) |
| 258 // Helper method for printing out an AudioStreamBasicDescription structure. | 220 // Helper method for printing out an AudioStreamBasicDescription structure. |
| 259 static void LogABSD(AudioStreamBasicDescription absd) { | 221 static void LogABSD(AudioStreamBasicDescription absd) { |
| 260 char formatIDString[5]; | 222 char formatIDString[5]; |
| 261 UInt32 formatID = CFSwapInt32HostToBig(absd.mFormatID); | 223 UInt32 formatID = CFSwapInt32HostToBig(absd.mFormatID); |
| 262 bcopy(&formatID, formatIDString, 4); | 224 bcopy(&formatID, formatIDString, 4); |
| 263 formatIDString[4] = '\0'; | 225 formatIDString[4] = '\0'; |
| 264 LOG(LS_INFO) << "LogABSD"; | 226 LOG(LS_INFO) << "LogABSD"; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 | 299 |
| 338 int32_t AudioDeviceIOS::Terminate() { | 300 int32_t AudioDeviceIOS::Terminate() { |
| 339 LOGI() << "Terminate"; | 301 LOGI() << "Terminate"; |
| 340 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 302 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 341 if (!initialized_) { | 303 if (!initialized_) { |
| 342 return 0; | 304 return 0; |
| 343 } | 305 } |
| 344 StopPlayout(); | 306 StopPlayout(); |
| 345 StopRecording(); | 307 StopRecording(); |
| 346 initialized_ = false; | 308 initialized_ = false; |
| 347 { | |
| 348 rtc::GlobalLockScope ls(&g_lock); | |
| 349 if (g_audio_session_users != 0) { | |
| 350 LOG(LS_WARNING) << "Object is destructed with an active audio session"; | |
| 351 } | |
| 352 RTC_DCHECK_GE(g_audio_session_users, 0); | |
| 353 } | |
| 354 return 0; | 309 return 0; |
| 355 } | 310 } |
| 356 | 311 |
| 357 int32_t AudioDeviceIOS::InitPlayout() { | 312 int32_t AudioDeviceIOS::InitPlayout() { |
| 358 LOGI() << "InitPlayout"; | 313 LOGI() << "InitPlayout"; |
| 359 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 314 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 360 RTC_DCHECK(initialized_); | 315 RTC_DCHECK(initialized_); |
| 361 RTC_DCHECK(!play_is_initialized_); | 316 RTC_DCHECK(!play_is_initialized_); |
| 362 RTC_DCHECK(!playing_); | 317 RTC_DCHECK(!playing_); |
| 363 if (!rec_is_initialized_) { | 318 if (!rec_is_initialized_) { |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 449 } | 404 } |
| 450 rec_is_initialized_ = false; | 405 rec_is_initialized_ = false; |
| 451 rtc::AtomicOps::ReleaseStore(&recording_, 0); | 406 rtc::AtomicOps::ReleaseStore(&recording_, 0); |
| 452 return 0; | 407 return 0; |
| 453 } | 408 } |
| 454 | 409 |
| 455 // Change the default receiver playout route to speaker. | 410 // Change the default receiver playout route to speaker. |
| 456 int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { | 411 int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { |
| 457 LOGI() << "SetLoudspeakerStatus(" << enable << ")"; | 412 LOGI() << "SetLoudspeakerStatus(" << enable << ")"; |
| 458 | 413 |
| 459 AVAudioSession* session = [AVAudioSession sharedInstance]; | 414 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 415 [session lockForConfiguration]; |
| 460 NSString* category = session.category; | 416 NSString* category = session.category; |
| 461 AVAudioSessionCategoryOptions options = session.categoryOptions; | 417 AVAudioSessionCategoryOptions options = session.categoryOptions; |
| 462 // Respect old category options if category is | 418 // Respect old category options if category is |
| 463 // AVAudioSessionCategoryPlayAndRecord. Otherwise reset it since old options | 419 // AVAudioSessionCategoryPlayAndRecord. Otherwise reset it since old options |
| 464 // might not be valid for this category. | 420 // might not be valid for this category. |
| 465 if ([category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) { | 421 if ([category isEqualToString:AVAudioSessionCategoryPlayAndRecord]) { |
| 466 if (enable) { | 422 if (enable) { |
| 467 options |= AVAudioSessionCategoryOptionDefaultToSpeaker; | 423 options |= AVAudioSessionCategoryOptionDefaultToSpeaker; |
| 468 } else { | 424 } else { |
| 469 options &= ~AVAudioSessionCategoryOptionDefaultToSpeaker; | 425 options &= ~AVAudioSessionCategoryOptionDefaultToSpeaker; |
| 470 } | 426 } |
| 471 } else { | 427 } else { |
| 472 options = AVAudioSessionCategoryOptionDefaultToSpeaker; | 428 options = AVAudioSessionCategoryOptionDefaultToSpeaker; |
| 473 } | 429 } |
| 474 NSError* error = nil; | 430 NSError* error = nil; |
| 475 BOOL success = [session setCategory:AVAudioSessionCategoryPlayAndRecord | 431 BOOL success = [session setCategory:AVAudioSessionCategoryPlayAndRecord |
| 476 withOptions:options | 432 withOptions:options |
| 477 error:&error]; | 433 error:&error]; |
| 478 ios::CheckAndLogError(success, error); | 434 ios::CheckAndLogError(success, error); |
| 435 [session unlockForConfiguration]; |
| 479 return (error == nil) ? 0 : -1; | 436 return (error == nil) ? 0 : -1; |
| 480 } | 437 } |
| 481 | 438 |
| 482 int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const { | 439 int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const { |
| 483 LOGI() << "GetLoudspeakerStatus"; | 440 LOGI() << "GetLoudspeakerStatus"; |
| 484 AVAudioSession* session = [AVAudioSession sharedInstance]; | 441 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 485 AVAudioSessionCategoryOptions options = session.categoryOptions; | 442 AVAudioSessionCategoryOptions options = session.categoryOptions; |
| 486 enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; | 443 enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; |
| 487 return 0; | 444 return 0; |
| 488 } | 445 } |
| 489 | 446 |
| 490 int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { | 447 int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { |
| 491 delayMS = kFixedPlayoutDelayEstimate; | 448 delayMS = kFixedPlayoutDelayEstimate; |
| 492 return 0; | 449 return 0; |
| 493 } | 450 } |
| 494 | 451 |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 if (valid_route_change) { | 568 if (valid_route_change) { |
| 612 // Log previous route configuration. | 569 // Log previous route configuration. |
| 613 AVAudioSessionRouteDescription* prev_route = | 570 AVAudioSessionRouteDescription* prev_route = |
| 614 notification.userInfo[AVAudioSessionRouteChangePreviousRouteKey]; | 571 notification.userInfo[AVAudioSessionRouteChangePreviousRouteKey]; |
| 615 LOG(LS_INFO) << "Previous route:"; | 572 LOG(LS_INFO) << "Previous route:"; |
| 616 LOG(LS_INFO) << ios::StdStringFromNSString( | 573 LOG(LS_INFO) << ios::StdStringFromNSString( |
| 617 [NSString stringWithFormat:@"%@", prev_route]); | 574 [NSString stringWithFormat:@"%@", prev_route]); |
| 618 | 575 |
| 619 // Only restart audio for a valid route change and if the | 576 // Only restart audio for a valid route change and if the |
| 620 // session sample rate has changed. | 577 // session sample rate has changed. |
| 621 AVAudioSession* session = [AVAudioSession sharedInstance]; | 578 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 622 const double session_sample_rate = session.sampleRate; | 579 const double session_sample_rate = session.sampleRate; |
| 623 LOG(LS_INFO) << "session sample rate: " << session_sample_rate; | 580 LOG(LS_INFO) << "session sample rate: " << session_sample_rate; |
| 624 if (playout_parameters_.sample_rate() != session_sample_rate) { | 581 if (playout_parameters_.sample_rate() != session_sample_rate) { |
| 625 if (!RestartAudioUnitWithNewFormat(session_sample_rate)) { | 582 if (!RestartAudioUnitWithNewFormat(session_sample_rate)) { |
| 626 LOG(LS_ERROR) << "Audio restart failed"; | 583 LOG(LS_ERROR) << "Audio restart failed"; |
| 627 } | 584 } |
| 628 } | 585 } |
| 629 } | 586 } |
| 630 }; | 587 }; |
| 631 | 588 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 665 if (route_change_observer_ != nullptr) { | 622 if (route_change_observer_ != nullptr) { |
| 666 id observer = (__bridge_transfer id)route_change_observer_; | 623 id observer = (__bridge_transfer id)route_change_observer_; |
| 667 [center removeObserver:observer]; | 624 [center removeObserver:observer]; |
| 668 route_change_observer_ = nullptr; | 625 route_change_observer_ = nullptr; |
| 669 } | 626 } |
| 670 } | 627 } |
| 671 | 628 |
| 672 void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { | 629 void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { |
| 673 LOGI() << "SetupAudioBuffersForActiveAudioSession"; | 630 LOGI() << "SetupAudioBuffersForActiveAudioSession"; |
| 674 // Verify the current values once the audio session has been activated. | 631 // Verify the current values once the audio session has been activated. |
| 675 AVAudioSession* session = [AVAudioSession sharedInstance]; | 632 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 676 LOG(LS_INFO) << " sample rate: " << session.sampleRate; | 633 LOG(LS_INFO) << " sample rate: " << session.sampleRate; |
| 677 LOG(LS_INFO) << " IO buffer duration: " << session.IOBufferDuration; | 634 LOG(LS_INFO) << " IO buffer duration: " << session.IOBufferDuration; |
| 678 LOG(LS_INFO) << " output channels: " << session.outputNumberOfChannels; | 635 LOG(LS_INFO) << " output channels: " << session.outputNumberOfChannels; |
| 679 LOG(LS_INFO) << " input channels: " << session.inputNumberOfChannels; | 636 LOG(LS_INFO) << " input channels: " << session.inputNumberOfChannels; |
| 680 LOG(LS_INFO) << " output latency: " << session.outputLatency; | 637 LOG(LS_INFO) << " output latency: " << session.outputLatency; |
| 681 LOG(LS_INFO) << " input latency: " << session.inputLatency; | 638 LOG(LS_INFO) << " input latency: " << session.inputLatency; |
| 682 | 639 |
| 683 // Log a warning message for the case when we are unable to set the preferred | 640 // Log a warning message for the case when we are unable to set the preferred |
| 684 // hardware sample rate but continue and use the non-ideal sample rate after | 641 // hardware sample rate but continue and use the non-ideal sample rate after |
| 685 // reinitializing the audio parameters. Most BT headsets only support 8kHz or | 642 // reinitializing the audio parameters. Most BT headsets only support 8kHz or |
| (...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 947 } | 904 } |
| 948 | 905 |
| 949 bool AudioDeviceIOS::InitPlayOrRecord() { | 906 bool AudioDeviceIOS::InitPlayOrRecord() { |
| 950 LOGI() << "InitPlayOrRecord"; | 907 LOGI() << "InitPlayOrRecord"; |
| 951 // Activate the audio session if not already activated. | 908 // Activate the audio session if not already activated. |
| 952 if (!ActivateAudioSession()) { | 909 if (!ActivateAudioSession()) { |
| 953 return false; | 910 return false; |
| 954 } | 911 } |
| 955 | 912 |
| 956 // Ensure that the active audio session has the correct category and mode. | 913 // Ensure that the active audio session has the correct category and mode. |
| 957 AVAudioSession* session = [AVAudioSession sharedInstance]; | 914 RTCAudioSession* session = [RTCAudioSession sharedInstance]; |
| 958 if (!VerifyAudioSession(session)) { | 915 if (!VerifyAudioSession(session)) { |
| 959 DeactivateAudioSession(); | 916 DeactivateAudioSession(); |
| 960 LOG(LS_ERROR) << "Failed to verify audio session category and mode"; | 917 LOG(LS_ERROR) << "Failed to verify audio session category and mode"; |
| 961 return false; | 918 return false; |
| 962 } | 919 } |
| 963 | 920 |
| 964 // Start observing audio session interruptions and route changes. | 921 // Start observing audio session interruptions and route changes. |
| 965 RegisterNotificationObservers(); | 922 RegisterNotificationObservers(); |
| 966 | 923 |
| 967 // Ensure that we got what what we asked for in our active audio session. | 924 // Ensure that we got what what we asked for in our active audio session. |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1103 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches | 1060 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches |
| 1104 // the native I/O audio unit) to a preallocated intermediate buffer and | 1061 // the native I/O audio unit) to a preallocated intermediate buffer and |
| 1105 // copy the result to the audio buffer in the |io_data| destination. | 1062 // copy the result to the audio buffer in the |io_data| destination. |
| 1106 SInt8* source = playout_audio_buffer_.get(); | 1063 SInt8* source = playout_audio_buffer_.get(); |
| 1107 fine_audio_buffer_->GetPlayoutData(source); | 1064 fine_audio_buffer_->GetPlayoutData(source); |
| 1108 memcpy(destination, source, dataSizeInBytes); | 1065 memcpy(destination, source, dataSizeInBytes); |
| 1109 return noErr; | 1066 return noErr; |
| 1110 } | 1067 } |
| 1111 | 1068 |
| 1112 } // namespace webrtc | 1069 } // namespace webrtc |
| OLD | NEW |