blob: 9ca664d119c6814e6f73f8cac72e510276b3d6fb [file] [log] [blame]
sjlee@webrtc.org4b425082012-09-10 17:58:21 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
tkchin@webrtc.org122caa52014-07-15 20:20:47 +000011#import <AVFoundation/AVFoundation.h>
12#import <Foundation/Foundation.h>
sjlee@webrtc.org4b425082012-09-10 17:58:21 +000013
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "modules/audio_device/ios/audio_device_ios.h"
sjlee@webrtc.org4b425082012-09-10 17:58:21 +000015
Henrik Andreasson6e286cb2017-06-13 15:20:52 +000016#include <cmath>
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "api/array_view.h"
19#include "modules/audio_device/fine_audio_buffer.h"
20#include "rtc_base/atomicops.h"
21#include "rtc_base/bind.h"
22#include "rtc_base/checks.h"
23#include "rtc_base/criticalsection.h"
24#include "rtc_base/logging.h"
25#include "rtc_base/thread.h"
26#include "rtc_base/thread_annotations.h"
27#include "rtc_base/timeutils.h"
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020028#include "sdk/objc/native/src/audio/helpers.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "system_wrappers/include/metrics.h"
sjlee@webrtc.org4b425082012-09-10 17:58:21 +000030
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020031#import "modules/audio_device/ios/objc/RTCAudioSessionDelegateAdapter.h"
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020032#import "sdk/objc/base/RTCLogging.h"
33#import "sdk/objc/components/audio/RTCAudioSession+Private.h"
34#import "sdk/objc/components/audio/RTCAudioSession.h"
35#import "sdk/objc/components/audio/RTCAudioSessionConfiguration.h"
denicija59ee91b2017-06-05 05:48:47 -070036
sjlee@webrtc.org4b425082012-09-10 17:58:21 +000037namespace webrtc {
sjlee@webrtc.org4b425082012-09-10 17:58:21 +000038
Mirko Bonadei675513b2017-11-09 11:09:25 +010039#define LOGI() RTC_LOG(LS_INFO) << "AudioDeviceIOS::"
henrikaba35d052015-07-14 17:04:08 +020040
Mirko Bonadei675513b2017-11-09 11:09:25 +010041#define LOG_AND_RETURN_IF_ERROR(error, message) \
42 do { \
43 OSStatus err = error; \
44 if (err) { \
45 RTC_LOG(LS_ERROR) << message << ": " << err; \
46 return false; \
47 } \
henrika86d907c2015-09-07 16:09:50 +020048 } while (0)
49
Mirko Bonadei675513b2017-11-09 11:09:25 +010050#define LOG_IF_ERROR(error, message) \
51 do { \
52 OSStatus err = error; \
53 if (err) { \
54 RTC_LOG(LS_ERROR) << message << ": " << err; \
55 } \
henrika45c136b2015-10-21 04:11:53 -070056 } while (0)
57
henrika86d907c2015-09-07 16:09:50 +020058// Hardcoded delay estimates based on real measurements.
59// TODO(henrika): these value is not used in combination with built-in AEC.
60// Can most likely be removed.
61const UInt16 kFixedPlayoutDelayEstimate = 30;
62const UInt16 kFixedRecordDelayEstimate = 30;
henrikac7290322015-12-02 10:46:46 +010063
tkchind2511962016-05-06 18:54:15 -070064enum AudioDeviceMessageType : uint32_t {
65 kMessageTypeInterruptionBegin,
66 kMessageTypeInterruptionEnd,
67 kMessageTypeValidRouteChange,
68 kMessageTypeCanPlayOrRecordChange,
henrika7be78832017-06-13 17:34:16 +020069 kMessageTypePlayoutGlitchDetected,
henrikaaf35f832017-06-16 13:22:13 +020070 kMessageOutputVolumeChange,
tkchind2511962016-05-06 18:54:15 -070071};
72
henrikaba35d052015-07-14 17:04:08 +020073using ios::CheckAndLogError;
74
henrika324d9c92015-07-20 13:09:23 +020075#if !defined(NDEBUG)
henrikabb6f7522017-05-30 02:01:30 -070076// Returns true when the code runs on a device simulator.
77static bool DeviceIsSimulator() {
78 return ios::GetDeviceName() == "x86_64";
79}
80
henrika86d907c2015-09-07 16:09:50 +020081// Helper method that logs essential device information strings.
henrika324d9c92015-07-20 13:09:23 +020082static void LogDeviceInfo() {
Mirko Bonadei675513b2017-11-09 11:09:25 +010083 RTC_LOG(LS_INFO) << "LogDeviceInfo";
henrika324d9c92015-07-20 13:09:23 +020084 @autoreleasepool {
Mirko Bonadei675513b2017-11-09 11:09:25 +010085 RTC_LOG(LS_INFO) << " system name: " << ios::GetSystemName();
Kári Tristan Helgason86f80472017-12-01 14:55:01 +010086 RTC_LOG(LS_INFO) << " system version: " << ios::GetSystemVersionAsString();
Mirko Bonadei675513b2017-11-09 11:09:25 +010087 RTC_LOG(LS_INFO) << " device type: " << ios::GetDeviceType();
88 RTC_LOG(LS_INFO) << " device name: " << ios::GetDeviceName();
89 RTC_LOG(LS_INFO) << " process name: " << ios::GetProcessName();
90 RTC_LOG(LS_INFO) << " process ID: " << ios::GetProcessID();
91 RTC_LOG(LS_INFO) << " OS version: " << ios::GetOSVersionString();
92 RTC_LOG(LS_INFO) << " processing cores: " << ios::GetProcessorCount();
Mirko Bonadei675513b2017-11-09 11:09:25 +010093 RTC_LOG(LS_INFO) << " low power mode: " << ios::GetLowPowerModeEnabled();
henrikabb6f7522017-05-30 02:01:30 -070094#if TARGET_IPHONE_SIMULATOR
Mirko Bonadei675513b2017-11-09 11:09:25 +010095 RTC_LOG(LS_INFO) << " TARGET_IPHONE_SIMULATOR is defined";
henrikabb6f7522017-05-30 02:01:30 -070096#endif
Mirko Bonadei675513b2017-11-09 11:09:25 +010097 RTC_LOG(LS_INFO) << " DeviceIsSimulator: " << DeviceIsSimulator();
henrika324d9c92015-07-20 13:09:23 +020098 }
99}
henrika86d907c2015-09-07 16:09:50 +0200100#endif // !defined(NDEBUG)
henrika324d9c92015-07-20 13:09:23 +0200101
henrikaba35d052015-07-14 17:04:08 +0200102AudioDeviceIOS::AudioDeviceIOS()
tkchind2511962016-05-06 18:54:15 -0700103 : audio_device_buffer_(nullptr),
Zeke Chin1300caa2016-03-18 14:39:11 -0700104 audio_unit_(nullptr),
105 recording_(0),
106 playing_(0),
107 initialized_(false),
henrika17802ae2016-09-21 04:55:04 -0700108 audio_is_initialized_(false),
tkchind2511962016-05-06 18:54:15 -0700109 is_interrupted_(false),
henrika7be78832017-06-13 17:34:16 +0200110 has_configured_session_(false),
111 num_detected_playout_glitches_(0),
112 last_playout_time_(0),
henrikaaf35f832017-06-16 13:22:13 +0200113 num_playout_callbacks_(0),
114 last_output_volume_change_time_(0) {
henrikaba35d052015-07-14 17:04:08 +0200115 LOGI() << "ctor" << ios::GetCurrentThreadDescription();
henrikaaf35f832017-06-16 13:22:13 +0200116 io_thread_checker_.DetachFromThread();
tkchine54467f2016-03-15 16:54:03 -0700117 thread_ = rtc::Thread::Current();
Mirko Bonadei72c42502017-11-09 09:33:23 +0100118 audio_session_observer_ = [[RTCAudioSessionDelegateAdapter alloc] initWithObserver:this];
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000119}
120
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000121AudioDeviceIOS::~AudioDeviceIOS() {
henrika34911ad2015-11-20 15:47:09 +0100122 LOGI() << "~dtor" << ios::GetCurrentThreadDescription();
tkchine54467f2016-03-15 16:54:03 -0700123 audio_session_observer_ = nil;
henrika8c471e72015-10-01 07:36:45 -0700124 RTC_DCHECK(thread_checker_.CalledOnValidThread());
henrikaba35d052015-07-14 17:04:08 +0200125 Terminate();
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000126}
127
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000128void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
henrikaba35d052015-07-14 17:04:08 +0200129 LOGI() << "AttachAudioBuffer";
henrikg91d6ede2015-09-17 00:24:34 -0700130 RTC_DCHECK(audioBuffer);
henrika8c471e72015-10-01 07:36:45 -0700131 RTC_DCHECK(thread_checker_.CalledOnValidThread());
132 audio_device_buffer_ = audioBuffer;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000133}
134
Max Morin84cab202016-07-01 13:35:19 +0200135AudioDeviceGeneric::InitStatus AudioDeviceIOS::Init() {
henrikaba35d052015-07-14 17:04:08 +0200136 LOGI() << "Init";
henrikaaf35f832017-06-16 13:22:13 +0200137 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika8c471e72015-10-01 07:36:45 -0700138 if (initialized_) {
Max Morin84cab202016-07-01 13:35:19 +0200139 return InitStatus::OK;
henrikaba35d052015-07-14 17:04:08 +0200140 }
141#if !defined(NDEBUG)
142 LogDeviceInfo();
143#endif
henrika86d907c2015-09-07 16:09:50 +0200144 // Store the preferred sample rate and preferred number of channels already
tkchin9f987d32016-03-12 20:06:28 -0800145 // here. They have not been set and confirmed yet since configureForWebRTC
henrika86d907c2015-09-07 16:09:50 +0200146 // is not called until audio is about to start. However, it makes sense to
147 // store the parameters now and then verify at a later stage.
Mirko Bonadei72c42502017-11-09 09:33:23 +0100148 RTCAudioSessionConfiguration* config = [RTCAudioSessionConfiguration webRTCConfiguration];
149 playout_parameters_.reset(config.sampleRate, config.outputNumberOfChannels);
150 record_parameters_.reset(config.sampleRate, config.inputNumberOfChannels);
henrika86d907c2015-09-07 16:09:50 +0200151 // Ensure that the audio device buffer (ADB) knows about the internal audio
152 // parameters. Note that, even if we are unable to get a mono audio session,
153 // we will always tell the I/O audio unit to do a channel format conversion
154 // to guarantee mono on the "input side" of the audio unit.
155 UpdateAudioDeviceBuffer();
henrika8c471e72015-10-01 07:36:45 -0700156 initialized_ = true;
Max Morin84cab202016-07-01 13:35:19 +0200157 return InitStatus::OK;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000158}
159
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000160int32_t AudioDeviceIOS::Terminate() {
henrikaba35d052015-07-14 17:04:08 +0200161 LOGI() << "Terminate";
henrikaaf35f832017-06-16 13:22:13 +0200162 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika8c471e72015-10-01 07:36:45 -0700163 if (!initialized_) {
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000164 return 0;
henrikaba35d052015-07-14 17:04:08 +0200165 }
henrika34911ad2015-11-20 15:47:09 +0100166 StopPlayout();
167 StopRecording();
henrika8c471e72015-10-01 07:36:45 -0700168 initialized_ = false;
henrikaba35d052015-07-14 17:04:08 +0200169 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000170}
171
henrikaaf35f832017-06-16 13:22:13 +0200172bool AudioDeviceIOS::Initialized() const {
173 RTC_DCHECK_RUN_ON(&thread_checker_);
174 return initialized_;
175}
176
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000177int32_t AudioDeviceIOS::InitPlayout() {
henrikaba35d052015-07-14 17:04:08 +0200178 LOGI() << "InitPlayout";
henrikaaf35f832017-06-16 13:22:13 +0200179 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika8c471e72015-10-01 07:36:45 -0700180 RTC_DCHECK(initialized_);
henrika17802ae2016-09-21 04:55:04 -0700181 RTC_DCHECK(!audio_is_initialized_);
pbos46ad5422015-12-07 14:29:14 -0800182 RTC_DCHECK(!playing_);
henrika17802ae2016-09-21 04:55:04 -0700183 if (!audio_is_initialized_) {
henrika86d907c2015-09-07 16:09:50 +0200184 if (!InitPlayOrRecord()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100185 RTC_LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitPlayout!";
henrikaba35d052015-07-14 17:04:08 +0200186 return -1;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000187 }
henrikaba35d052015-07-14 17:04:08 +0200188 }
henrika17802ae2016-09-21 04:55:04 -0700189 audio_is_initialized_ = true;
henrikaba35d052015-07-14 17:04:08 +0200190 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000191}
192
henrikaaf35f832017-06-16 13:22:13 +0200193bool AudioDeviceIOS::PlayoutIsInitialized() const {
194 RTC_DCHECK_RUN_ON(&thread_checker_);
195 return audio_is_initialized_;
196}
197
198bool AudioDeviceIOS::RecordingIsInitialized() const {
199 RTC_DCHECK_RUN_ON(&thread_checker_);
200 return audio_is_initialized_;
201}
202
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000203int32_t AudioDeviceIOS::InitRecording() {
henrika324d9c92015-07-20 13:09:23 +0200204 LOGI() << "InitRecording";
henrikaaf35f832017-06-16 13:22:13 +0200205 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika8c471e72015-10-01 07:36:45 -0700206 RTC_DCHECK(initialized_);
henrika17802ae2016-09-21 04:55:04 -0700207 RTC_DCHECK(!audio_is_initialized_);
pbos46ad5422015-12-07 14:29:14 -0800208 RTC_DCHECK(!recording_);
henrika17802ae2016-09-21 04:55:04 -0700209 if (!audio_is_initialized_) {
henrika86d907c2015-09-07 16:09:50 +0200210 if (!InitPlayOrRecord()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100211 RTC_LOG_F(LS_ERROR) << "InitPlayOrRecord failed for InitRecording!";
henrikaba35d052015-07-14 17:04:08 +0200212 return -1;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000213 }
henrikaba35d052015-07-14 17:04:08 +0200214 }
henrika17802ae2016-09-21 04:55:04 -0700215 audio_is_initialized_ = true;
henrikaba35d052015-07-14 17:04:08 +0200216 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000217}
218
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000219int32_t AudioDeviceIOS::StartPlayout() {
henrikaba35d052015-07-14 17:04:08 +0200220 LOGI() << "StartPlayout";
henrikaaf35f832017-06-16 13:22:13 +0200221 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika17802ae2016-09-21 04:55:04 -0700222 RTC_DCHECK(audio_is_initialized_);
pbos46ad5422015-12-07 14:29:14 -0800223 RTC_DCHECK(!playing_);
tkchind2511962016-05-06 18:54:15 -0700224 RTC_DCHECK(audio_unit_);
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700225 if (fine_audio_buffer_) {
226 fine_audio_buffer_->ResetPlayout();
227 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100228 if (!recording_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) {
Zeke Chin1300caa2016-03-18 14:39:11 -0700229 if (!audio_unit_->Start()) {
230 RTCLogError(@"StartPlayout failed to start audio unit.");
henrikaba35d052015-07-14 17:04:08 +0200231 return -1;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000232 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100233 RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started";
henrikaba35d052015-07-14 17:04:08 +0200234 }
pbos46ad5422015-12-07 14:29:14 -0800235 rtc::AtomicOps::ReleaseStore(&playing_, 1);
henrika7be78832017-06-13 17:34:16 +0200236 num_playout_callbacks_ = 0;
henrika070efc02017-07-05 02:34:31 -0700237 num_detected_playout_glitches_ = 0;
henrikaba35d052015-07-14 17:04:08 +0200238 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000239}
240
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000241int32_t AudioDeviceIOS::StopPlayout() {
henrikaba35d052015-07-14 17:04:08 +0200242 LOGI() << "StopPlayout";
henrikaaf35f832017-06-16 13:22:13 +0200243 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika17802ae2016-09-21 04:55:04 -0700244 if (!audio_is_initialized_ || !playing_) {
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000245 return 0;
henrikaba35d052015-07-14 17:04:08 +0200246 }
pbos46ad5422015-12-07 14:29:14 -0800247 if (!recording_) {
henrikaba35d052015-07-14 17:04:08 +0200248 ShutdownPlayOrRecord();
henrika17802ae2016-09-21 04:55:04 -0700249 audio_is_initialized_ = false;
henrikaba35d052015-07-14 17:04:08 +0200250 }
pbos46ad5422015-12-07 14:29:14 -0800251 rtc::AtomicOps::ReleaseStore(&playing_, 0);
henrika7be78832017-06-13 17:34:16 +0200252
253 // Derive average number of calls to OnGetPlayoutData() between detected
254 // audio glitches and add the result to a histogram.
255 int average_number_of_playout_callbacks_between_glitches = 100000;
henrika070efc02017-07-05 02:34:31 -0700256 RTC_DCHECK_GE(num_playout_callbacks_, num_detected_playout_glitches_);
henrika7be78832017-06-13 17:34:16 +0200257 if (num_detected_playout_glitches_ > 0) {
258 average_number_of_playout_callbacks_between_glitches =
259 num_playout_callbacks_ / num_detected_playout_glitches_;
260 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100261 RTC_HISTOGRAM_COUNTS_100000("WebRTC.Audio.AveragePlayoutCallbacksBetweenGlitches",
262 average_number_of_playout_callbacks_between_glitches);
henrika7be78832017-06-13 17:34:16 +0200263 RTCLog(@"Average number of playout callbacks between glitches: %d",
264 average_number_of_playout_callbacks_between_glitches);
henrikaba35d052015-07-14 17:04:08 +0200265 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000266}
267
henrikaba35d052015-07-14 17:04:08 +0200268int32_t AudioDeviceIOS::StartRecording() {
269 LOGI() << "StartRecording";
henrikaaf35f832017-06-16 13:22:13 +0200270 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika17802ae2016-09-21 04:55:04 -0700271 RTC_DCHECK(audio_is_initialized_);
pbos46ad5422015-12-07 14:29:14 -0800272 RTC_DCHECK(!recording_);
tkchind2511962016-05-06 18:54:15 -0700273 RTC_DCHECK(audio_unit_);
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700274 if (fine_audio_buffer_) {
275 fine_audio_buffer_->ResetRecord();
276 }
Mirko Bonadei72c42502017-11-09 09:33:23 +0100277 if (!playing_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) {
Zeke Chin1300caa2016-03-18 14:39:11 -0700278 if (!audio_unit_->Start()) {
279 RTCLogError(@"StartRecording failed to start audio unit.");
henrikaba35d052015-07-14 17:04:08 +0200280 return -1;
281 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100282 RTC_LOG(LS_INFO) << "Voice-Processing I/O audio unit is now started";
henrikaba35d052015-07-14 17:04:08 +0200283 }
pbos46ad5422015-12-07 14:29:14 -0800284 rtc::AtomicOps::ReleaseStore(&recording_, 1);
henrikaba35d052015-07-14 17:04:08 +0200285 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000286}
287
henrikaba35d052015-07-14 17:04:08 +0200288int32_t AudioDeviceIOS::StopRecording() {
289 LOGI() << "StopRecording";
henrikaaf35f832017-06-16 13:22:13 +0200290 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika17802ae2016-09-21 04:55:04 -0700291 if (!audio_is_initialized_ || !recording_) {
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000292 return 0;
henrikaba35d052015-07-14 17:04:08 +0200293 }
pbos46ad5422015-12-07 14:29:14 -0800294 if (!playing_) {
henrikaba35d052015-07-14 17:04:08 +0200295 ShutdownPlayOrRecord();
henrika17802ae2016-09-21 04:55:04 -0700296 audio_is_initialized_ = false;
henrikaba35d052015-07-14 17:04:08 +0200297 }
pbos46ad5422015-12-07 14:29:14 -0800298 rtc::AtomicOps::ReleaseStore(&recording_, 0);
henrikaba35d052015-07-14 17:04:08 +0200299 return 0;
300}
301
tkchin@webrtc.org122caa52014-07-15 20:20:47 +0000302int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const {
henrika86d907c2015-09-07 16:09:50 +0200303 delayMS = kFixedPlayoutDelayEstimate;
henrikaba35d052015-07-14 17:04:08 +0200304 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000305}
306
henrikaba35d052015-07-14 17:04:08 +0200307int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const {
henrika86d907c2015-09-07 16:09:50 +0200308 LOGI() << "GetPlayoutAudioParameters";
henrika8c471e72015-10-01 07:36:45 -0700309 RTC_DCHECK(playout_parameters_.is_valid());
310 RTC_DCHECK(thread_checker_.CalledOnValidThread());
311 *params = playout_parameters_;
henrikaba35d052015-07-14 17:04:08 +0200312 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000313}
314
henrikaba35d052015-07-14 17:04:08 +0200315int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const {
henrika86d907c2015-09-07 16:09:50 +0200316 LOGI() << "GetRecordAudioParameters";
henrika8c471e72015-10-01 07:36:45 -0700317 RTC_DCHECK(record_parameters_.is_valid());
318 RTC_DCHECK(thread_checker_.CalledOnValidThread());
319 *params = record_parameters_;
henrikaba35d052015-07-14 17:04:08 +0200320 return 0;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000321}
322
tkchine54467f2016-03-15 16:54:03 -0700323void AudioDeviceIOS::OnInterruptionBegin() {
tkchine54467f2016-03-15 16:54:03 -0700324 RTC_DCHECK(thread_);
haysc7735b1e2017-03-29 14:53:32 -0700325 LOGI() << "OnInterruptionBegin";
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700326 thread_->Post(RTC_FROM_HERE, this, kMessageTypeInterruptionBegin);
tkchine54467f2016-03-15 16:54:03 -0700327}
328
329void AudioDeviceIOS::OnInterruptionEnd() {
tkchine54467f2016-03-15 16:54:03 -0700330 RTC_DCHECK(thread_);
haysc7735b1e2017-03-29 14:53:32 -0700331 LOGI() << "OnInterruptionEnd";
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700332 thread_->Post(RTC_FROM_HERE, this, kMessageTypeInterruptionEnd);
tkchine54467f2016-03-15 16:54:03 -0700333}
334
335void AudioDeviceIOS::OnValidRouteChange() {
tkchine54467f2016-03-15 16:54:03 -0700336 RTC_DCHECK(thread_);
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700337 thread_->Post(RTC_FROM_HERE, this, kMessageTypeValidRouteChange);
tkchine54467f2016-03-15 16:54:03 -0700338}
339
tkchind2511962016-05-06 18:54:15 -0700340void AudioDeviceIOS::OnCanPlayOrRecordChange(bool can_play_or_record) {
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700341 RTC_DCHECK(thread_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100342 thread_->Post(RTC_FROM_HERE,
343 this,
344 kMessageTypeCanPlayOrRecordChange,
tkchind2511962016-05-06 18:54:15 -0700345 new rtc::TypedMessageData<bool>(can_play_or_record));
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700346}
347
henrikaaf35f832017-06-16 13:22:13 +0200348void AudioDeviceIOS::OnChangedOutputVolume() {
349 RTC_DCHECK(thread_);
350 thread_->Post(RTC_FROM_HERE, this, kMessageOutputVolumeChange);
351}
352
henrikabc9ffad2017-06-01 14:25:45 +0200353OSStatus AudioDeviceIOS::OnDeliverRecordedData(AudioUnitRenderActionFlags* flags,
354 const AudioTimeStamp* time_stamp,
355 UInt32 bus_number,
356 UInt32 num_frames,
357 AudioBufferList* /* io_data */) {
henrikaaf35f832017-06-16 13:22:13 +0200358 RTC_DCHECK_RUN_ON(&io_thread_checker_);
Zeke Chin1300caa2016-03-18 14:39:11 -0700359 OSStatus result = noErr;
360 // Simply return if recording is not enabled.
Mirko Bonadei72c42502017-11-09 09:33:23 +0100361 if (!rtc::AtomicOps::AcquireLoad(&recording_)) return result;
Zeke Chin1300caa2016-03-18 14:39:11 -0700362
henrikabc9ffad2017-06-01 14:25:45 +0200363 // Set the size of our own audio buffer and clear it first to avoid copying
364 // in combination with potential reallocations.
365 // On real iOS devices, the size will only be set once (at first callback).
366 record_audio_buffer_.Clear();
henrika8d7393b2018-04-19 13:40:15 +0200367 record_audio_buffer_.SetSize(num_frames);
tkchind2511962016-05-06 18:54:15 -0700368
henrikabc9ffad2017-06-01 14:25:45 +0200369 // Allocate AudioBuffers to be used as storage for the received audio.
370 // The AudioBufferList structure works as a placeholder for the
371 // AudioBuffer structure, which holds a pointer to the actual data buffer
372 // in |record_audio_buffer_|. Recorded audio will be rendered into this memory
373 // at each input callback when calling AudioUnitRender().
374 AudioBufferList audio_buffer_list;
375 audio_buffer_list.mNumberBuffers = 1;
376 AudioBuffer* audio_buffer = &audio_buffer_list.mBuffers[0];
377 audio_buffer->mNumberChannels = record_parameters_.channels();
henrika8d7393b2018-04-19 13:40:15 +0200378 audio_buffer->mDataByteSize =
379 record_audio_buffer_.size() * VoiceProcessingAudioUnit::kBytesPerSample;
380 audio_buffer->mData = reinterpret_cast<int8_t*>(record_audio_buffer_.data());
Zeke Chin1300caa2016-03-18 14:39:11 -0700381
382 // Obtain the recorded audio samples by initiating a rendering cycle.
383 // Since it happens on the input bus, the |io_data| parameter is a reference
384 // to the preallocated audio buffer list that the audio unit renders into.
385 // We can make the audio unit provide a buffer instead in io_data, but we
386 // currently just use our own.
387 // TODO(henrika): should error handling be improved?
Mirko Bonadei72c42502017-11-09 09:33:23 +0100388 result = audio_unit_->Render(flags, time_stamp, bus_number, num_frames, &audio_buffer_list);
Zeke Chin1300caa2016-03-18 14:39:11 -0700389 if (result != noErr) {
390 RTCLogError(@"Failed to render audio.");
391 return result;
392 }
393
394 // Get a pointer to the recorded audio and send it to the WebRTC ADB.
395 // Use the FineAudioBuffer instance to convert between native buffer size
396 // and the 10ms buffer size used by WebRTC.
henrika883d00f2018-03-16 10:09:49 +0100397 fine_audio_buffer_->DeliverRecordedData(record_audio_buffer_, kFixedRecordDelayEstimate);
Zeke Chin1300caa2016-03-18 14:39:11 -0700398 return noErr;
399}
400
401OSStatus AudioDeviceIOS::OnGetPlayoutData(AudioUnitRenderActionFlags* flags,
402 const AudioTimeStamp* time_stamp,
403 UInt32 bus_number,
404 UInt32 num_frames,
405 AudioBufferList* io_data) {
henrikaaf35f832017-06-16 13:22:13 +0200406 RTC_DCHECK_RUN_ON(&io_thread_checker_);
Zeke Chin1300caa2016-03-18 14:39:11 -0700407 // Verify 16-bit, noninterleaved mono PCM signal format.
kwibergaf476c72016-11-28 15:21:39 -0800408 RTC_DCHECK_EQ(1, io_data->mNumberBuffers);
Zeke Chin1300caa2016-03-18 14:39:11 -0700409 AudioBuffer* audio_buffer = &io_data->mBuffers[0];
kwibergaf476c72016-11-28 15:21:39 -0800410 RTC_DCHECK_EQ(1, audio_buffer->mNumberChannels);
henrika7be78832017-06-13 17:34:16 +0200411
Zeke Chin1300caa2016-03-18 14:39:11 -0700412 // Produce silence and give audio unit a hint about it if playout is not
413 // activated.
414 if (!rtc::AtomicOps::AcquireLoad(&playing_)) {
henrika8d7393b2018-04-19 13:40:15 +0200415 const size_t size_in_bytes = audio_buffer->mDataByteSize;
416 RTC_CHECK_EQ(size_in_bytes / VoiceProcessingAudioUnit::kBytesPerSample, num_frames);
Zeke Chin1300caa2016-03-18 14:39:11 -0700417 *flags |= kAudioUnitRenderAction_OutputIsSilence;
henrika8d7393b2018-04-19 13:40:15 +0200418 memset(static_cast<int8_t*>(audio_buffer->mData), 0, size_in_bytes);
Zeke Chin1300caa2016-03-18 14:39:11 -0700419 return noErr;
420 }
henrika2d014be2016-06-16 14:26:55 +0200421
henrika7be78832017-06-13 17:34:16 +0200422 // Measure time since last call to OnGetPlayoutData() and see if it is larger
henrikaaf35f832017-06-16 13:22:13 +0200423 // than a well defined threshold which depends on the current IO buffer size.
424 // If so, we have an indication of a glitch in the output audio since the
425 // core audio layer will most likely run dry in this state.
henrika7be78832017-06-13 17:34:16 +0200426 ++num_playout_callbacks_;
427 const int64_t now_time = rtc::TimeMillis();
428 if (time_stamp->mSampleTime != num_frames) {
429 const int64_t delta_time = now_time - last_playout_time_;
henrikaaf35f832017-06-16 13:22:13 +0200430 const int glitch_threshold = 1.6 * playout_parameters_.GetBufferSizeInMilliseconds();
henrika7be78832017-06-13 17:34:16 +0200431 if (delta_time > glitch_threshold) {
henrikaaf35f832017-06-16 13:22:13 +0200432 RTCLogWarning(@"Possible playout audio glitch detected.\n"
433 " Time since last OnGetPlayoutData was %lld ms.\n",
henrika7be78832017-06-13 17:34:16 +0200434 delta_time);
henrikaaf35f832017-06-16 13:22:13 +0200435 // Exclude extreme delta values since they do most likely not correspond
436 // to a real glitch. Instead, the most probable cause is that a headset
437 // has been plugged in or out. There are more direct ways to detect
438 // audio device changes (see HandleValidRouteChange()) but experiments
439 // show that using it leads to more complex implementations.
440 // TODO(henrika): more tests might be needed to come up with an even
441 // better upper limit.
442 if (glitch_threshold < 120 && delta_time > 120) {
443 RTCLog(@"Glitch warning is ignored. Probably caused by device switch.");
444 } else {
445 thread_->Post(RTC_FROM_HERE, this, kMessageTypePlayoutGlitchDetected);
446 }
henrika7be78832017-06-13 17:34:16 +0200447 }
448 }
449 last_playout_time_ = now_time;
450
Zeke Chin1300caa2016-03-18 14:39:11 -0700451 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches
henrikabb6f7522017-05-30 02:01:30 -0700452 // the native I/O audio unit) and copy the result to the audio buffer in the
453 // |io_data| destination.
henrika8d7393b2018-04-19 13:40:15 +0200454 fine_audio_buffer_->GetPlayoutData(
455 rtc::ArrayView<int16_t>(static_cast<int16_t*>(audio_buffer->mData), num_frames),
456 kFixedPlayoutDelayEstimate);
Zeke Chin1300caa2016-03-18 14:39:11 -0700457 return noErr;
458}
459
Mirko Bonadei72c42502017-11-09 09:33:23 +0100460void AudioDeviceIOS::OnMessage(rtc::Message* msg) {
tkchind2511962016-05-06 18:54:15 -0700461 switch (msg->message_id) {
462 case kMessageTypeInterruptionBegin:
463 HandleInterruptionBegin();
464 break;
465 case kMessageTypeInterruptionEnd:
466 HandleInterruptionEnd();
467 break;
468 case kMessageTypeValidRouteChange:
469 HandleValidRouteChange();
470 break;
471 case kMessageTypeCanPlayOrRecordChange: {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100472 rtc::TypedMessageData<bool>* data = static_cast<rtc::TypedMessageData<bool>*>(msg->pdata);
tkchind2511962016-05-06 18:54:15 -0700473 HandleCanPlayOrRecordChange(data->data());
474 delete data;
475 break;
476 }
henrika7be78832017-06-13 17:34:16 +0200477 case kMessageTypePlayoutGlitchDetected:
478 HandlePlayoutGlitchDetected();
479 break;
henrikaaf35f832017-06-16 13:22:13 +0200480 case kMessageOutputVolumeChange:
481 HandleOutputVolumeChange();
482 break;
tkchind2511962016-05-06 18:54:15 -0700483 }
484}
485
tkchine54467f2016-03-15 16:54:03 -0700486void AudioDeviceIOS::HandleInterruptionBegin() {
henrikaaf35f832017-06-16 13:22:13 +0200487 RTC_DCHECK_RUN_ON(&thread_checker_);
Mirko Bonadei72c42502017-11-09 09:33:23 +0100488 RTCLog(@"Interruption begin. IsInterrupted changed from %d to 1.", is_interrupted_);
489 if (audio_unit_ && audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) {
tkchind2511962016-05-06 18:54:15 -0700490 RTCLog(@"Stopping the audio unit due to interruption begin.");
491 if (!audio_unit_->Stop()) {
492 RTCLogError(@"Failed to stop the audio unit for interruption begin.");
henrika09a76192017-08-23 15:04:40 +0200493 } else {
henrika79445ea2018-05-29 16:04:16 +0200494 PrepareForNewStart();
tkchind2511962016-05-06 18:54:15 -0700495 }
Zeke Chin1300caa2016-03-18 14:39:11 -0700496 }
tkchine54467f2016-03-15 16:54:03 -0700497 is_interrupted_ = true;
498}
499
500void AudioDeviceIOS::HandleInterruptionEnd() {
henrikaaf35f832017-06-16 13:22:13 +0200501 RTC_DCHECK_RUN_ON(&thread_checker_);
haysc7735b1e2017-03-29 14:53:32 -0700502 RTCLog(@"Interruption ended. IsInterrupted changed from %d to 0. "
Mirko Bonadei72c42502017-11-09 09:33:23 +0100503 "Updating audio unit state.",
504 is_interrupted_);
tkchine54467f2016-03-15 16:54:03 -0700505 is_interrupted_ = false;
tkchind2511962016-05-06 18:54:15 -0700506 UpdateAudioUnit([RTCAudioSession sharedInstance].canPlayOrRecord);
tkchine54467f2016-03-15 16:54:03 -0700507}
508
509void AudioDeviceIOS::HandleValidRouteChange() {
henrikaaf35f832017-06-16 13:22:13 +0200510 RTC_DCHECK_RUN_ON(&thread_checker_);
tkchine54467f2016-03-15 16:54:03 -0700511 RTCAudioSession* session = [RTCAudioSession sharedInstance];
henrika051d1512016-09-22 08:48:04 -0700512 RTCLog(@"%@", session);
tkchind2511962016-05-06 18:54:15 -0700513 HandleSampleRateChange(session.sampleRate);
tkchine54467f2016-03-15 16:54:03 -0700514}
515
tkchind2511962016-05-06 18:54:15 -0700516void AudioDeviceIOS::HandleCanPlayOrRecordChange(bool can_play_or_record) {
517 RTCLog(@"Handling CanPlayOrRecord change to: %d", can_play_or_record);
518 UpdateAudioUnit(can_play_or_record);
519}
520
521void AudioDeviceIOS::HandleSampleRateChange(float sample_rate) {
henrikaaf35f832017-06-16 13:22:13 +0200522 RTC_DCHECK_RUN_ON(&thread_checker_);
tkchind2511962016-05-06 18:54:15 -0700523 RTCLog(@"Handling sample rate change to %f.", sample_rate);
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700524
tkchind2511962016-05-06 18:54:15 -0700525 // Don't do anything if we're interrupted.
526 if (is_interrupted_) {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100527 RTCLog(@"Ignoring sample rate change to %f due to interruption.", sample_rate);
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700528 return;
tkchind2511962016-05-06 18:54:15 -0700529 }
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700530
tkchind2511962016-05-06 18:54:15 -0700531 // If we don't have an audio unit yet, or the audio unit is uninitialized,
532 // there is no work to do.
Mirko Bonadei72c42502017-11-09 09:33:23 +0100533 if (!audio_unit_ || audio_unit_->GetState() < VoiceProcessingAudioUnit::kInitialized) {
tkchind2511962016-05-06 18:54:15 -0700534 return;
535 }
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700536
tkchind2511962016-05-06 18:54:15 -0700537 // The audio unit is already initialized or started.
538 // Check to see if the sample rate or buffer size has changed.
539 RTCAudioSession* session = [RTCAudioSession sharedInstance];
540 const double session_sample_rate = session.sampleRate;
541 const NSTimeInterval session_buffer_duration = session.IOBufferDuration;
542 const size_t session_frames_per_buffer =
543 static_cast<size_t>(session_sample_rate * session_buffer_duration + .5);
544 const double current_sample_rate = playout_parameters_.sample_rate();
Mirko Bonadei72c42502017-11-09 09:33:23 +0100545 const size_t current_frames_per_buffer = playout_parameters_.frames_per_buffer();
tkchind2511962016-05-06 18:54:15 -0700546 RTCLog(@"Handling playout sample rate change to: %f\n"
547 " Session sample rate: %f frames_per_buffer: %lu\n"
548 " ADM sample rate: %f frames_per_buffer: %lu",
549 sample_rate,
henrika7be78832017-06-13 17:34:16 +0200550 session_sample_rate,
551 (unsigned long)session_frames_per_buffer,
552 current_sample_rate,
553 (unsigned long)current_frames_per_buffer);
tkchind2511962016-05-06 18:54:15 -0700554
555 // Sample rate and buffer size are the same, no work to do.
jianjun.zhua84aa572016-10-05 19:19:23 -0700556 if (std::abs(current_sample_rate - session_sample_rate) <= DBL_EPSILON &&
tkchind2511962016-05-06 18:54:15 -0700557 current_frames_per_buffer == session_frames_per_buffer) {
henrikaaf35f832017-06-16 13:22:13 +0200558 RTCLog(@"Ignoring sample rate change since audio parameters are intact.");
tkchind2511962016-05-06 18:54:15 -0700559 return;
560 }
561
henrikaabcf1122017-07-13 04:42:50 -0700562 // Extra sanity check to ensure that the new sample rate is valid.
563 if (session_sample_rate <= 0.0) {
564 RTCLogError(@"Sample rate is invalid: %f", session_sample_rate);
565 return;
566 }
567
tkchind2511962016-05-06 18:54:15 -0700568 // We need to adjust our format and buffer sizes.
569 // The stream format is about to be changed and it requires that we first
570 // stop and uninitialize the audio unit to deallocate its resources.
571 RTCLog(@"Stopping and uninitializing audio unit to adjust buffers.");
572 bool restart_audio_unit = false;
573 if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kStarted) {
574 audio_unit_->Stop();
575 restart_audio_unit = true;
henrika79445ea2018-05-29 16:04:16 +0200576 PrepareForNewStart();
tkchind2511962016-05-06 18:54:15 -0700577 }
578 if (audio_unit_->GetState() == VoiceProcessingAudioUnit::kInitialized) {
579 audio_unit_->Uninitialize();
580 }
581
582 // Allocate new buffers given the new stream format.
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700583 SetupAudioBuffersForActiveAudioSession();
584
tkchind2511962016-05-06 18:54:15 -0700585 // Initialize the audio unit again with the new sample rate.
586 RTC_DCHECK_EQ(playout_parameters_.sample_rate(), session_sample_rate);
587 if (!audio_unit_->Initialize(session_sample_rate)) {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100588 RTCLogError(@"Failed to initialize the audio unit with sample rate: %f", session_sample_rate);
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700589 return;
590 }
591
tkchind2511962016-05-06 18:54:15 -0700592 // Restart the audio unit if it was already running.
593 if (restart_audio_unit && !audio_unit_->Start()) {
Mirko Bonadei72c42502017-11-09 09:33:23 +0100594 RTCLogError(@"Failed to start audio unit with sample rate: %f", session_sample_rate);
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700595 return;
596 }
tkchind2511962016-05-06 18:54:15 -0700597 RTCLog(@"Successfully handled sample rate change.");
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700598}
599
henrika7be78832017-06-13 17:34:16 +0200600void AudioDeviceIOS::HandlePlayoutGlitchDetected() {
henrikaaf35f832017-06-16 13:22:13 +0200601 RTC_DCHECK_RUN_ON(&thread_checker_);
602 // Don't update metrics if we're interrupted since a "glitch" is expected
603 // in this state.
604 if (is_interrupted_) {
605 RTCLog(@"Ignoring audio glitch due to interruption.");
606 return;
607 }
608 // Avoid doing glitch detection for two seconds after a volume change
609 // has been detected to reduce the risk of false alarm.
610 if (last_output_volume_change_time_ > 0 &&
611 rtc::TimeSince(last_output_volume_change_time_) < 2000) {
612 RTCLog(@"Ignoring audio glitch due to recent output volume change.");
613 return;
614 }
henrika7be78832017-06-13 17:34:16 +0200615 num_detected_playout_glitches_++;
Mirko Bonadei72c42502017-11-09 09:33:23 +0100616 RTCLog(@"Number of detected playout glitches: %lld", num_detected_playout_glitches_);
Anders Carlsson121ea322017-06-26 15:34:47 +0200617
618 int64_t glitch_count = num_detected_playout_glitches_;
619 dispatch_async(dispatch_get_main_queue(), ^{
620 RTCAudioSession* session = [RTCAudioSession sharedInstance];
621 [session notifyDidDetectPlayoutGlitch:glitch_count];
622 });
henrika7be78832017-06-13 17:34:16 +0200623}
624
henrikaaf35f832017-06-16 13:22:13 +0200625void AudioDeviceIOS::HandleOutputVolumeChange() {
626 RTC_DCHECK_RUN_ON(&thread_checker_);
627 RTCLog(@"Output volume change detected.");
628 // Store time of this detection so it can be used to defer detection of
629 // glitches too close in time to this event.
630 last_output_volume_change_time_ = rtc::TimeMillis();
631}
632
henrika86d907c2015-09-07 16:09:50 +0200633void AudioDeviceIOS::UpdateAudioDeviceBuffer() {
634 LOGI() << "UpdateAudioDevicebuffer";
635 // AttachAudioBuffer() is called at construction by the main class but check
636 // just in case.
henrika8c471e72015-10-01 07:36:45 -0700637 RTC_DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first";
henrikae6aca632018-01-03 13:58:58 +0100638 RTC_DCHECK_GT(playout_parameters_.sample_rate(), 0);
639 RTC_DCHECK_GT(record_parameters_.sample_rate(), 0);
640 RTC_DCHECK_EQ(playout_parameters_.channels(), 1);
641 RTC_DCHECK_EQ(record_parameters_.channels(), 1);
henrika86d907c2015-09-07 16:09:50 +0200642 // Inform the audio device buffer (ADB) about the new audio format.
henrika8c471e72015-10-01 07:36:45 -0700643 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate());
644 audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels());
Mirko Bonadei72c42502017-11-09 09:33:23 +0100645 audio_device_buffer_->SetRecordingSampleRate(record_parameters_.sample_rate());
henrika8c471e72015-10-01 07:36:45 -0700646 audio_device_buffer_->SetRecordingChannels(record_parameters_.channels());
henrika86d907c2015-09-07 16:09:50 +0200647}
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000648
henrika86d907c2015-09-07 16:09:50 +0200649void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() {
650 LOGI() << "SetupAudioBuffersForActiveAudioSession";
henrika86d907c2015-09-07 16:09:50 +0200651 // Verify the current values once the audio session has been activated.
Zeke Chinb3fb71c2016-02-18 15:44:07 -0800652 RTCAudioSession* session = [RTCAudioSession sharedInstance];
tkchine54467f2016-03-15 16:54:03 -0700653 double sample_rate = session.sampleRate;
654 NSTimeInterval io_buffer_duration = session.IOBufferDuration;
Zeke Chin1300caa2016-03-18 14:39:11 -0700655 RTCLog(@"%@", session);
henrika45c136b2015-10-21 04:11:53 -0700656
henrika86d907c2015-09-07 16:09:50 +0200657 // Log a warning message for the case when we are unable to set the preferred
658 // hardware sample rate but continue and use the non-ideal sample rate after
henrika45c136b2015-10-21 04:11:53 -0700659 // reinitializing the audio parameters. Most BT headsets only support 8kHz or
660 // 16kHz.
Mirko Bonadei72c42502017-11-09 09:33:23 +0100661 RTCAudioSessionConfiguration* webRTCConfig = [RTCAudioSessionConfiguration webRTCConfiguration];
tkchine54467f2016-03-15 16:54:03 -0700662 if (sample_rate != webRTCConfig.sampleRate) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100663 RTC_LOG(LS_WARNING) << "Unable to set the preferred sample rate";
henrikaba35d052015-07-14 17:04:08 +0200664 }
andrew@webrtc.org73a702c2013-01-30 21:18:31 +0000665
henrikafb09eeb2017-11-22 14:25:25 +0100666 // Crash reports indicates that it can happen in rare cases that the reported
667 // sample rate is less than or equal to zero. If that happens and if a valid
668 // sample rate has already been set during initialization, the best guess we
669 // can do is to reuse the current sample rate.
670 if (sample_rate <= DBL_EPSILON && playout_parameters_.sample_rate() > 0) {
671 RTCLogError(@"Reported rate is invalid: %f. "
672 "Using %d as sample rate instead.",
673 sample_rate, playout_parameters_.sample_rate());
674 sample_rate = playout_parameters_.sample_rate();
675 }
676
henrika86d907c2015-09-07 16:09:50 +0200677 // At this stage, we also know the exact IO buffer duration and can add
678 // that info to the existing audio parameters where it is converted into
679 // number of audio frames.
680 // Example: IO buffer size = 0.008 seconds <=> 128 audio frames at 16kHz.
681 // Hence, 128 is the size we expect to see in upcoming render callbacks.
Mirko Bonadei72c42502017-11-09 09:33:23 +0100682 playout_parameters_.reset(sample_rate, playout_parameters_.channels(), io_buffer_duration);
henrika8c471e72015-10-01 07:36:45 -0700683 RTC_DCHECK(playout_parameters_.is_complete());
Mirko Bonadei72c42502017-11-09 09:33:23 +0100684 record_parameters_.reset(sample_rate, record_parameters_.channels(), io_buffer_duration);
henrika8c471e72015-10-01 07:36:45 -0700685 RTC_DCHECK(record_parameters_.is_complete());
Mirko Bonadei675513b2017-11-09 11:09:25 +0100686 RTC_LOG(LS_INFO) << " frames per I/O buffer: " << playout_parameters_.frames_per_buffer();
687 RTC_LOG(LS_INFO) << " bytes per I/O buffer: " << playout_parameters_.GetBytesPerBuffer();
Mirko Bonadei72c42502017-11-09 09:33:23 +0100688 RTC_DCHECK_EQ(playout_parameters_.GetBytesPerBuffer(), record_parameters_.GetBytesPerBuffer());
andrew@webrtc.org73a702c2013-01-30 21:18:31 +0000689
henrika86d907c2015-09-07 16:09:50 +0200690 // Update the ADB parameters since the sample rate might have changed.
691 UpdateAudioDeviceBuffer();
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000692
henrika86d907c2015-09-07 16:09:50 +0200693 // Create a modified audio buffer class which allows us to ask for,
694 // or deliver, any number of samples (and not only multiple of 10ms) to match
henrika29e865a2018-04-24 13:22:31 +0200695 // the native audio unit buffer size.
henrika8c471e72015-10-01 07:36:45 -0700696 RTC_DCHECK(audio_device_buffer_);
henrika29e865a2018-04-24 13:22:31 +0200697 fine_audio_buffer_.reset(new FineAudioBuffer(audio_device_buffer_));
henrika86d907c2015-09-07 16:09:50 +0200698}
699
Zeke Chin1300caa2016-03-18 14:39:11 -0700700bool AudioDeviceIOS::CreateAudioUnit() {
701 RTC_DCHECK(!audio_unit_);
henrikac7290322015-12-02 10:46:46 +0100702
Zeke Chin1300caa2016-03-18 14:39:11 -0700703 audio_unit_.reset(new VoiceProcessingAudioUnit(this));
704 if (!audio_unit_->Init()) {
705 audio_unit_.reset();
henrika34911ad2015-11-20 15:47:09 +0100706 return false;
707 }
henrika86d907c2015-09-07 16:09:50 +0200708
henrika86d907c2015-09-07 16:09:50 +0200709 return true;
710}
711
tkchind2511962016-05-06 18:54:15 -0700712void AudioDeviceIOS::UpdateAudioUnit(bool can_play_or_record) {
henrikaaf35f832017-06-16 13:22:13 +0200713 RTC_DCHECK_RUN_ON(&thread_checker_);
tkchind2511962016-05-06 18:54:15 -0700714 RTCLog(@"Updating audio unit state. CanPlayOrRecord=%d IsInterrupted=%d",
Mirko Bonadei72c42502017-11-09 09:33:23 +0100715 can_play_or_record,
716 is_interrupted_);
Zeke Chin1300caa2016-03-18 14:39:11 -0700717
tkchind2511962016-05-06 18:54:15 -0700718 if (is_interrupted_) {
719 RTCLog(@"Ignoring audio unit update due to interruption.");
720 return;
Zeke Chin1300caa2016-03-18 14:39:11 -0700721 }
henrika45c136b2015-10-21 04:11:53 -0700722
tkchind2511962016-05-06 18:54:15 -0700723 // If we're not initialized we don't need to do anything. Audio unit will
724 // be initialized on initialization.
Mirko Bonadei72c42502017-11-09 09:33:23 +0100725 if (!audio_is_initialized_) return;
tkchind2511962016-05-06 18:54:15 -0700726
727 // If we're initialized, we must have an audio unit.
728 RTC_DCHECK(audio_unit_);
729
730 bool should_initialize_audio_unit = false;
731 bool should_uninitialize_audio_unit = false;
732 bool should_start_audio_unit = false;
733 bool should_stop_audio_unit = false;
734
735 switch (audio_unit_->GetState()) {
736 case VoiceProcessingAudioUnit::kInitRequired:
henrika86eff722016-06-21 11:26:47 +0200737 RTCLog(@"VPAU state: InitRequired");
tkchind2511962016-05-06 18:54:15 -0700738 RTC_NOTREACHED();
739 break;
740 case VoiceProcessingAudioUnit::kUninitialized:
henrika86eff722016-06-21 11:26:47 +0200741 RTCLog(@"VPAU state: Uninitialized");
tkchind2511962016-05-06 18:54:15 -0700742 should_initialize_audio_unit = can_play_or_record;
Mirko Bonadei72c42502017-11-09 09:33:23 +0100743 should_start_audio_unit = should_initialize_audio_unit && (playing_ || recording_);
tkchind2511962016-05-06 18:54:15 -0700744 break;
745 case VoiceProcessingAudioUnit::kInitialized:
henrika86eff722016-06-21 11:26:47 +0200746 RTCLog(@"VPAU state: Initialized");
Mirko Bonadei72c42502017-11-09 09:33:23 +0100747 should_start_audio_unit = can_play_or_record && (playing_ || recording_);
tkchind2511962016-05-06 18:54:15 -0700748 should_uninitialize_audio_unit = !can_play_or_record;
749 break;
750 case VoiceProcessingAudioUnit::kStarted:
henrika86eff722016-06-21 11:26:47 +0200751 RTCLog(@"VPAU state: Started");
tkchind2511962016-05-06 18:54:15 -0700752 RTC_DCHECK(playing_ || recording_);
753 should_stop_audio_unit = !can_play_or_record;
754 should_uninitialize_audio_unit = should_stop_audio_unit;
755 break;
Zeke Chin1300caa2016-03-18 14:39:11 -0700756 }
henrika45c136b2015-10-21 04:11:53 -0700757
tkchind2511962016-05-06 18:54:15 -0700758 if (should_initialize_audio_unit) {
759 RTCLog(@"Initializing audio unit for UpdateAudioUnit");
760 ConfigureAudioSession();
761 SetupAudioBuffersForActiveAudioSession();
762 if (!audio_unit_->Initialize(playout_parameters_.sample_rate())) {
763 RTCLogError(@"Failed to initialize audio unit.");
764 return;
765 }
Zeke Chin1300caa2016-03-18 14:39:11 -0700766 }
henrika45c136b2015-10-21 04:11:53 -0700767
tkchind2511962016-05-06 18:54:15 -0700768 if (should_start_audio_unit) {
769 RTCLog(@"Starting audio unit for UpdateAudioUnit");
henrika86eff722016-06-21 11:26:47 +0200770 // Log session settings before trying to start audio streaming.
771 RTCAudioSession* session = [RTCAudioSession sharedInstance];
772 RTCLog(@"%@", session);
tkchind2511962016-05-06 18:54:15 -0700773 if (!audio_unit_->Start()) {
774 RTCLogError(@"Failed to start audio unit.");
775 return;
776 }
Zeke Chin1300caa2016-03-18 14:39:11 -0700777 }
henrika45c136b2015-10-21 04:11:53 -0700778
tkchind2511962016-05-06 18:54:15 -0700779 if (should_stop_audio_unit) {
780 RTCLog(@"Stopping audio unit for UpdateAudioUnit");
781 if (!audio_unit_->Stop()) {
782 RTCLogError(@"Failed to stop audio unit.");
783 return;
784 }
785 }
786
787 if (should_uninitialize_audio_unit) {
788 RTCLog(@"Uninitializing audio unit for UpdateAudioUnit");
789 audio_unit_->Uninitialize();
790 UnconfigureAudioSession();
791 }
792}
793
jttehf84c1d62017-04-21 13:56:39 -0700794bool AudioDeviceIOS::ConfigureAudioSession() {
henrikaaf35f832017-06-16 13:22:13 +0200795 RTC_DCHECK_RUN_ON(&thread_checker_);
tkchind2511962016-05-06 18:54:15 -0700796 RTCLog(@"Configuring audio session.");
797 if (has_configured_session_) {
798 RTCLogWarning(@"Audio session already configured.");
jttehf84c1d62017-04-21 13:56:39 -0700799 return false;
tkchind2511962016-05-06 18:54:15 -0700800 }
801 RTCAudioSession* session = [RTCAudioSession sharedInstance];
802 [session lockForConfiguration];
jttehf84c1d62017-04-21 13:56:39 -0700803 bool success = [session configureWebRTCSession:nil];
tkchind2511962016-05-06 18:54:15 -0700804 [session unlockForConfiguration];
jttehf84c1d62017-04-21 13:56:39 -0700805 if (success) {
806 has_configured_session_ = true;
807 RTCLog(@"Configured audio session.");
808 } else {
809 RTCLog(@"Failed to configure audio session.");
810 }
811 return success;
tkchind2511962016-05-06 18:54:15 -0700812}
813
814void AudioDeviceIOS::UnconfigureAudioSession() {
henrikaaf35f832017-06-16 13:22:13 +0200815 RTC_DCHECK_RUN_ON(&thread_checker_);
tkchind2511962016-05-06 18:54:15 -0700816 RTCLog(@"Unconfiguring audio session.");
817 if (!has_configured_session_) {
818 RTCLogWarning(@"Audio session already unconfigured.");
819 return;
820 }
821 RTCAudioSession* session = [RTCAudioSession sharedInstance];
822 [session lockForConfiguration];
823 [session unconfigureWebRTCSession:nil];
824 [session unlockForConfiguration];
825 has_configured_session_ = false;
826 RTCLog(@"Unconfigured audio session.");
henrika45c136b2015-10-21 04:11:53 -0700827}
828
henrika86d907c2015-09-07 16:09:50 +0200829bool AudioDeviceIOS::InitPlayOrRecord() {
830 LOGI() << "InitPlayOrRecord";
henrikaaf35f832017-06-16 13:22:13 +0200831 RTC_DCHECK_RUN_ON(&thread_checker_);
henrika34911ad2015-11-20 15:47:09 +0100832
tkchind2511962016-05-06 18:54:15 -0700833 // There should be no audio unit at this point.
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700834 if (!CreateAudioUnit()) {
henrika34911ad2015-11-20 15:47:09 +0100835 return false;
836 }
henrika86d907c2015-09-07 16:09:50 +0200837
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700838 RTCAudioSession* session = [RTCAudioSession sharedInstance];
839 // Subscribe to audio session events.
tkchine54467f2016-03-15 16:54:03 -0700840 [session pushDelegate:audio_session_observer_];
jtteh5171a7f2017-05-09 15:09:37 -0700841 is_interrupted_ = session.isInterrupted ? true : false;
henrika45c136b2015-10-21 04:11:53 -0700842
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700843 // Lock the session to make configuration changes.
844 [session lockForConfiguration];
845 NSError* error = nil;
846 if (![session beginWebRTCSession:&error]) {
tkchin9f987d32016-03-12 20:06:28 -0800847 [session unlockForConfiguration];
Mirko Bonadei72c42502017-11-09 09:33:23 +0100848 RTCLogError(@"Failed to begin WebRTC session: %@", error.localizedDescription);
Jiawei Ou5f7d00e2018-07-30 09:44:16 -0700849 audio_unit_.reset();
henrika86d907c2015-09-07 16:09:50 +0200850 return false;
henrikaba35d052015-07-14 17:04:08 +0200851 }
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700852
henrikae6aca632018-01-03 13:58:58 +0100853 // If we are ready to play or record, and if the audio session can be
854 // configured, then initialize the audio unit.
tkchind2511962016-05-06 18:54:15 -0700855 if (session.canPlayOrRecord) {
henrikae6aca632018-01-03 13:58:58 +0100856 if (!ConfigureAudioSession()) {
857 // One possible reason for failure is if an attempt was made to use the
858 // audio session during or after a Media Services failure.
859 // See AVAudioSessionErrorCodeMediaServicesFailed for details.
860 [session unlockForConfiguration];
Jiawei Ou5f7d00e2018-07-30 09:44:16 -0700861 audio_unit_.reset();
henrikae6aca632018-01-03 13:58:58 +0100862 return false;
863 }
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700864 SetupAudioBuffersForActiveAudioSession();
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700865 audio_unit_->Initialize(playout_parameters_.sample_rate());
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700866 }
867
868 // Release the lock.
tkchin9f987d32016-03-12 20:06:28 -0800869 [session unlockForConfiguration];
henrika86d907c2015-09-07 16:09:50 +0200870 return true;
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000871}
872
henrika34911ad2015-11-20 15:47:09 +0100873void AudioDeviceIOS::ShutdownPlayOrRecord() {
henrikaba35d052015-07-14 17:04:08 +0200874 LOGI() << "ShutdownPlayOrRecord";
henrikaaf35f832017-06-16 13:22:13 +0200875 RTC_DCHECK_RUN_ON(&thread_checker_);
Zeke Chin1300caa2016-03-18 14:39:11 -0700876
henrika41ed7e12016-06-21 11:41:07 +0200877 // Stop the audio unit to prevent any additional audio callbacks.
878 audio_unit_->Stop();
879
henrika86d907c2015-09-07 16:09:50 +0200880 // Close and delete the voice-processing I/O unit.
tkchind2511962016-05-06 18:54:15 -0700881 audio_unit_.reset();
henrika45c136b2015-10-21 04:11:53 -0700882
henrika3d0e7bb2017-06-28 15:17:17 +0200883 // Detach thread checker for the AURemoteIO::IOThread to ensure that the
884 // next session uses a fresh thread id.
885 io_thread_checker_.DetachFromThread();
886
henrika34911ad2015-11-20 15:47:09 +0100887 // Remove audio session notification observers.
tkchine54467f2016-03-15 16:54:03 -0700888 RTCAudioSession* session = [RTCAudioSession sharedInstance];
889 [session removeDelegate:audio_session_observer_];
henrika34911ad2015-11-20 15:47:09 +0100890
henrika324d9c92015-07-20 13:09:23 +0200891 // All I/O should be stopped or paused prior to deactivating the audio
892 // session, hence we deactivate as last action.
tkchin9f987d32016-03-12 20:06:28 -0800893 [session lockForConfiguration];
tkchind2511962016-05-06 18:54:15 -0700894 UnconfigureAudioSession();
Tze Kwang Chin307a0922016-03-21 13:57:40 -0700895 [session endWebRTCSession:nil];
tkchin9f987d32016-03-12 20:06:28 -0800896 [session unlockForConfiguration];
henrika34911ad2015-11-20 15:47:09 +0100897}
898
henrika79445ea2018-05-29 16:04:16 +0200899void AudioDeviceIOS::PrepareForNewStart() {
900 LOGI() << "PrepareForNewStart";
901 // The audio unit has been stopped and preparations are needed for an upcoming
902 // restart. It will result in audio callbacks from a new native I/O thread
903 // which means that we must detach thread checkers here to be prepared for an
904 // upcoming new audio stream.
905 io_thread_checker_.DetachFromThread();
906 // The audio device buffer must also be informed about the interrupted
907 // state so it can detach its thread checkers as well.
908 if (audio_device_buffer_) {
909 audio_device_buffer_->NativeAudioPlayoutInterrupted();
910 audio_device_buffer_->NativeAudioRecordingInterrupted();
911 }
912}
913
sjlee@webrtc.org4b425082012-09-10 17:58:21 +0000914} // namespace webrtc