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 #include "webrtc/modules/utility/interface/helpers_ios.h" | |
20 | 19 |
20 #include "webrtc/base/atomicops.h" | |
21 #include "webrtc/base/checks.h" | 21 #include "webrtc/base/checks.h" |
22 #include "webrtc/base/logging.h" | 22 #include "webrtc/base/logging.h" |
23 #include "webrtc/system_wrappers/interface/trace.h" | 23 #include "webrtc/modules/audio_device/fine_audio_buffer.h" |
24 #include "webrtc/modules/utility/interface/helpers_ios.h" | |
24 | 25 |
25 namespace webrtc { | 26 namespace webrtc { |
26 | 27 |
27 #define LOGI() LOG(LS_INFO) << "AudioDeviceIOS::" | 28 #define LOGI() LOG(LS_INFO) << "AudioDeviceIOS::" |
28 | 29 |
30 #define LOG_AND_RETURN_IF_ERROR(error, message) \ | |
31 do { \ | |
32 OSStatus err = error; \ | |
33 if (err) { \ | |
34 LOG(LS_ERROR) << message << ": " << OSStatusToString(err).get(); \ | |
35 return false; \ | |
36 } \ | |
37 } while (0) | |
38 | |
39 // Preferred hardware sample rate (unit is in Hertz). The client sample rate | |
40 // will be set to this value as well to avoid resampling the the audio unit's | |
41 // format converter. Note that, some devices, e.g. BT headsets, only supports | |
42 // 8000Hz as native sample rate. | |
43 const double kPreferredSampleRate = 48000.0; | |
44 // Use a hardware I/O buffer size (unit is in seconds) that matches the 10ms | |
45 // size used by WebRTC. The exact actual size will differ between devices. | |
46 // Example: using 48kHz on iPhone 6 results in a native buffer size of | |
47 // ~10.6667ms or 512 audio frames per buffer. The FineAudioBuffer instance will | |
48 // take care of any buffering required to convert between native buffers and | |
49 // buffers used by WebRTC. It is beneficial for the performance if the native | |
50 // size is as close to 10ms as possible since it results in "clean" callback | |
51 // sequence without bursts of callbacks back to back. | |
52 const double kPreferredIOBufferDuration = 0.01; | |
53 // Try to use mono to save resources. Also avoids channel format conversion | |
54 // in the I/O audio unit. Initial tests have shown that it is possible to use | |
55 // mono natively for built-in microphones and for BT headsets but not for | |
56 // wired headsets. Wired headsets only support stereo as native channel format | |
57 // but it is a low cost operation to do a format conversion to mono in the | |
58 // audio unit. Hence, we will not hit a CHECK in | |
59 // VerifyAudioParametersForActiveAudioSession() for a mismatch between the | |
60 // preferred number of channels and the actual number of channels. | |
61 const int kPreferredNumberOfChannels = 1; | |
62 // Number of bytes per audio sample for 16-bit signed integer representation. | |
63 const UInt32 kBytesPerSample = 2; | |
64 // Hardcoded delay estimates based on real measurements. | |
65 // TODO(henrika): these value is not used in combination with built-in AEC. | |
66 // Can most likely be removed. | |
67 const UInt16 kFixedPlayoutDelayEstimate = 30; | |
68 const UInt16 kFixedRecordDelayEstimate = 30; | |
69 | |
70 // This utility class is taken from PublicUtility/CAXException.h. | |
tkchin_webrtc
2015/09/04 22:33:05
I don't think we can do this. Can't copy code from
henrika_webrtc
2015/09/07 12:42:17
Thanks. Now removed.
| |
71 // It converts an OSStatus error code into the most suitable format to print. | |
72 class OSStatusToString { | |
73 public: | |
74 OSStatusToString(OSStatus error) { | |
75 // See if it appears to be a 4-char-code. | |
76 char* str = str_; | |
77 *(UInt32*)(str + 1) = CFSwapInt32HostToBig(error); | |
78 if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && | |
79 isprint(str[4])) { | |
80 str[0] = str[5] = '\''; | |
81 str[6] = '\0'; | |
82 } else if (error > -200000 && error < 200000) | |
83 // Format it as an integer. | |
84 sprintf(str, "%d", (int)error); | |
85 else { | |
86 // Format it as hex code. | |
87 sprintf(str, "0x%x", (int)error); | |
88 } | |
89 } | |
90 const char* get() const { return str_; } | |
91 operator const char*() const { return str_; } | |
92 | |
93 private: | |
94 char str_[16]; | |
95 }; | |
96 | |
29 using ios::CheckAndLogError; | 97 using ios::CheckAndLogError; |
30 | 98 |
99 // Activates an audio session suitable for full duplex VoIP sessions when | |
100 // |activate| is true. Also sets the preferred sample rate and IO buffer | |
101 // duration. Deactivates an active audio session if |activate| is set to false. | |
31 static void ActivateAudioSession(AVAudioSession* session, bool activate) { | 102 static void ActivateAudioSession(AVAudioSession* session, bool activate) { |
32 LOG(LS_INFO) << "ActivateAudioSession(" << activate << ")"; | 103 LOG(LS_INFO) << "ActivateAudioSession(" << activate << ")"; |
33 @autoreleasepool { | 104 @autoreleasepool { |
34 NSError* error = nil; | 105 NSError* error = nil; |
35 BOOL success = NO; | 106 BOOL success = NO; |
107 // Deactivate the audio session and return if |activate| is false. | |
36 if (!activate) { | 108 if (!activate) { |
37 // Deactivate the audio session. | |
38 success = [session setActive:NO error:&error]; | 109 success = [session setActive:NO error:&error]; |
39 DCHECK(CheckAndLogError(success, error)); | 110 DCHECK(CheckAndLogError(success, error)); |
40 return; | 111 return; |
41 } | 112 } |
42 // Activate an audio session and set category and mode. Only make changes | 113 // Use a category which supports simultaneous recording and playback. |
43 // if needed since setting them to the value they already have will clear | 114 // By default, using this category implies that our app’s audio is |
44 // transient properties (such as PortOverride) that some other component | 115 // nonmixable, hence activating the session will interrupt any other |
45 // have set up. | 116 // audio sessions which are also nonmixable. |
46 if (session.category != AVAudioSessionCategoryPlayAndRecord) { | 117 if (session.category != AVAudioSessionCategoryPlayAndRecord) { |
47 error = nil; | 118 error = nil; |
48 success = [session setCategory:AVAudioSessionCategoryPlayAndRecord | 119 success = [session setCategory:AVAudioSessionCategoryPlayAndRecord |
49 error:&error]; | 120 error:&error]; |
50 DCHECK(CheckAndLogError(success, error)); | 121 DCHECK(CheckAndLogError(success, error)); |
51 } | 122 } |
123 // Specify mode for two-way voice communication (e.g. VoIP). | |
52 if (session.mode != AVAudioSessionModeVoiceChat) { | 124 if (session.mode != AVAudioSessionModeVoiceChat) { |
53 error = nil; | 125 error = nil; |
54 success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; | 126 success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; |
55 DCHECK(CheckAndLogError(success, error)); | 127 DCHECK(CheckAndLogError(success, error)); |
56 } | 128 } |
129 // Set the session's sample rate or the hardware sample rate. | |
130 // It is essential that we use the same sample rate as stream format | |
131 // to ensure that the I/O unit does not have to do sample rate conversion. | |
132 error = nil; | |
133 success = | |
134 [session setPreferredSampleRate:kPreferredSampleRate error:&error]; | |
135 DCHECK(CheckAndLogError(success, error)); | |
136 // Set the preferred audio I/O buffer duration, in seconds. | |
137 // TODO(henrika): add more comments here. | |
138 error = nil; | |
139 success = [session setPreferredIOBufferDuration:kPreferredIOBufferDuration | |
140 error:&error]; | |
141 DCHECK(CheckAndLogError(success, error)); | |
142 | |
143 // TODO(henrika): add observers here... | |
144 | |
145 // Activate the audio session. Activation can fail if another active audio | |
146 // session (e.g. phone call) has higher priority than ours. | |
57 error = nil; | 147 error = nil; |
58 success = [session setActive:YES error:&error]; | 148 success = [session setActive:YES error:&error]; |
59 DCHECK(CheckAndLogError(success, error)); | 149 DCHECK(CheckAndLogError(success, error)); |
150 CHECK(session.isInputAvailable) << "No input path is available!"; | |
60 // Ensure that category and mode are actually activated. | 151 // Ensure that category and mode are actually activated. |
61 DCHECK( | 152 DCHECK( |
62 [session.category isEqualToString:AVAudioSessionCategoryPlayAndRecord]); | 153 [session.category isEqualToString:AVAudioSessionCategoryPlayAndRecord]); |
63 DCHECK([session.mode isEqualToString:AVAudioSessionModeVoiceChat]); | 154 DCHECK([session.mode isEqualToString:AVAudioSessionModeVoiceChat]); |
64 } | 155 // Try to set the preferred number of hardware audio channels. These calls |
65 } | 156 // must be done after setting the audio session’s category and mode and |
66 | 157 // activating the session. |
67 // Query hardware characteristics, such as input and output latency, input and | 158 // We try to use mono in both directions to save resources and format |
68 // output channel count, hardware sample rate, hardware volume setting, and | 159 // conversions in the audio unit. Some devices does only support stereo; |
69 // whether audio input is available. To obtain meaningful values for hardware | 160 // e.g. wired headset on iPhone 6. |
70 // characteristics,the audio session must be initialized and active before we | 161 // TODO(henrika): add support for stereo if needed. |
71 // query the values. | 162 error = nil; |
72 // TODO(henrika): Note that these characteristics can change at runtime. For | 163 success = |
73 // instance, input sample rate may change when a user plugs in a headset. | 164 [session setPreferredInputNumberOfChannels:kPreferredNumberOfChannels |
74 static void GetHardwareAudioParameters(AudioParameters* playout_parameters, | 165 error:&error]; |
75 AudioParameters* record_parameters) { | 166 DCHECK(CheckAndLogError(success, error)); |
76 LOG(LS_INFO) << "GetHardwareAudioParameters"; | 167 error = nil; |
77 @autoreleasepool { | 168 success = |
78 // Implicit initialization happens when we obtain a reference to the | 169 [session setPreferredOutputNumberOfChannels:kPreferredNumberOfChannels |
79 // AVAudioSession object. | 170 error:&error]; |
80 AVAudioSession* session = [AVAudioSession sharedInstance]; | 171 DCHECK(CheckAndLogError(success, error)); |
81 // Always get values when the audio session is active. | |
82 ActivateAudioSession(session, true); | |
83 CHECK(session.isInputAvailable) << "No input path is available!"; | |
84 // Get current hardware parameters. | |
85 double sample_rate = (double)session.sampleRate; | |
86 double io_buffer_duration = (double)session.IOBufferDuration; | |
87 int output_channels = (int)session.outputNumberOfChannels; | |
88 int input_channels = (int)session.inputNumberOfChannels; | |
89 size_t frames_per_buffer = | |
90 static_cast<size_t>(sample_rate * io_buffer_duration + 0.5); | |
91 // Copy hardware parameters to output parameters. | |
92 playout_parameters->reset(sample_rate, output_channels, frames_per_buffer); | |
93 record_parameters->reset(sample_rate, input_channels, frames_per_buffer); | |
94 // Add logging for debugging purposes. | |
95 LOG(LS_INFO) << " sample rate: " << sample_rate; | |
96 LOG(LS_INFO) << " IO buffer duration: " << io_buffer_duration; | |
97 LOG(LS_INFO) << " frames_per_buffer: " << frames_per_buffer; | |
98 LOG(LS_INFO) << " output channels: " << output_channels; | |
99 LOG(LS_INFO) << " input channels: " << input_channels; | |
100 LOG(LS_INFO) << " output latency: " << (double)session.outputLatency; | |
101 LOG(LS_INFO) << " input latency: " << (double)session.inputLatency; | |
102 // Don't keep the audio session active. Instead, deactivate when needed. | |
103 ActivateAudioSession(session, false); | |
104 // TODO(henrika): to be extra safe, we can do more here. E.g., set | |
105 // preferred values for sample rate, channels etc., re-activate an audio | |
106 // session and verify the actual values again. Then we know for sure that | |
107 // the current values will in fact be correct. Or, we can skip all this | |
108 // and check setting when audio is started. Probably better. | |
109 } | 172 } |
110 } | 173 } |
111 | 174 |
112 #if !defined(NDEBUG) | 175 #if !defined(NDEBUG) |
176 // Helper method for printing out an AudioStreamBasicDescription structure. | |
177 static void LogABSD(AudioStreamBasicDescription absd) { | |
178 char formatIDString[5]; | |
179 UInt32 formatID = CFSwapInt32HostToBig(absd.mFormatID); | |
180 bcopy(&formatID, formatIDString, 4); | |
181 formatIDString[4] = '\0'; | |
182 LOG(LS_INFO) << "LogABSD"; | |
183 LOG(LS_INFO) << " sample rate: " << absd.mSampleRate; | |
184 LOG(LS_INFO) << " format ID: " << formatIDString; | |
185 LOG(LS_INFO) << " format flags: " << std::hex << absd.mFormatFlags; | |
186 LOG(LS_INFO) << " bytes per packet: " << absd.mBytesPerPacket; | |
187 LOG(LS_INFO) << " frames per packet: " << absd.mFramesPerPacket; | |
188 LOG(LS_INFO) << " bytes per frame: " << absd.mBytesPerFrame; | |
189 LOG(LS_INFO) << " channels per packet: " << absd.mChannelsPerFrame; | |
190 LOG(LS_INFO) << " bits per channel: " << absd.mBitsPerChannel; | |
191 LOG(LS_INFO) << " reserved: " << absd.mReserved; | |
192 } | |
193 | |
194 // Helper method that logs essential device information strings. | |
113 static void LogDeviceInfo() { | 195 static void LogDeviceInfo() { |
114 LOG(LS_INFO) << "LogDeviceInfo"; | 196 LOG(LS_INFO) << "LogDeviceInfo"; |
115 @autoreleasepool { | 197 @autoreleasepool { |
116 LOG(LS_INFO) << " system name: " << ios::GetSystemName(); | 198 LOG(LS_INFO) << " system name: " << ios::GetSystemName(); |
117 LOG(LS_INFO) << " system version: " << ios::GetSystemVersion(); | 199 LOG(LS_INFO) << " system version: " << ios::GetSystemVersion(); |
118 LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); | 200 LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); |
119 LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); | 201 LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); |
120 } | 202 } |
121 } | 203 } |
122 #endif | 204 #endif // !defined(NDEBUG) |
123 | 205 |
124 AudioDeviceIOS::AudioDeviceIOS() | 206 AudioDeviceIOS::AudioDeviceIOS() |
125 : audio_device_buffer_(nullptr), | 207 : _audioDeviceBuffer(nullptr), |
126 _critSect(*CriticalSectionWrapper::CreateCriticalSection()), | 208 _vpioUnit(nullptr), |
127 _auVoiceProcessing(nullptr), | 209 _recording(0), |
128 _audioInterruptionObserver(nullptr), | 210 _playing(0), |
129 _initialized(false), | 211 _initialized(false), |
130 _isShutDown(false), | |
131 _recording(false), | |
132 _playing(false), | |
133 _recIsInitialized(false), | 212 _recIsInitialized(false), |
134 _playIsInitialized(false), | 213 _playIsInitialized(false), |
135 _adbSampFreq(0), | 214 _audioInterruptionObserver(nullptr) { |
136 _recordingDelay(0), | |
137 _playoutDelay(0), | |
138 _playoutDelayMeasurementCounter(9999), | |
139 _recordingDelayHWAndOS(0), | |
140 _recordingDelayMeasurementCounter(9999), | |
141 _playoutBufferUsed(0), | |
142 _recordingCurrentSeq(0), | |
143 _recordingBufferTotalSize(0) { | |
144 LOGI() << "ctor" << ios::GetCurrentThreadDescription(); | 215 LOGI() << "ctor" << ios::GetCurrentThreadDescription(); |
145 memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); | |
146 memset(_recordingBuffer, 0, sizeof(_recordingBuffer)); | |
147 memset(_recordingLength, 0, sizeof(_recordingLength)); | |
148 memset(_recordingSeqNumber, 0, sizeof(_recordingSeqNumber)); | |
149 } | 216 } |
150 | 217 |
151 AudioDeviceIOS::~AudioDeviceIOS() { | 218 AudioDeviceIOS::~AudioDeviceIOS() { |
152 LOGI() << "~dtor"; | 219 LOGI() << "~dtor"; |
153 DCHECK(thread_checker_.CalledOnValidThread()); | 220 DCHECK(_threadChecker.CalledOnValidThread()); |
154 Terminate(); | 221 Terminate(); |
155 delete &_critSect; | |
156 } | 222 } |
157 | 223 |
158 void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { | 224 void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { |
159 LOGI() << "AttachAudioBuffer"; | 225 LOGI() << "AttachAudioBuffer"; |
160 DCHECK(audioBuffer); | 226 DCHECK(audioBuffer); |
161 DCHECK(thread_checker_.CalledOnValidThread()); | 227 DCHECK(_threadChecker.CalledOnValidThread()); |
162 audio_device_buffer_ = audioBuffer; | 228 _audioDeviceBuffer = audioBuffer; |
163 } | 229 } |
164 | 230 |
165 int32_t AudioDeviceIOS::Init() { | 231 int32_t AudioDeviceIOS::Init() { |
166 LOGI() << "Init"; | 232 LOGI() << "Init"; |
167 DCHECK(thread_checker_.CalledOnValidThread()); | 233 DCHECK(_threadChecker.CalledOnValidThread()); |
168 if (_initialized) { | 234 if (_initialized) { |
169 return 0; | 235 return 0; |
170 } | 236 } |
171 #if !defined(NDEBUG) | 237 #if !defined(NDEBUG) |
172 LogDeviceInfo(); | 238 LogDeviceInfo(); |
173 #endif | 239 #endif |
174 // Query hardware audio parameters and cache the results. These parameters | 240 // Store the preferred sample rate and preferred number of channels already |
175 // will be used as preferred values later when streaming starts. | 241 // here. They have not been set and confirmed yet since ActivateAudioSession() |
176 // Note that I override these "optimal" value below since I don't want to | 242 // is not called until audio is about to start. However, it makes sense to |
177 // modify the existing behavior yet. | 243 // store the parameters now and then verify at a later stage. |
178 GetHardwareAudioParameters(&playout_parameters_, &record_parameters_); | 244 _playoutParameters.reset(kPreferredSampleRate, kPreferredNumberOfChannels); |
179 // TODO(henrika): these parameters are currently hard coded to match the | 245 _recordParameters.reset(kPreferredSampleRate, kPreferredNumberOfChannels); |
180 // existing implementation where we always use 16kHz as preferred sample | 246 // Ensure that the audio device buffer (ADB) knows about the internal audio |
181 // rate and mono only. Goal is to improve this scheme and make it more | 247 // parameters. Note that, even if we are unable to get a mono audio session, |
182 // flexible. In addition, a better native buffer size shall be derived. | 248 // we will always tell the I/O audio unit to do a channel format conversion |
183 // Using 10ms as default here (only used by unit test so far). | 249 // to guarantee mono on the "input side" of the audio unit. |
184 // We should also implemented observers for notification of any change in | 250 UpdateAudioDeviceBuffer(); |
185 // these parameters. | |
186 playout_parameters_.reset(16000, 1, 160); | |
187 record_parameters_.reset(16000, 1, 160); | |
188 | |
189 // AttachAudioBuffer() is called at construction by the main class but check | |
190 // just in case. | |
191 DCHECK(audio_device_buffer_) << "AttachAudioBuffer must be called first"; | |
192 // Inform the audio device buffer (ADB) about the new audio format. | |
193 // TODO(henrika): try to improve this section. | |
194 audio_device_buffer_->SetPlayoutSampleRate(playout_parameters_.sample_rate()); | |
195 audio_device_buffer_->SetPlayoutChannels(playout_parameters_.channels()); | |
196 audio_device_buffer_->SetRecordingSampleRate( | |
197 record_parameters_.sample_rate()); | |
198 audio_device_buffer_->SetRecordingChannels(record_parameters_.channels()); | |
199 | |
200 DCHECK(!_captureWorkerThread); | |
201 // Create and start the capture thread. | |
202 // TODO(henrika): do we need this thread? | |
203 _isShutDown = false; | |
204 _captureWorkerThread = | |
205 ThreadWrapper::CreateThread(RunCapture, this, "CaptureWorkerThread"); | |
206 if (!_captureWorkerThread->Start()) { | |
207 LOG_F(LS_ERROR) << "Failed to start CaptureWorkerThread!"; | |
208 return -1; | |
209 } | |
210 _captureWorkerThread->SetPriority(kRealtimePriority); | |
211 _initialized = true; | 251 _initialized = true; |
212 return 0; | 252 return 0; |
213 } | 253 } |
214 | 254 |
215 int32_t AudioDeviceIOS::Terminate() { | 255 int32_t AudioDeviceIOS::Terminate() { |
216 LOGI() << "Terminate"; | 256 LOGI() << "Terminate"; |
217 DCHECK(thread_checker_.CalledOnValidThread()); | 257 DCHECK(_threadChecker.CalledOnValidThread()); |
218 if (!_initialized) { | 258 if (!_initialized) { |
219 return 0; | 259 return 0; |
220 } | 260 } |
221 // Stop the capture thread. | |
222 if (_captureWorkerThread) { | |
223 if (!_captureWorkerThread->Stop()) { | |
224 LOG_F(LS_ERROR) << "Failed to stop CaptureWorkerThread!"; | |
225 return -1; | |
226 } | |
227 _captureWorkerThread.reset(); | |
228 } | |
229 ShutdownPlayOrRecord(); | 261 ShutdownPlayOrRecord(); |
230 _isShutDown = true; | |
231 _initialized = false; | 262 _initialized = false; |
232 return 0; | 263 return 0; |
233 } | 264 } |
234 | 265 |
235 int32_t AudioDeviceIOS::InitPlayout() { | 266 int32_t AudioDeviceIOS::InitPlayout() { |
236 LOGI() << "InitPlayout"; | 267 LOGI() << "InitPlayout"; |
237 DCHECK(thread_checker_.CalledOnValidThread()); | 268 DCHECK(_threadChecker.CalledOnValidThread()); |
238 DCHECK(_initialized); | 269 DCHECK(_initialized); |
239 DCHECK(!_playIsInitialized); | 270 DCHECK(!_playIsInitialized); |
240 DCHECK(!_playing); | 271 DCHECK(!_playing); |
241 if (!_recIsInitialized) { | 272 if (!_recIsInitialized) { |
242 if (InitPlayOrRecord() == -1) { | 273 if (!InitPlayOrRecord()) { |
243 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!"; | 274 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!"; |
244 return -1; | 275 return -1; |
245 } | 276 } |
246 } | 277 } |
247 _playIsInitialized = true; | 278 _playIsInitialized = true; |
248 return 0; | 279 return 0; |
249 } | 280 } |
250 | 281 |
251 int32_t AudioDeviceIOS::InitRecording() { | 282 int32_t AudioDeviceIOS::InitRecording() { |
252 LOGI() << "InitRecording"; | 283 LOGI() << "InitRecording"; |
253 DCHECK(thread_checker_.CalledOnValidThread()); | 284 DCHECK(_threadChecker.CalledOnValidThread()); |
254 DCHECK(_initialized); | 285 DCHECK(_initialized); |
255 DCHECK(!_recIsInitialized); | 286 DCHECK(!_recIsInitialized); |
256 DCHECK(!_recording); | 287 DCHECK(!_recording); |
257 if (!_playIsInitialized) { | 288 if (!_playIsInitialized) { |
258 if (InitPlayOrRecord() == -1) { | 289 if (!InitPlayOrRecord()) { |
259 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!"; | 290 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!"; |
260 return -1; | 291 return -1; |
261 } | 292 } |
262 } | 293 } |
263 _recIsInitialized = true; | 294 _recIsInitialized = true; |
264 return 0; | 295 return 0; |
265 } | 296 } |
266 | 297 |
267 int32_t AudioDeviceIOS::StartPlayout() { | 298 int32_t AudioDeviceIOS::StartPlayout() { |
268 LOGI() << "StartPlayout"; | 299 LOGI() << "StartPlayout"; |
269 DCHECK(thread_checker_.CalledOnValidThread()); | 300 DCHECK(_threadChecker.CalledOnValidThread()); |
270 DCHECK(_playIsInitialized); | 301 DCHECK(_playIsInitialized); |
271 DCHECK(!_playing); | 302 DCHECK(!_playing); |
272 | 303 _fineAudioBuffer->ResetPlayout(); |
273 CriticalSectionScoped lock(&_critSect); | |
274 | |
275 memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); | |
276 _playoutBufferUsed = 0; | |
277 _playoutDelay = 0; | |
278 // Make sure first call to update delay function will update delay | |
279 _playoutDelayMeasurementCounter = 9999; | |
280 | |
281 if (!_recording) { | 304 if (!_recording) { |
282 OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); | 305 OSStatus result = AudioOutputUnitStart(_vpioUnit); |
283 if (result != noErr) { | 306 if (result != noErr) { |
284 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result; | 307 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result; |
285 return -1; | 308 return -1; |
286 } | 309 } |
287 } | 310 } |
288 _playing = true; | 311 rtc::AtomicOps::ReleaseStore(&_playing, 1); |
289 return 0; | 312 return 0; |
290 } | 313 } |
291 | 314 |
292 int32_t AudioDeviceIOS::StopPlayout() { | 315 int32_t AudioDeviceIOS::StopPlayout() { |
293 LOGI() << "StopPlayout"; | 316 LOGI() << "StopPlayout"; |
294 DCHECK(thread_checker_.CalledOnValidThread()); | 317 DCHECK(_threadChecker.CalledOnValidThread()); |
295 if (!_playIsInitialized || !_playing) { | 318 if (!_playIsInitialized || !_playing) { |
296 return 0; | 319 return 0; |
297 } | 320 } |
298 | |
299 CriticalSectionScoped lock(&_critSect); | |
300 | |
301 if (!_recording) { | 321 if (!_recording) { |
302 // Both playout and recording has stopped, shutdown the device. | |
303 ShutdownPlayOrRecord(); | 322 ShutdownPlayOrRecord(); |
304 } | 323 } |
305 _playIsInitialized = false; | 324 _playIsInitialized = false; |
306 _playing = false; | 325 rtc::AtomicOps::ReleaseStore(&_playing, 0); |
307 return 0; | 326 return 0; |
308 } | 327 } |
309 | 328 |
310 int32_t AudioDeviceIOS::StartRecording() { | 329 int32_t AudioDeviceIOS::StartRecording() { |
311 LOGI() << "StartRecording"; | 330 LOGI() << "StartRecording"; |
312 DCHECK(thread_checker_.CalledOnValidThread()); | 331 DCHECK(_threadChecker.CalledOnValidThread()); |
313 DCHECK(_recIsInitialized); | 332 DCHECK(_recIsInitialized); |
314 DCHECK(!_recording); | 333 DCHECK(!_recording); |
315 | 334 _fineAudioBuffer->ResetRecord(); |
316 CriticalSectionScoped lock(&_critSect); | |
317 | |
318 memset(_recordingBuffer, 0, sizeof(_recordingBuffer)); | |
319 memset(_recordingLength, 0, sizeof(_recordingLength)); | |
320 memset(_recordingSeqNumber, 0, sizeof(_recordingSeqNumber)); | |
321 | |
322 _recordingCurrentSeq = 0; | |
323 _recordingBufferTotalSize = 0; | |
324 _recordingDelay = 0; | |
325 _recordingDelayHWAndOS = 0; | |
326 // Make sure first call to update delay function will update delay | |
327 _recordingDelayMeasurementCounter = 9999; | |
328 | |
329 if (!_playing) { | 335 if (!_playing) { |
330 OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); | 336 OSStatus result = AudioOutputUnitStart(_vpioUnit); |
331 if (result != noErr) { | 337 if (result != noErr) { |
332 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result; | 338 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result; |
333 return -1; | 339 return -1; |
334 } | 340 } |
335 } | 341 } |
336 _recording = true; | 342 rtc::AtomicOps::ReleaseStore(&_recording, 1); |
337 return 0; | 343 return 0; |
338 } | 344 } |
339 | 345 |
340 int32_t AudioDeviceIOS::StopRecording() { | 346 int32_t AudioDeviceIOS::StopRecording() { |
341 LOGI() << "StopRecording"; | 347 LOGI() << "StopRecording"; |
342 DCHECK(thread_checker_.CalledOnValidThread()); | 348 DCHECK(_threadChecker.CalledOnValidThread()); |
343 if (!_recIsInitialized || !_recording) { | 349 if (!_recIsInitialized || !_recording) { |
344 return 0; | 350 return 0; |
345 } | 351 } |
346 | |
347 CriticalSectionScoped lock(&_critSect); | |
348 | |
349 if (!_playing) { | 352 if (!_playing) { |
350 // Both playout and recording has stopped, shutdown the device. | |
351 ShutdownPlayOrRecord(); | 353 ShutdownPlayOrRecord(); |
352 } | 354 } |
353 _recIsInitialized = false; | 355 _recIsInitialized = false; |
354 _recording = false; | 356 rtc::AtomicOps::ReleaseStore(&_recording, 0); |
355 return 0; | 357 return 0; |
356 } | 358 } |
357 | 359 |
358 // Change the default receiver playout route to speaker. | 360 // Change the default receiver playout route to speaker. |
359 int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { | 361 int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { |
360 LOGI() << "SetLoudspeakerStatus(" << enable << ")"; | 362 LOGI() << "SetLoudspeakerStatus(" << enable << ")"; |
361 | 363 |
362 AVAudioSession* session = [AVAudioSession sharedInstance]; | 364 AVAudioSession* session = [AVAudioSession sharedInstance]; |
363 NSString* category = session.category; | 365 NSString* category = session.category; |
364 AVAudioSessionCategoryOptions options = session.categoryOptions; | 366 AVAudioSessionCategoryOptions options = session.categoryOptions; |
(...skipping 19 matching lines...) Expand all Loading... | |
384 | 386 |
385 int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const { | 387 int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const { |
386 LOGI() << "GetLoudspeakerStatus"; | 388 LOGI() << "GetLoudspeakerStatus"; |
387 AVAudioSession* session = [AVAudioSession sharedInstance]; | 389 AVAudioSession* session = [AVAudioSession sharedInstance]; |
388 AVAudioSessionCategoryOptions options = session.categoryOptions; | 390 AVAudioSessionCategoryOptions options = session.categoryOptions; |
389 enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; | 391 enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; |
390 return 0; | 392 return 0; |
391 } | 393 } |
392 | 394 |
393 int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { | 395 int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { |
394 delayMS = _playoutDelay; | 396 delayMS = kFixedPlayoutDelayEstimate; |
395 return 0; | 397 return 0; |
396 } | 398 } |
397 | 399 |
398 int32_t AudioDeviceIOS::RecordingDelay(uint16_t& delayMS) const { | 400 int32_t AudioDeviceIOS::RecordingDelay(uint16_t& delayMS) const { |
399 delayMS = _recordingDelay; | 401 delayMS = kFixedRecordDelayEstimate; |
400 return 0; | |
401 } | |
402 | |
403 int32_t AudioDeviceIOS::PlayoutBuffer(AudioDeviceModule::BufferType& type, | |
404 uint16_t& sizeMS) const { | |
405 type = AudioDeviceModule::kAdaptiveBufferSize; | |
406 sizeMS = _playoutDelay; | |
407 return 0; | 402 return 0; |
408 } | 403 } |
409 | 404 |
410 int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const { | 405 int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const { |
411 CHECK(playout_parameters_.is_valid()); | 406 LOGI() << "GetPlayoutAudioParameters"; |
412 DCHECK(thread_checker_.CalledOnValidThread()); | 407 DCHECK(_playoutParameters.is_valid()); |
413 *params = playout_parameters_; | 408 DCHECK(_threadChecker.CalledOnValidThread()); |
409 *params = _playoutParameters; | |
414 return 0; | 410 return 0; |
415 } | 411 } |
416 | 412 |
417 int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const { | 413 int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const { |
418 CHECK(record_parameters_.is_valid()); | 414 LOGI() << "GetRecordAudioParameters"; |
419 DCHECK(thread_checker_.CalledOnValidThread()); | 415 DCHECK(_recordParameters.is_valid()); |
420 *params = record_parameters_; | 416 DCHECK(_threadChecker.CalledOnValidThread()); |
421 return 0; | 417 *params = _recordParameters; |
422 } | 418 return 0; |
423 | 419 } |
424 // ============================================================================ | 420 |
425 // Private Methods | 421 void AudioDeviceIOS::UpdateAudioDeviceBuffer() { |
426 // ============================================================================ | 422 LOGI() << "UpdateAudioDevicebuffer"; |
427 | 423 // AttachAudioBuffer() is called at construction by the main class but check |
428 int32_t AudioDeviceIOS::InitPlayOrRecord() { | 424 // just in case. |
429 LOGI() << "AudioDeviceIOS::InitPlayOrRecord"; | 425 DCHECK(_audioDeviceBuffer) << "AttachAudioBuffer must be called first"; |
430 DCHECK(!_auVoiceProcessing); | 426 // Inform the audio device buffer (ADB) about the new audio format. |
431 | 427 _audioDeviceBuffer->SetPlayoutSampleRate(_playoutParameters.sample_rate()); |
432 OSStatus result = -1; | 428 _audioDeviceBuffer->SetPlayoutChannels(_playoutParameters.channels()); |
433 | 429 _audioDeviceBuffer->SetRecordingSampleRate(_recordParameters.sample_rate()); |
434 // Create Voice Processing Audio Unit | 430 _audioDeviceBuffer->SetRecordingChannels(_recordParameters.channels()); |
435 AudioComponentDescription desc; | 431 } |
436 AudioComponent comp; | 432 |
437 | 433 void AudioDeviceIOS::SetupAudioBuffersForActiveAudioSession() { |
438 desc.componentType = kAudioUnitType_Output; | 434 LOGI() << "SetupAudioBuffersForActiveAudioSession"; |
439 desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; | 435 AVAudioSession* session = [AVAudioSession sharedInstance]; |
440 desc.componentManufacturer = kAudioUnitManufacturer_Apple; | 436 // Verify the current values once the audio session has been activated. |
441 desc.componentFlags = 0; | 437 LOG(LS_INFO) << " sample rate: " << session.sampleRate; |
442 desc.componentFlagsMask = 0; | 438 LOG(LS_INFO) << " IO buffer duration: " << session.IOBufferDuration; |
443 | 439 LOG(LS_INFO) << " output channels: " << session.outputNumberOfChannels; |
444 comp = AudioComponentFindNext(nullptr, &desc); | 440 LOG(LS_INFO) << " input channels: " << session.inputNumberOfChannels; |
445 if (nullptr == comp) { | 441 LOG(LS_INFO) << " output latency: " << session.outputLatency; |
446 LOG_F(LS_ERROR) << "Could not find audio component for Audio Unit"; | 442 LOG(LS_INFO) << " input latency: " << session.inputLatency; |
447 return -1; | 443 // Log a warning message for the case when we are unable to set the preferred |
444 // hardware sample rate but continue and use the non-ideal sample rate after | |
445 // reinitializing the audio parameters. | |
446 if (session.sampleRate != _playoutParameters.sample_rate()) { | |
447 LOG(LS_WARNING) | |
448 << "Failed to enable an audio session with the preferred sample rate!"; | |
448 } | 449 } |
449 | 450 |
450 result = AudioComponentInstanceNew(comp, &_auVoiceProcessing); | 451 // At this stage, we also know the exact IO buffer duration and can add |
451 if (0 != result) { | 452 // that info to the existing audio parameters where it is converted into |
452 LOG_F(LS_ERROR) << "Failed to create Audio Unit instance: " << result; | 453 // number of audio frames. |
453 return -1; | 454 // Example: IO buffer size = 0.008 seconds <=> 128 audio frames at 16kHz. |
455 // Hence, 128 is the size we expect to see in upcoming render callbacks. | |
456 _playoutParameters.reset(session.sampleRate, | |
457 _playoutParameters.channels(), | |
458 session.IOBufferDuration); | |
459 DCHECK(_playoutParameters.is_complete()); | |
460 _recordParameters.reset(session.sampleRate, | |
461 _recordParameters.channels(), | |
462 session.IOBufferDuration); | |
463 DCHECK(_recordParameters.is_complete()); | |
464 LOG(LS_INFO) << " frames per I/O buffer: " | |
465 << _playoutParameters.frames_per_buffer(); | |
466 LOG(LS_INFO) << " bytes per I/O buffer: " | |
467 << _playoutParameters.GetBytesPerBuffer(); | |
468 DCHECK_EQ(_playoutParameters.GetBytesPerBuffer(), | |
469 _recordParameters.GetBytesPerBuffer()); | |
470 | |
471 // Update the ADB parameters since the sample rate might have changed. | |
472 UpdateAudioDeviceBuffer(); | |
473 | |
474 // Create a modified audio buffer class which allows us to ask for, | |
475 // or deliver, any number of samples (and not only multiple of 10ms) to match | |
476 // the native audio unit buffer size. | |
477 DCHECK(_audioDeviceBuffer); | |
478 _fineAudioBuffer.reset(new FineAudioBuffer( | |
479 _audioDeviceBuffer, _playoutParameters.GetBytesPerBuffer(), | |
480 _playoutParameters.sample_rate())); | |
481 | |
482 // The extra/temporary playoutbuffer must be of this size to avoid | |
483 // unnecessary memcpy while caching data between successive callbacks. | |
484 const int requiredPlayoutBufferSize = | |
485 _fineAudioBuffer->RequiredPlayoutBufferSizeBytes(); | |
486 LOG(LS_INFO) << " required playout buffer size: " | |
487 << requiredPlayoutBufferSize; | |
488 _playoutAudioBuffer.reset(new SInt8[requiredPlayoutBufferSize]); | |
489 | |
490 // Allocate AudioBuffers to be used as storage for the received audio. | |
491 // The AudioBufferList structure works as a placeholder for the | |
492 // AudioBuffer structure, which holds a pointer to the actual data buffer | |
493 // in |_recordAudioBuffer|. Recorded audio will be rendered into this memory | |
494 // at each input callback when calling AudioUnitRender(). | |
495 const int dataByteSize = _recordParameters.GetBytesPerBuffer(); | |
496 _recordAudioBuffer.reset(new SInt8[dataByteSize]); | |
497 _audioRecordBufferList.mNumberBuffers = 1; | |
498 AudioBuffer* audioBuffer = &_audioRecordBufferList.mBuffers[0]; | |
499 audioBuffer->mNumberChannels = _recordParameters.channels(); | |
500 audioBuffer->mDataByteSize = dataByteSize; | |
501 audioBuffer->mData = _recordAudioBuffer.get(); | |
502 } | |
503 | |
504 bool AudioDeviceIOS::SetupAndInitializeVoiceProcessingAudioUnit() { | |
505 LOGI() << "SetupAndInitializeVoiceProcessingAudioUnit"; | |
506 DCHECK(!_vpioUnit); | |
507 // Create an audio component description to identify the Voice-Processing | |
508 // I/O audio unit. | |
509 AudioComponentDescription vpioUnitDescription; | |
510 vpioUnitDescription.componentType = kAudioUnitType_Output; | |
511 vpioUnitDescription.componentSubType = kAudioUnitSubType_VoiceProcessingIO; | |
512 vpioUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple; | |
513 vpioUnitDescription.componentFlags = 0; | |
514 vpioUnitDescription.componentFlagsMask = 0; | |
515 // Obtain an audio unit instance given the description. | |
516 AudioComponent foundVpioUnitRef = | |
517 AudioComponentFindNext(nullptr, &vpioUnitDescription); | |
518 | |
519 // Create a Voice-Processing IO audio unit. | |
520 LOG_AND_RETURN_IF_ERROR( | |
521 AudioComponentInstanceNew(foundVpioUnitRef, &_vpioUnit), | |
522 "Failed to create a VoiceProcessingIO audio unit"); | |
523 | |
524 // A VP I/O unit's bus 1 connects to input hardware (microphone). Enable | |
525 // input on the input scope of the input element. | |
526 AudioUnitElement inputBus = 1; | |
527 UInt32 enableInput = 1; | |
528 LOG_AND_RETURN_IF_ERROR( | |
529 AudioUnitSetProperty(_vpioUnit, kAudioOutputUnitProperty_EnableIO, | |
530 kAudioUnitScope_Input, inputBus, &enableInput, | |
531 sizeof(enableInput)), | |
532 "Failed to enable input on input scope of input element"); | |
533 | |
534 // A VP I/O unit's bus 0 connects to output hardware (speaker). Enable | |
535 // output on the output scope of the output element. | |
536 AudioUnitElement outputBus = 0; | |
537 UInt32 enableOutput = 1; | |
538 LOG_AND_RETURN_IF_ERROR( | |
539 AudioUnitSetProperty(_vpioUnit, kAudioOutputUnitProperty_EnableIO, | |
540 kAudioUnitScope_Output, outputBus, &enableOutput, | |
541 sizeof(enableOutput)), | |
542 "Failed to enable output on output scope of output element"); | |
543 | |
544 // Set the application formats for input and output: | |
545 // - use same format in both directions | |
546 // - avoid resampling in the I/O unit by using the hardware sample rate | |
547 // - linear PCM => noncompressed audio data format with one frame per packet | |
548 // - no need to specify interleaving since only mono is supported | |
549 AudioStreamBasicDescription applicationFormat = {0}; | |
550 UInt32 size = sizeof(applicationFormat); | |
551 DCHECK_EQ(_playoutParameters.sample_rate(), _recordParameters.sample_rate()); | |
552 DCHECK_EQ(1, kPreferredNumberOfChannels); | |
553 applicationFormat.mSampleRate = _playoutParameters.sample_rate(); | |
554 applicationFormat.mFormatID = kAudioFormatLinearPCM; | |
555 applicationFormat.mFormatFlags = | |
556 kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; | |
557 applicationFormat.mBytesPerPacket = kBytesPerSample; | |
558 applicationFormat.mFramesPerPacket = 1; // uncompressed | |
559 applicationFormat.mBytesPerFrame = kBytesPerSample; | |
560 applicationFormat.mChannelsPerFrame = kPreferredNumberOfChannels; | |
561 applicationFormat.mBitsPerChannel = 8 * kBytesPerSample; | |
562 #if !defined(NDEBUG) | |
563 LogABSD(applicationFormat); | |
564 #endif | |
565 | |
566 // Set the application format on the output scope of the input element/bus. | |
567 LOG_AND_RETURN_IF_ERROR( | |
568 AudioUnitSetProperty(_vpioUnit, kAudioUnitProperty_StreamFormat, | |
569 kAudioUnitScope_Output, inputBus, &applicationFormat, | |
570 size), | |
571 "Failed to set application format on output scope of input element"); | |
572 | |
573 // Set the application format on the input scope of the output element/bus. | |
574 LOG_AND_RETURN_IF_ERROR( | |
575 AudioUnitSetProperty(_vpioUnit, kAudioUnitProperty_StreamFormat, | |
576 kAudioUnitScope_Input, outputBus, &applicationFormat, | |
577 size), | |
578 "Failed to set application format on input scope of output element"); | |
579 | |
580 // Specify the callback function that provides audio samples to the audio | |
581 // unit. | |
582 AURenderCallbackStruct renderCallback; | |
583 renderCallback.inputProc = GetPlayoutData; | |
584 renderCallback.inputProcRefCon = this; | |
585 LOG_AND_RETURN_IF_ERROR( | |
586 AudioUnitSetProperty(_vpioUnit, kAudioUnitProperty_SetRenderCallback, | |
587 kAudioUnitScope_Input, outputBus, &renderCallback, | |
588 sizeof(renderCallback)), | |
589 "Failed to specify the render callback on the output element"); | |
590 | |
591 // Disable AU buffer allocation for the recorder, we allocate our own. | |
592 // TODO(henrika): not sure that it actually saves resource to make this call. | |
593 UInt32 flag = 0; | |
594 LOG_AND_RETURN_IF_ERROR( | |
595 AudioUnitSetProperty(_vpioUnit, kAudioUnitProperty_ShouldAllocateBuffer, | |
596 kAudioUnitScope_Output, inputBus, &flag, | |
597 sizeof(flag)), | |
598 "Failed to disable buffer allocation on the input element"); | |
599 | |
600 // Specify the callback to be called by the I/O thread to us when input audio | |
601 // is available. The recorded samples can then be obtained by calling the | |
602 // AudioUnitRender() method. | |
603 AURenderCallbackStruct inputCallback; | |
604 inputCallback.inputProc = RecordedDataIsAvailable; | |
605 inputCallback.inputProcRefCon = this; | |
606 LOG_AND_RETURN_IF_ERROR( | |
607 AudioUnitSetProperty(_vpioUnit, kAudioOutputUnitProperty_SetInputCallback, | |
608 kAudioUnitScope_Global, inputBus, &inputCallback, | |
609 sizeof(inputCallback)), | |
610 "Failed to specify the input callback on the input element"); | |
611 | |
612 // Initialize the Voice-Processing I/O unit instance. | |
613 LOG_AND_RETURN_IF_ERROR(AudioUnitInitialize(_vpioUnit), | |
614 "Failed to initialize the Voice-Processing I/O unit"); | |
615 return true; | |
616 } | |
617 | |
618 bool AudioDeviceIOS::InitPlayOrRecord() { | |
619 LOGI() << "InitPlayOrRecord"; | |
620 AVAudioSession* session = [AVAudioSession sharedInstance]; | |
621 // Activate the audio session and ask for a set of preferred audio parameters. | |
622 ActivateAudioSession(session, true); | |
623 | |
624 // Ensure that we got what what we asked for in our active audio session. | |
625 SetupAudioBuffersForActiveAudioSession(); | |
626 | |
627 // Create, setup and initialize a new Voice-Processing I/O unit. | |
628 if (!SetupAndInitializeVoiceProcessingAudioUnit()) { | |
629 return false; | |
454 } | 630 } |
455 | 631 |
456 // TODO(henrika): I think we should set the preferred channel configuration | |
457 // in both directions as well to be safe. | |
458 | |
459 // Set preferred hardware sample rate to 16 kHz. | |
460 // TODO(henrika): improve this selection of sample rate. Why do we currently | |
461 // use a hard coded value? How can we fail and still continue? | |
462 NSError* error = nil; | |
463 AVAudioSession* session = [AVAudioSession sharedInstance]; | |
464 Float64 preferredSampleRate(playout_parameters_.sample_rate()); | |
465 [session setPreferredSampleRate:preferredSampleRate error:&error]; | |
466 if (error != nil) { | |
467 const char* errorString = [[error localizedDescription] UTF8String]; | |
468 LOG_F(LS_ERROR) << "setPreferredSampleRate failed: " << errorString; | |
469 } | |
470 | |
471 // TODO(henrika): we can reduce latency by setting the IOBufferDuration | |
472 // here. Default size for 16kHz is 0.016 sec or 16 msec on an iPhone 6. | |
473 | |
474 // Activate the audio session. | |
475 ActivateAudioSession(session, true); | |
476 | |
477 UInt32 enableIO = 1; | |
478 result = AudioUnitSetProperty(_auVoiceProcessing, | |
479 kAudioOutputUnitProperty_EnableIO, | |
480 kAudioUnitScope_Input, | |
481 1, // input bus | |
482 &enableIO, sizeof(enableIO)); | |
483 if (0 != result) { | |
484 LOG_F(LS_ERROR) << "Failed to enable IO on input: " << result; | |
485 } | |
486 | |
487 result = AudioUnitSetProperty(_auVoiceProcessing, | |
488 kAudioOutputUnitProperty_EnableIO, | |
489 kAudioUnitScope_Output, | |
490 0, // output bus | |
491 &enableIO, sizeof(enableIO)); | |
492 if (0 != result) { | |
493 LOG_F(LS_ERROR) << "Failed to enable IO on output: " << result; | |
494 } | |
495 | |
496 // Disable AU buffer allocation for the recorder, we allocate our own. | |
497 // TODO(henrika): understand this part better. | |
498 UInt32 flag = 0; | |
499 result = AudioUnitSetProperty(_auVoiceProcessing, | |
500 kAudioUnitProperty_ShouldAllocateBuffer, | |
501 kAudioUnitScope_Output, 1, &flag, sizeof(flag)); | |
502 if (0 != result) { | |
503 LOG_F(LS_WARNING) << "Failed to disable AU buffer allocation: " << result; | |
504 // Should work anyway | |
505 } | |
506 | |
507 // Set recording callback. | |
508 AURenderCallbackStruct auCbS; | |
509 memset(&auCbS, 0, sizeof(auCbS)); | |
510 auCbS.inputProc = RecordProcess; | |
511 auCbS.inputProcRefCon = this; | |
512 result = AudioUnitSetProperty( | |
513 _auVoiceProcessing, kAudioOutputUnitProperty_SetInputCallback, | |
514 kAudioUnitScope_Global, 1, &auCbS, sizeof(auCbS)); | |
515 if (0 != result) { | |
516 LOG_F(LS_ERROR) << "Failed to set AU record callback: " << result; | |
517 } | |
518 | |
519 // Set playout callback. | |
520 memset(&auCbS, 0, sizeof(auCbS)); | |
521 auCbS.inputProc = PlayoutProcess; | |
522 auCbS.inputProcRefCon = this; | |
523 result = AudioUnitSetProperty( | |
524 _auVoiceProcessing, kAudioUnitProperty_SetRenderCallback, | |
525 kAudioUnitScope_Global, 0, &auCbS, sizeof(auCbS)); | |
526 if (0 != result) { | |
527 LOG_F(LS_ERROR) << "Failed to set AU output callback: " << result; | |
528 } | |
529 | |
530 // Get stream format for out/0 | |
531 AudioStreamBasicDescription playoutDesc; | |
532 UInt32 size = sizeof(playoutDesc); | |
533 result = | |
534 AudioUnitGetProperty(_auVoiceProcessing, kAudioUnitProperty_StreamFormat, | |
535 kAudioUnitScope_Output, 0, &playoutDesc, &size); | |
536 if (0 != result) { | |
537 LOG_F(LS_ERROR) << "Failed to get AU output stream format: " << result; | |
538 } | |
539 | |
540 playoutDesc.mSampleRate = preferredSampleRate; | |
541 LOG(LS_INFO) << "Audio Unit playout opened in sampling rate: " | |
542 << playoutDesc.mSampleRate; | |
543 | |
544 // Store the sampling frequency to use towards the Audio Device Buffer | |
545 // todo: Add 48 kHz (increase buffer sizes). Other fs? | |
546 // TODO(henrika): Figure out if we really need this complex handling. | |
547 if ((playoutDesc.mSampleRate > 44090.0) && | |
548 (playoutDesc.mSampleRate < 44110.0)) { | |
549 _adbSampFreq = 44100; | |
550 } else if ((playoutDesc.mSampleRate > 15990.0) && | |
551 (playoutDesc.mSampleRate < 16010.0)) { | |
552 _adbSampFreq = 16000; | |
553 } else if ((playoutDesc.mSampleRate > 7990.0) && | |
554 (playoutDesc.mSampleRate < 8010.0)) { | |
555 _adbSampFreq = 8000; | |
556 } else { | |
557 _adbSampFreq = 0; | |
558 FATAL() << "Invalid sample rate"; | |
559 } | |
560 | |
561 // Set the audio device buffer sampling rates (use same for play and record). | |
562 // TODO(henrika): this is not a good place to set these things up. | |
563 DCHECK(audio_device_buffer_); | |
564 DCHECK_EQ(_adbSampFreq, playout_parameters_.sample_rate()); | |
565 audio_device_buffer_->SetRecordingSampleRate(_adbSampFreq); | |
566 audio_device_buffer_->SetPlayoutSampleRate(_adbSampFreq); | |
567 | |
568 // Set stream format for out/0. | |
569 playoutDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | | |
570 kLinearPCMFormatFlagIsPacked | | |
571 kLinearPCMFormatFlagIsNonInterleaved; | |
572 playoutDesc.mBytesPerPacket = 2; | |
573 playoutDesc.mFramesPerPacket = 1; | |
574 playoutDesc.mBytesPerFrame = 2; | |
575 playoutDesc.mChannelsPerFrame = 1; | |
576 playoutDesc.mBitsPerChannel = 16; | |
577 result = | |
578 AudioUnitSetProperty(_auVoiceProcessing, kAudioUnitProperty_StreamFormat, | |
579 kAudioUnitScope_Input, 0, &playoutDesc, size); | |
580 if (0 != result) { | |
581 LOG_F(LS_ERROR) << "Failed to set AU stream format for out/0"; | |
582 } | |
583 | |
584 // Get stream format for in/1. | |
585 AudioStreamBasicDescription recordingDesc; | |
586 size = sizeof(recordingDesc); | |
587 result = | |
588 AudioUnitGetProperty(_auVoiceProcessing, kAudioUnitProperty_StreamFormat, | |
589 kAudioUnitScope_Input, 1, &recordingDesc, &size); | |
590 if (0 != result) { | |
591 LOG_F(LS_ERROR) << "Failed to get AU stream format for in/1"; | |
592 } | |
593 | |
594 recordingDesc.mSampleRate = preferredSampleRate; | |
595 LOG(LS_INFO) << "Audio Unit recording opened in sampling rate: " | |
596 << recordingDesc.mSampleRate; | |
597 | |
598 // Set stream format for out/1 (use same sampling frequency as for in/1). | |
599 recordingDesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | | |
600 kLinearPCMFormatFlagIsPacked | | |
601 kLinearPCMFormatFlagIsNonInterleaved; | |
602 recordingDesc.mBytesPerPacket = 2; | |
603 recordingDesc.mFramesPerPacket = 1; | |
604 recordingDesc.mBytesPerFrame = 2; | |
605 recordingDesc.mChannelsPerFrame = 1; | |
606 recordingDesc.mBitsPerChannel = 16; | |
607 result = | |
608 AudioUnitSetProperty(_auVoiceProcessing, kAudioUnitProperty_StreamFormat, | |
609 kAudioUnitScope_Output, 1, &recordingDesc, size); | |
610 if (0 != result) { | |
611 LOG_F(LS_ERROR) << "Failed to set AU stream format for out/1"; | |
612 } | |
613 | |
614 // Initialize here already to be able to get/set stream properties. | |
615 result = AudioUnitInitialize(_auVoiceProcessing); | |
616 if (0 != result) { | |
617 LOG_F(LS_ERROR) << "AudioUnitInitialize failed: " << result; | |
618 } | |
619 | |
620 // Get hardware sample rate for logging (see if we get what we asked for). | |
621 // TODO(henrika): what if we don't get what we ask for? | |
622 double sampleRate = session.sampleRate; | |
623 LOG(LS_INFO) << "Current HW sample rate is: " << sampleRate | |
624 << ", ADB sample rate is: " << _adbSampFreq; | |
625 LOG(LS_INFO) << "Current HW IO buffer size is: " << | |
626 [session IOBufferDuration]; | |
627 | |
628 // Listen to audio interruptions. | 632 // Listen to audio interruptions. |
629 // TODO(henrika): learn this area better. | 633 // TODO(henrika): learn this area better. |
630 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | 634 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
631 id observer = [center | 635 id observer = [center |
632 addObserverForName:AVAudioSessionInterruptionNotification | 636 addObserverForName:AVAudioSessionInterruptionNotification |
633 object:nil | 637 object:nil |
634 queue:[NSOperationQueue mainQueue] | 638 queue:[NSOperationQueue mainQueue] |
635 usingBlock:^(NSNotification* notification) { | 639 usingBlock:^(NSNotification* notification) { |
636 NSNumber* typeNumber = | 640 NSNumber* typeNumber = |
637 [notification userInfo][AVAudioSessionInterruptionTypeKey]; | 641 [notification userInfo][AVAudioSessionInterruptionTypeKey]; |
(...skipping 10 matching lines...) Expand all Loading... | |
648 case AVAudioSessionInterruptionTypeEnded: { | 652 case AVAudioSessionInterruptionTypeEnded: { |
649 NSError* error = nil; | 653 NSError* error = nil; |
650 AVAudioSession* session = [AVAudioSession sharedInstance]; | 654 AVAudioSession* session = [AVAudioSession sharedInstance]; |
651 [session setActive:YES error:&error]; | 655 [session setActive:YES error:&error]; |
652 if (error != nil) { | 656 if (error != nil) { |
653 LOG_F(LS_ERROR) << "Failed to active audio session"; | 657 LOG_F(LS_ERROR) << "Failed to active audio session"; |
654 } | 658 } |
655 // Post interruption the audio unit render callbacks don't | 659 // Post interruption the audio unit render callbacks don't |
656 // automatically continue, so we restart the unit manually | 660 // automatically continue, so we restart the unit manually |
657 // here. | 661 // here. |
658 AudioOutputUnitStop(_auVoiceProcessing); | 662 AudioOutputUnitStop(_vpioUnit); |
659 AudioOutputUnitStart(_auVoiceProcessing); | 663 AudioOutputUnitStart(_vpioUnit); |
660 break; | 664 break; |
661 } | 665 } |
662 } | 666 } |
663 }]; | 667 }]; |
664 // Increment refcount on observer using ARC bridge. Instance variable is a | 668 // Increment refcount on observer using ARC bridge. Instance variable is a |
665 // void* instead of an id because header is included in other pure C++ | 669 // void* instead of an id because header is included in other pure C++ |
666 // files. | 670 // files. |
667 _audioInterruptionObserver = (__bridge_retained void*)observer; | 671 _audioInterruptionObserver = (__bridge_retained void*)observer; |
668 | 672 return true; |
669 return 0; | |
670 } | 673 } |
671 | 674 |
672 int32_t AudioDeviceIOS::ShutdownPlayOrRecord() { | 675 bool AudioDeviceIOS::ShutdownPlayOrRecord() { |
673 LOGI() << "ShutdownPlayOrRecord"; | 676 LOGI() << "ShutdownPlayOrRecord"; |
674 | |
675 if (_audioInterruptionObserver != nullptr) { | 677 if (_audioInterruptionObserver != nullptr) { |
676 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | 678 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
677 // Transfer ownership of observer back to ARC, which will dealloc the | 679 // Transfer ownership of observer back to ARC, which will dealloc the |
678 // observer once it exits this scope. | 680 // observer once it exits this scope. |
679 id observer = (__bridge_transfer id)_audioInterruptionObserver; | 681 id observer = (__bridge_transfer id)_audioInterruptionObserver; |
680 [center removeObserver:observer]; | 682 [center removeObserver:observer]; |
681 _audioInterruptionObserver = nullptr; | 683 _audioInterruptionObserver = nullptr; |
682 } | 684 } |
683 | 685 // Close and delete the voice-processing I/O unit. |
684 // Close and delete AU. | |
685 OSStatus result = -1; | 686 OSStatus result = -1; |
686 if (nullptr != _auVoiceProcessing) { | 687 if (nullptr != _vpioUnit) { |
687 result = AudioOutputUnitStop(_auVoiceProcessing); | 688 result = AudioOutputUnitStop(_vpioUnit); |
688 if (0 != result) { | 689 if (result != noErr) { |
689 LOG_F(LS_ERROR) << "AudioOutputUnitStop failed: " << result; | 690 LOG_F(LS_ERROR) << "AudioOutputUnitStop failed: " << result; |
690 } | 691 } |
691 result = AudioComponentInstanceDispose(_auVoiceProcessing); | 692 result = AudioComponentInstanceDispose(_vpioUnit); |
692 if (0 != result) { | 693 if (result != noErr) { |
693 LOG_F(LS_ERROR) << "AudioComponentInstanceDispose failed: " << result; | 694 LOG_F(LS_ERROR) << "AudioComponentInstanceDispose failed: " << result; |
694 } | 695 } |
695 _auVoiceProcessing = nullptr; | 696 _vpioUnit = nullptr; |
696 } | 697 } |
697 | |
698 // All I/O should be stopped or paused prior to deactivating the audio | 698 // All I/O should be stopped or paused prior to deactivating the audio |
699 // session, hence we deactivate as last action. | 699 // session, hence we deactivate as last action. |
700 AVAudioSession* session = [AVAudioSession sharedInstance]; | 700 AVAudioSession* session = [AVAudioSession sharedInstance]; |
701 ActivateAudioSession(session, false); | 701 ActivateAudioSession(session, false); |
702 return 0; | 702 return true; |
703 } | 703 } |
704 | 704 |
705 // ============================================================================ | 705 OSStatus AudioDeviceIOS::RecordedDataIsAvailable( |
706 // Thread Methods | |
707 // ============================================================================ | |
708 | |
709 OSStatus AudioDeviceIOS::RecordProcess( | |
710 void* inRefCon, | 706 void* inRefCon, |
711 AudioUnitRenderActionFlags* ioActionFlags, | 707 AudioUnitRenderActionFlags* ioActionFlags, |
712 const AudioTimeStamp* inTimeStamp, | 708 const AudioTimeStamp* inTimeStamp, |
713 UInt32 inBusNumber, | 709 UInt32 inBusNumber, |
714 UInt32 inNumberFrames, | 710 UInt32 inNumberFrames, |
715 AudioBufferList* ioData) { | 711 AudioBufferList* ioData) { |
716 AudioDeviceIOS* ptrThis = static_cast<AudioDeviceIOS*>(inRefCon); | 712 DCHECK_EQ(1u, inBusNumber); |
717 return ptrThis->RecordProcessImpl(ioActionFlags, inTimeStamp, inBusNumber, | 713 DCHECK(!ioData); // no buffer should be allocated for input at this stage |
718 inNumberFrames); | 714 AudioDeviceIOS* audio_device_ios = static_cast<AudioDeviceIOS*>(inRefCon); |
715 return audio_device_ios->OnRecordedDataIsAvailable( | |
716 ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames); | |
719 } | 717 } |
720 | 718 |
721 OSStatus AudioDeviceIOS::RecordProcessImpl( | 719 OSStatus AudioDeviceIOS::OnRecordedDataIsAvailable( |
722 AudioUnitRenderActionFlags* ioActionFlags, | 720 AudioUnitRenderActionFlags* ioActionFlags, |
723 const AudioTimeStamp* inTimeStamp, | 721 const AudioTimeStamp* inTimeStamp, |
724 uint32_t inBusNumber, | 722 UInt32 inBusNumber, |
725 uint32_t inNumberFrames) { | 723 UInt32 inNumberFrames) { |
726 // Setup some basic stuff | 724 DCHECK_EQ(_recordParameters.frames_per_buffer(), inNumberFrames); |
727 // Use temp buffer not to lock up recording buffer more than necessary | 725 OSStatus result = noErr; |
728 // todo: Make dataTmp a member variable with static size that holds | 726 // Simply return if recording is not enabled. |
729 // max possible frames? | 727 if (!rtc::AtomicOps::AcquireLoad(&_recording)) |
730 int16_t* dataTmp = new int16_t[inNumberFrames]; | 728 return result; |
731 memset(dataTmp, 0, 2 * inNumberFrames); | 729 // Obtain the recorded audio samples by initiating a rendering cycle. |
732 | 730 // Since it happens on the input bus, the |ioData| parameter is a reference |
733 AudioBufferList abList; | 731 // to the preallocated audio buffer list that the audio unit renders into. |
734 abList.mNumberBuffers = 1; | 732 // TODO(henrika): should error handling be improved? |
735 abList.mBuffers[0].mData = dataTmp; | 733 AudioBufferList* ioData = &_audioRecordBufferList; |
736 abList.mBuffers[0].mDataByteSize = 2 * inNumberFrames; // 2 bytes/sample | 734 result = AudioUnitRender(_vpioUnit, ioActionFlags, inTimeStamp, inBusNumber, |
737 abList.mBuffers[0].mNumberChannels = 1; | 735 inNumberFrames, ioData); |
738 | 736 if (result != noErr) { |
739 // Get data from mic | 737 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result; |
740 OSStatus res = AudioUnitRender(_auVoiceProcessing, ioActionFlags, inTimeStamp, | 738 return result; |
741 inBusNumber, inNumberFrames, &abList); | |
742 if (res != 0) { | |
743 // TODO(henrika): improve error handling. | |
744 delete[] dataTmp; | |
745 return 0; | |
746 } | 739 } |
747 | 740 // Get a pointer to the recorded audio and send it to the WebRTC ADB. |
748 if (_recording) { | 741 // Use the FineAudioBuffer instance to convert between native buffer size |
749 // Insert all data in temp buffer into recording buffers | 742 // and the 10ms buffer size used by WebRTC. |
750 // There is zero or one buffer partially full at any given time, | 743 const UInt32 dataSizeInBytes = ioData->mBuffers[0].mDataByteSize; |
751 // all others are full or empty | 744 CHECK_EQ(dataSizeInBytes / kBytesPerSample, inNumberFrames); |
752 // Full means filled with noSamp10ms samples. | 745 SInt8* data = static_cast<SInt8*>(ioData->mBuffers[0].mData); |
753 | 746 _fineAudioBuffer->DeliverRecordedData(data, dataSizeInBytes, |
754 const unsigned int noSamp10ms = _adbSampFreq / 100; | 747 kFixedPlayoutDelayEstimate, |
755 unsigned int dataPos = 0; | 748 kFixedRecordDelayEstimate); |
756 uint16_t bufPos = 0; | 749 return noErr; |
757 int16_t insertPos = -1; | |
758 unsigned int nCopy = 0; // Number of samples to copy | |
759 | |
760 while (dataPos < inNumberFrames) { | |
761 // Loop over all recording buffers or | |
762 // until we find the partially full buffer | |
763 // First choice is to insert into partially full buffer, | |
764 // second choice is to insert into empty buffer | |
765 bufPos = 0; | |
766 insertPos = -1; | |
767 nCopy = 0; | |
768 while (bufPos < N_REC_BUFFERS) { | |
769 if ((_recordingLength[bufPos] > 0) && | |
770 (_recordingLength[bufPos] < noSamp10ms)) { | |
771 // Found the partially full buffer | |
772 insertPos = static_cast<int16_t>(bufPos); | |
773 // Don't need to search more, quit loop | |
774 bufPos = N_REC_BUFFERS; | |
775 } else if ((-1 == insertPos) && (0 == _recordingLength[bufPos])) { | |
776 // Found an empty buffer | |
777 insertPos = static_cast<int16_t>(bufPos); | |
778 } | |
779 ++bufPos; | |
780 } | |
781 | |
782 // Insert data into buffer | |
783 if (insertPos > -1) { | |
784 // We found a non-full buffer, copy data to it | |
785 unsigned int dataToCopy = inNumberFrames - dataPos; | |
786 unsigned int currentRecLen = _recordingLength[insertPos]; | |
787 unsigned int roomInBuffer = noSamp10ms - currentRecLen; | |
788 nCopy = (dataToCopy < roomInBuffer ? dataToCopy : roomInBuffer); | |
789 | |
790 memcpy(&_recordingBuffer[insertPos][currentRecLen], &dataTmp[dataPos], | |
791 nCopy * sizeof(int16_t)); | |
792 if (0 == currentRecLen) { | |
793 _recordingSeqNumber[insertPos] = _recordingCurrentSeq; | |
794 ++_recordingCurrentSeq; | |
795 } | |
796 _recordingBufferTotalSize += nCopy; | |
797 // Has to be done last to avoid interrupt problems between threads. | |
798 _recordingLength[insertPos] += nCopy; | |
799 dataPos += nCopy; | |
800 } else { | |
801 // Didn't find a non-full buffer | |
802 // TODO(henrika): improve error handling | |
803 dataPos = inNumberFrames; // Don't try to insert more | |
804 } | |
805 } | |
806 } | |
807 delete[] dataTmp; | |
808 return 0; | |
809 } | 750 } |
810 | 751 |
811 OSStatus AudioDeviceIOS::PlayoutProcess( | 752 OSStatus AudioDeviceIOS::GetPlayoutData( |
812 void* inRefCon, | 753 void* inRefCon, |
813 AudioUnitRenderActionFlags* ioActionFlags, | 754 AudioUnitRenderActionFlags* ioActionFlags, |
814 const AudioTimeStamp* inTimeStamp, | 755 const AudioTimeStamp* inTimeStamp, |
815 UInt32 inBusNumber, | 756 UInt32 inBusNumber, |
816 UInt32 inNumberFrames, | 757 UInt32 inNumberFrames, |
817 AudioBufferList* ioData) { | 758 AudioBufferList* ioData) { |
818 AudioDeviceIOS* ptrThis = static_cast<AudioDeviceIOS*>(inRefCon); | 759 DCHECK_EQ(0u, inBusNumber); |
819 return ptrThis->PlayoutProcessImpl(inNumberFrames, ioData); | 760 DCHECK(ioData); |
761 AudioDeviceIOS* audio_device_ios = static_cast<AudioDeviceIOS*>(inRefCon); | |
762 return audio_device_ios->OnGetPlayoutData(ioActionFlags, inNumberFrames, | |
763 ioData); | |
820 } | 764 } |
821 | 765 |
822 OSStatus AudioDeviceIOS::PlayoutProcessImpl(uint32_t inNumberFrames, | 766 OSStatus AudioDeviceIOS::OnGetPlayoutData( |
823 AudioBufferList* ioData) { | 767 AudioUnitRenderActionFlags* ioActionFlags, |
824 int16_t* data = static_cast<int16_t*>(ioData->mBuffers[0].mData); | 768 UInt32 inNumberFrames, |
825 unsigned int dataSizeBytes = ioData->mBuffers[0].mDataByteSize; | 769 AudioBufferList* ioData) { |
826 unsigned int dataSize = dataSizeBytes / 2; // Number of samples | 770 // Verify 16-bit, noninterleaved mono PCM signal format. |
827 CHECK_EQ(dataSize, inNumberFrames); | 771 DCHECK_EQ(1u, ioData->mNumberBuffers); |
828 memset(data, 0, dataSizeBytes); // Start with empty buffer | 772 DCHECK_EQ(1u, ioData->mBuffers[0].mNumberChannels); |
829 | 773 // Get pointer to internal audio buffer to which new audio data shall be |
830 // Get playout data from Audio Device Buffer | 774 // written. |
831 | 775 const UInt32 dataSizeInBytes = ioData->mBuffers[0].mDataByteSize; |
832 if (_playing) { | 776 CHECK_EQ(dataSizeInBytes / kBytesPerSample, inNumberFrames); |
833 unsigned int noSamp10ms = _adbSampFreq / 100; | 777 SInt8* destination = static_cast<SInt8*>(ioData->mBuffers[0].mData); |
834 // todo: Member variable and allocate when samp freq is determined | 778 // Produce silence and give audio unit a hint about it if playout is not |
835 int16_t* dataTmp = new int16_t[noSamp10ms]; | 779 // activated. |
836 memset(dataTmp, 0, 2 * noSamp10ms); | 780 if (!rtc::AtomicOps::AcquireLoad(&_playing)) { |
837 unsigned int dataPos = 0; | 781 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence; |
838 int noSamplesOut = 0; | 782 memset(destination, 0, dataSizeInBytes); |
839 unsigned int nCopy = 0; | 783 return noErr; |
840 | |
841 // First insert data from playout buffer if any | |
842 if (_playoutBufferUsed > 0) { | |
843 nCopy = (dataSize < _playoutBufferUsed) ? dataSize : _playoutBufferUsed; | |
844 DCHECK_EQ(nCopy, _playoutBufferUsed); | |
845 memcpy(data, _playoutBuffer, 2 * nCopy); | |
846 dataPos = nCopy; | |
847 memset(_playoutBuffer, 0, sizeof(_playoutBuffer)); | |
848 _playoutBufferUsed = 0; | |
849 } | |
850 | |
851 // Now get the rest from Audio Device Buffer. | |
852 while (dataPos < dataSize) { | |
853 // Update playout delay | |
854 UpdatePlayoutDelay(); | |
855 | |
856 // Ask for new PCM data to be played out using the AudioDeviceBuffer | |
857 noSamplesOut = audio_device_buffer_->RequestPlayoutData(noSamp10ms); | |
858 | |
859 // Get data from Audio Device Buffer | |
860 noSamplesOut = audio_device_buffer_->GetPlayoutData( | |
861 reinterpret_cast<int8_t*>(dataTmp)); | |
862 CHECK_EQ(noSamp10ms, (unsigned int)noSamplesOut); | |
863 | |
864 // Insert as much as fits in data buffer | |
865 nCopy = | |
866 (dataSize - dataPos) > noSamp10ms ? noSamp10ms : (dataSize - dataPos); | |
867 memcpy(&data[dataPos], dataTmp, 2 * nCopy); | |
868 | |
869 // Save rest in playout buffer if any | |
870 if (nCopy < noSamp10ms) { | |
871 memcpy(_playoutBuffer, &dataTmp[nCopy], 2 * (noSamp10ms - nCopy)); | |
872 _playoutBufferUsed = noSamp10ms - nCopy; | |
873 } | |
874 | |
875 // Update loop/index counter, if we copied less than noSamp10ms | |
876 // samples we shall quit loop anyway | |
877 dataPos += noSamp10ms; | |
878 } | |
879 delete[] dataTmp; | |
880 } | 784 } |
881 return 0; | 785 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches |
882 } | 786 // the native I/O audio unit) to a preallocated intermediate buffer and |
883 | 787 // copy the result to the audio buffer in the |ioData| destination. |
884 // TODO(henrika): can either be removed or simplified. | 788 SInt8* source = _playoutAudioBuffer.get(); |
885 void AudioDeviceIOS::UpdatePlayoutDelay() { | 789 _fineAudioBuffer->GetPlayoutData(source); |
886 ++_playoutDelayMeasurementCounter; | 790 memcpy(destination, source, dataSizeInBytes); |
887 | 791 return noErr; |
888 if (_playoutDelayMeasurementCounter >= 100) { | |
889 // Update HW and OS delay every second, unlikely to change | |
890 | |
891 // Since this is eventually rounded to integral ms, add 0.5ms | |
892 // here to get round-to-nearest-int behavior instead of | |
893 // truncation. | |
894 double totalDelaySeconds = 0.0005; | |
895 | |
896 // HW output latency | |
897 AVAudioSession* session = [AVAudioSession sharedInstance]; | |
898 double latency = session.outputLatency; | |
899 assert(latency >= 0); | |
900 totalDelaySeconds += latency; | |
901 | |
902 // HW buffer duration | |
903 double ioBufferDuration = session.IOBufferDuration; | |
904 assert(ioBufferDuration >= 0); | |
905 totalDelaySeconds += ioBufferDuration; | |
906 | |
907 // AU latency | |
908 Float64 f64(0); | |
909 UInt32 size = sizeof(f64); | |
910 OSStatus result = | |
911 AudioUnitGetProperty(_auVoiceProcessing, kAudioUnitProperty_Latency, | |
912 kAudioUnitScope_Global, 0, &f64, &size); | |
913 if (0 != result) { | |
914 LOG_F(LS_ERROR) << "AU latency error: " << result; | |
915 } | |
916 assert(f64 >= 0); | |
917 totalDelaySeconds += f64; | |
918 | |
919 // To ms | |
920 _playoutDelay = static_cast<uint32_t>(totalDelaySeconds * 1000); | |
921 | |
922 // Reset counter | |
923 _playoutDelayMeasurementCounter = 0; | |
924 } | |
925 | |
926 // todo: Add playout buffer? | |
927 } | |
928 | |
929 void AudioDeviceIOS::UpdateRecordingDelay() { | |
930 ++_recordingDelayMeasurementCounter; | |
931 | |
932 if (_recordingDelayMeasurementCounter >= 100) { | |
933 // Update HW and OS delay every second, unlikely to change | |
934 | |
935 // Since this is eventually rounded to integral ms, add 0.5ms | |
936 // here to get round-to-nearest-int behavior instead of | |
937 // truncation. | |
938 double totalDelaySeconds = 0.0005; | |
939 | |
940 // HW input latency | |
941 AVAudioSession* session = [AVAudioSession sharedInstance]; | |
942 double latency = session.inputLatency; | |
943 assert(latency >= 0); | |
944 totalDelaySeconds += latency; | |
945 | |
946 // HW buffer duration | |
947 double ioBufferDuration = session.IOBufferDuration; | |
948 assert(ioBufferDuration >= 0); | |
949 totalDelaySeconds += ioBufferDuration; | |
950 | |
951 // AU latency | |
952 Float64 f64(0); | |
953 UInt32 size = sizeof(f64); | |
954 OSStatus result = | |
955 AudioUnitGetProperty(_auVoiceProcessing, kAudioUnitProperty_Latency, | |
956 kAudioUnitScope_Global, 0, &f64, &size); | |
957 if (0 != result) { | |
958 LOG_F(LS_ERROR) << "AU latency error: " << result; | |
959 } | |
960 assert(f64 >= 0); | |
961 totalDelaySeconds += f64; | |
962 | |
963 // To ms | |
964 _recordingDelayHWAndOS = static_cast<uint32_t>(totalDelaySeconds / 1000); | |
965 | |
966 // Reset counter | |
967 _recordingDelayMeasurementCounter = 0; | |
968 } | |
969 | |
970 _recordingDelay = _recordingDelayHWAndOS; | |
971 | |
972 // ADB recording buffer size, update every time | |
973 // Don't count the one next 10 ms to be sent, then convert samples => ms | |
974 const uint32_t noSamp10ms = _adbSampFreq / 100; | |
975 if (_recordingBufferTotalSize > noSamp10ms) { | |
976 _recordingDelay += | |
977 (_recordingBufferTotalSize - noSamp10ms) / (_adbSampFreq / 1000); | |
978 } | |
979 } | |
980 | |
981 bool AudioDeviceIOS::RunCapture(void* ptrThis) { | |
982 return static_cast<AudioDeviceIOS*>(ptrThis)->CaptureWorkerThread(); | |
983 } | |
984 | |
985 bool AudioDeviceIOS::CaptureWorkerThread() { | |
986 if (_recording) { | |
987 int bufPos = 0; | |
988 unsigned int lowestSeq = 0; | |
989 int lowestSeqBufPos = 0; | |
990 bool foundBuf = true; | |
991 const unsigned int noSamp10ms = _adbSampFreq / 100; | |
992 | |
993 while (foundBuf) { | |
994 // Check if we have any buffer with data to insert | |
995 // into the Audio Device Buffer, | |
996 // and find the one with the lowest seq number | |
997 foundBuf = false; | |
998 for (bufPos = 0; bufPos < N_REC_BUFFERS; ++bufPos) { | |
999 if (noSamp10ms == _recordingLength[bufPos]) { | |
1000 if (!foundBuf) { | |
1001 lowestSeq = _recordingSeqNumber[bufPos]; | |
1002 lowestSeqBufPos = bufPos; | |
1003 foundBuf = true; | |
1004 } else if (_recordingSeqNumber[bufPos] < lowestSeq) { | |
1005 lowestSeq = _recordingSeqNumber[bufPos]; | |
1006 lowestSeqBufPos = bufPos; | |
1007 } | |
1008 } | |
1009 } | |
1010 | |
1011 // Insert data into the Audio Device Buffer if found any | |
1012 if (foundBuf) { | |
1013 // Update recording delay | |
1014 UpdateRecordingDelay(); | |
1015 | |
1016 // Set the recorded buffer | |
1017 audio_device_buffer_->SetRecordedBuffer( | |
1018 reinterpret_cast<int8_t*>(_recordingBuffer[lowestSeqBufPos]), | |
1019 _recordingLength[lowestSeqBufPos]); | |
1020 | |
1021 // Don't need to set the current mic level in ADB since we only | |
1022 // support digital AGC, | |
1023 // and besides we cannot get or set the IOS mic level anyway. | |
1024 | |
1025 // Set VQE info, use clockdrift == 0 | |
1026 audio_device_buffer_->SetVQEData(_playoutDelay, _recordingDelay, 0); | |
1027 | |
1028 // Deliver recorded samples at specified sample rate, mic level | |
1029 // etc. to the observer using callback | |
1030 audio_device_buffer_->DeliverRecordedData(); | |
1031 | |
1032 // Make buffer available | |
1033 _recordingSeqNumber[lowestSeqBufPos] = 0; | |
1034 _recordingBufferTotalSize -= _recordingLength[lowestSeqBufPos]; | |
1035 // Must be done last to avoid interrupt problems between threads | |
1036 _recordingLength[lowestSeqBufPos] = 0; | |
1037 } | |
1038 } | |
1039 } | |
1040 | |
1041 { | |
1042 // Normal case | |
1043 // Sleep thread (5ms) to let other threads get to work | |
1044 // todo: Is 5 ms optimal? Sleep shorter if inserted into the Audio | |
1045 // Device Buffer? | |
1046 timespec t; | |
1047 t.tv_sec = 0; | |
1048 t.tv_nsec = 5 * 1000 * 1000; | |
1049 nanosleep(&t, nullptr); | |
1050 } | |
1051 return true; | |
1052 } | 792 } |
1053 | 793 |
1054 } // namespace webrtc | 794 } // namespace webrtc |
OLD | NEW |