Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: webrtc/modules/audio_device/ios/audio_device_ios.mm

Issue 1254883002: Refactor the AudioDevice for iOS and improve the performance and stability (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Extended unit test for FineAudioBuffer with recorded data Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 // Preferred hardware sample rate (unit is in Hertz). The client sample rate
31 // will be set to this value as well to avoid resampling the the audio unit's
32 // format converter.
33 const double kPreferredSampleRate = 48000.0;
34 // Use a hardware I/O buffer size (unit is in seconds) that matches the 10ms
35 // size used by WebRTC. The exact actual size will differ between devices.
36 // Example: using 48kHz on iPhone 6 results in a native buffer size of
37 // ~10.6667ms or 512 audio frames per buffer. The FineAudioBuffer instance will
38 // take care of any buffering required to convert between native buffers and
39 // buffers used by WebRTC. It is beneficial for the performance if the native
40 // size is as close to 10ms as possible since it results in "clean" callback
41 // sequence without bursts of callbacks back to back.
42 const double kPreferredIOBufferDuration = 0.01;
43 // Try to use mono to save resources. Also avoids channel format conversion
44 // in the I/O audio unit. Seems to work only for built-in microphones.
tkchin_webrtc 2015/09/01 20:54:50 Did you try this with bluetooth microphone? Wonder
henrika_webrtc 2015/09/03 13:44:41 I'll see if I can test with a BT device as well. M
tkchin_webrtc 2015/09/04 05:29:16 That's fine since this is active work. We should f
henrika_webrtc 2015/09/04 09:51:18 Will avoid crash on assert.
45 const int kPreferredNumberOfChannels = 1;
46 // Number of bytes per audio sample for 16-bit signed integer representation.
47 const UInt32 kBytesPerSample = 2;
48 // Hardcoded delay estimates based on real measurements.
49 // TODO(henrika): these value is not used in combination with built-in AEC.
50 // Can most likely be removed.
51 const UInt16 kFixedPlayoutDelayEstimate = 30;
52 const UInt16 kFixedRecordDelayEstimate = 30;
53
29 using ios::CheckAndLogError; 54 using ios::CheckAndLogError;
30 55
56 // Activates an audio session suitable for full duplex VoIP sessions when
57 // |activate| is true. Also sets the preferred sample rate and IO buffer
58 // duration. Deactivates an active audio session if |activate| is set to false.
31 static void ActivateAudioSession(AVAudioSession* session, bool activate) { 59 static void ActivateAudioSession(AVAudioSession* session, bool activate) {
32 LOG(LS_INFO) << "ActivateAudioSession(" << activate << ")"; 60 LOG(LS_INFO) << "ActivateAudioSession(" << activate << ")";
33 @autoreleasepool { 61 @autoreleasepool {
34 NSError* error = nil; 62 NSError* error = nil;
35 BOOL success = NO; 63 BOOL success = NO;
64 // Deactivate the audio session and return if |activate| is false.
36 if (!activate) { 65 if (!activate) {
37 // Deactivate the audio session.
38 success = [session setActive:NO error:&error]; 66 success = [session setActive:NO error:&error];
39 DCHECK(CheckAndLogError(success, error)); 67 DCHECK(CheckAndLogError(success, error));
40 return; 68 return;
41 } 69 }
42 // Activate an audio session and set category and mode. Only make changes 70 // Use a category which supports simultaneous recording and playback.
43 // if needed since setting them to the value they already have will clear 71 // By default, using this category implies that our app’s audio is
44 // transient properties (such as PortOverride) that some other component 72 // nonmixable, hence activating the session will interrupt any other
45 // have set up. 73 // audio sessions which are also nonmixable.
46 if (session.category != AVAudioSessionCategoryPlayAndRecord) { 74 if (session.category != AVAudioSessionCategoryPlayAndRecord) {
47 error = nil; 75 error = nil;
48 success = [session setCategory:AVAudioSessionCategoryPlayAndRecord 76 success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
49 error:&error]; 77 error:&error];
50 DCHECK(CheckAndLogError(success, error)); 78 DCHECK(CheckAndLogError(success, error));
51 } 79 }
80 // Specify mode for two-way voice communication (e.g. VoIP).
52 if (session.mode != AVAudioSessionModeVoiceChat) { 81 if (session.mode != AVAudioSessionModeVoiceChat) {
53 error = nil; 82 error = nil;
54 success = [session setMode:AVAudioSessionModeVoiceChat error:&error]; 83 success = [session setMode:AVAudioSessionModeVoiceChat error:&error];
55 DCHECK(CheckAndLogError(success, error)); 84 DCHECK(CheckAndLogError(success, error));
56 } 85 }
86 // Set the session's sample rate or the hardware sample rate.
87 // It is essential that we use the same sample rate as stream format
88 // to ensure that the I/O unit does not have to do sample rate conversion.
89 error = nil;
90 success =
91 [session setPreferredSampleRate:kPreferredSampleRate error:&error];
92 DCHECK(CheckAndLogError(success, error));
93 // Set the preferred audio I/O buffer duration, in seconds.
94 // TODO(henrika): add more comments here.
95 error = nil;
96 success = [session setPreferredIOBufferDuration:kPreferredIOBufferDuration
97 error:&error];
98 DCHECK(CheckAndLogError(success, error));
99
100 // TODO(henrika): add observers here...
101
102 // Activate the audio session. Activation can fail if another active audio
103 // session (e.g. phone call) has higher priority than ours.
57 error = nil; 104 error = nil;
58 success = [session setActive:YES error:&error]; 105 success = [session setActive:YES error:&error];
59 DCHECK(CheckAndLogError(success, error)); 106 DCHECK(CheckAndLogError(success, error));
107 CHECK(session.isInputAvailable) << "No input path is available!";
60 // Ensure that category and mode are actually activated. 108 // Ensure that category and mode are actually activated.
61 DCHECK( 109 DCHECK(
62 [session.category isEqualToString:AVAudioSessionCategoryPlayAndRecord]); 110 [session.category isEqualToString:AVAudioSessionCategoryPlayAndRecord]);
63 DCHECK([session.mode isEqualToString:AVAudioSessionModeVoiceChat]); 111 DCHECK([session.mode isEqualToString:AVAudioSessionModeVoiceChat]);
64 } 112 // Try to set the preferred number of hardware audio channels. These calls
65 } 113 // must be done after setting the audio session’s category and mode and
66 114 // activating the session.
67 // Query hardware characteristics, such as input and output latency, input and 115 // We try to use mono in both directions to save resources and format
68 // output channel count, hardware sample rate, hardware volume setting, and 116 // conversions in the audio unit. Some devices does only support stereo;
69 // whether audio input is available. To obtain meaningful values for hardware 117 // e.g. wired headset on iPhone 6.
70 // characteristics,the audio session must be initialized and active before we 118 // TODO(henrika): add support for stereo if needed.
71 // query the values. 119 error = nil;
72 // TODO(henrika): Note that these characteristics can change at runtime. For 120 success =
73 // instance, input sample rate may change when a user plugs in a headset. 121 [session setPreferredInputNumberOfChannels:kPreferredNumberOfChannels
74 static void GetHardwareAudioParameters(AudioParameters* playout_parameters, 122 error:&error];
75 AudioParameters* record_parameters) { 123 DCHECK(CheckAndLogError(success, error));
76 LOG(LS_INFO) << "GetHardwareAudioParameters"; 124 error = nil;
77 @autoreleasepool { 125 success =
78 // Implicit initialization happens when we obtain a reference to the 126 [session setPreferredOutputNumberOfChannels:kPreferredNumberOfChannels
79 // AVAudioSession object. 127 error:&error];
80 AVAudioSession* session = [AVAudioSession sharedInstance]; 128 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 } 129 }
110 } 130 }
111 131
112 #if !defined(NDEBUG) 132 #if !defined(NDEBUG)
133 // Helper method for printing out an AudioStreamBasicDescription structure.
134 static void LogABSD(AudioStreamBasicDescription absd) {
135 char formatIDString[5];
136 UInt32 formatID = CFSwapInt32HostToBig(absd.mFormatID);
137 bcopy(&formatID, formatIDString, 4);
138 formatIDString[4] = '\0';
139 LOG(LS_INFO) << "LogABSD";
140 LOG(LS_INFO) << " sample rate: " << absd.mSampleRate;
141 LOG(LS_INFO) << " format ID: " << formatIDString;
142 LOG(LS_INFO) << " format flags: " << std::hex << absd.mFormatFlags;
143 LOG(LS_INFO) << " bytes per packet: " << absd.mBytesPerPacket;
144 LOG(LS_INFO) << " frames per packet: " << absd.mFramesPerPacket;
145 LOG(LS_INFO) << " bytes per frame: " << absd.mBytesPerFrame;
146 LOG(LS_INFO) << " channels per packet: " << absd.mChannelsPerFrame;
147 LOG(LS_INFO) << " bits per channel: " << absd.mBitsPerChannel;
148 LOG(LS_INFO) << " reserved: " << absd.mReserved;
149 }
150
151 // Helper method that logs essential device information strings.
113 static void LogDeviceInfo() { 152 static void LogDeviceInfo() {
114 LOG(LS_INFO) << "LogDeviceInfo"; 153 LOG(LS_INFO) << "LogDeviceInfo";
115 @autoreleasepool { 154 @autoreleasepool {
116 LOG(LS_INFO) << " system name: " << ios::GetSystemName(); 155 LOG(LS_INFO) << " system name: " << ios::GetSystemName();
117 LOG(LS_INFO) << " system version: " << ios::GetSystemVersion(); 156 LOG(LS_INFO) << " system version: " << ios::GetSystemVersion();
118 LOG(LS_INFO) << " device type: " << ios::GetDeviceType(); 157 LOG(LS_INFO) << " device type: " << ios::GetDeviceType();
119 LOG(LS_INFO) << " device name: " << ios::GetDeviceName(); 158 LOG(LS_INFO) << " device name: " << ios::GetDeviceName();
120 } 159 }
121 } 160 }
122 #endif 161 #endif // !defined(NDEBUG)
123 162
124 AudioDeviceIOS::AudioDeviceIOS() 163 AudioDeviceIOS::AudioDeviceIOS()
125 : audio_device_buffer_(nullptr), 164 : _audioDeviceBuffer(nullptr),
126 _critSect(*CriticalSectionWrapper::CreateCriticalSection()), 165 _vpioUnit(nullptr),
127 _auVoiceProcessing(nullptr), 166 _recording(0),
128 _audioInterruptionObserver(nullptr), 167 _playing(0),
129 _initialized(false), 168 _initialized(false),
130 _isShutDown(false),
131 _recording(false),
132 _playing(false),
133 _recIsInitialized(false), 169 _recIsInitialized(false),
134 _playIsInitialized(false), 170 _playIsInitialized(false),
135 _adbSampFreq(0), 171 _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(); 172 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 } 173 }
150 174
151 AudioDeviceIOS::~AudioDeviceIOS() { 175 AudioDeviceIOS::~AudioDeviceIOS() {
152 LOGI() << "~dtor"; 176 LOGI() << "~dtor";
153 DCHECK(thread_checker_.CalledOnValidThread()); 177 DCHECK(_threadChecker.CalledOnValidThread());
154 Terminate(); 178 Terminate();
155 delete &_critSect;
156 } 179 }
157 180
158 void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { 181 void AudioDeviceIOS::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
159 LOGI() << "AttachAudioBuffer"; 182 LOGI() << "AttachAudioBuffer";
160 DCHECK(audioBuffer); 183 DCHECK(audioBuffer);
161 DCHECK(thread_checker_.CalledOnValidThread()); 184 DCHECK(_threadChecker.CalledOnValidThread());
162 audio_device_buffer_ = audioBuffer; 185 _audioDeviceBuffer = audioBuffer;
163 } 186 }
164 187
165 int32_t AudioDeviceIOS::Init() { 188 int32_t AudioDeviceIOS::Init() {
166 LOGI() << "Init"; 189 LOGI() << "Init";
167 DCHECK(thread_checker_.CalledOnValidThread()); 190 DCHECK(_threadChecker.CalledOnValidThread());
168 if (_initialized) { 191 if (_initialized) {
169 return 0; 192 return 0;
170 } 193 }
171 #if !defined(NDEBUG) 194 #if !defined(NDEBUG)
172 LogDeviceInfo(); 195 LogDeviceInfo();
173 #endif 196 #endif
174 // Query hardware audio parameters and cache the results. These parameters 197 // Store the preferred sample rate and preferred number of channels already
175 // will be used as preferred values later when streaming starts. 198 // 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 199 // is not called until audio is about to start. However, it makes sense to
177 // modify the existing behavior yet. 200 // store the parameters now and then verify at a later stage.
178 GetHardwareAudioParameters(&playout_parameters_, &record_parameters_); 201 _playoutParameters.reset(kPreferredSampleRate, kPreferredNumberOfChannels);
179 // TODO(henrika): these parameters are currently hard coded to match the 202 _recordParameters.reset(kPreferredSampleRate, kPreferredNumberOfChannels);
180 // existing implementation where we always use 16kHz as preferred sample 203 // 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 204 // 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. 205 // 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). 206 // to guarantee mono on the "input side" of the audio unit.
184 // We should also implemented observers for notification of any change in 207 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; 208 _initialized = true;
212 return 0; 209 return 0;
213 } 210 }
214 211
215 int32_t AudioDeviceIOS::Terminate() { 212 int32_t AudioDeviceIOS::Terminate() {
216 LOGI() << "Terminate"; 213 LOGI() << "Terminate";
217 DCHECK(thread_checker_.CalledOnValidThread()); 214 DCHECK(_threadChecker.CalledOnValidThread());
218 if (!_initialized) { 215 if (!_initialized) {
219 return 0; 216 return 0;
220 } 217 }
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(); 218 ShutdownPlayOrRecord();
230 _isShutDown = true;
231 _initialized = false; 219 _initialized = false;
232 return 0; 220 return 0;
233 } 221 }
234 222
235 int32_t AudioDeviceIOS::InitPlayout() { 223 int32_t AudioDeviceIOS::InitPlayout() {
236 LOGI() << "InitPlayout"; 224 LOGI() << "InitPlayout";
237 DCHECK(thread_checker_.CalledOnValidThread()); 225 DCHECK(_threadChecker.CalledOnValidThread());
238 DCHECK(_initialized); 226 DCHECK(_initialized);
239 DCHECK(!_playIsInitialized); 227 DCHECK(!_playIsInitialized);
240 DCHECK(!_playing); 228 DCHECK(!_playing);
241 if (!_recIsInitialized) { 229 if (!_recIsInitialized) {
242 if (InitPlayOrRecord() == -1) { 230 if (!InitPlayOrRecord()) {
243 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!"; 231 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!";
244 return -1; 232 return -1;
245 } 233 }
246 } 234 }
247 _playIsInitialized = true; 235 _playIsInitialized = true;
248 return 0; 236 return 0;
249 } 237 }
250 238
251 int32_t AudioDeviceIOS::InitRecording() { 239 int32_t AudioDeviceIOS::InitRecording() {
252 LOGI() << "InitRecording"; 240 LOGI() << "InitRecording";
253 DCHECK(thread_checker_.CalledOnValidThread()); 241 DCHECK(_threadChecker.CalledOnValidThread());
254 DCHECK(_initialized); 242 DCHECK(_initialized);
255 DCHECK(!_recIsInitialized); 243 DCHECK(!_recIsInitialized);
256 DCHECK(!_recording); 244 DCHECK(!_recording);
257 if (!_playIsInitialized) { 245 if (!_playIsInitialized) {
258 if (InitPlayOrRecord() == -1) { 246 if (!InitPlayOrRecord()) {
259 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!"; 247 LOG_F(LS_ERROR) << "InitPlayOrRecord failed!";
260 return -1; 248 return -1;
261 } 249 }
262 } 250 }
263 _recIsInitialized = true; 251 _recIsInitialized = true;
264 return 0; 252 return 0;
265 } 253 }
266 254
267 int32_t AudioDeviceIOS::StartPlayout() { 255 int32_t AudioDeviceIOS::StartPlayout() {
268 LOGI() << "StartPlayout"; 256 LOGI() << "StartPlayout";
269 DCHECK(thread_checker_.CalledOnValidThread()); 257 DCHECK(_threadChecker.CalledOnValidThread());
270 DCHECK(_playIsInitialized); 258 DCHECK(_playIsInitialized);
271 DCHECK(!_playing); 259 DCHECK(!_playing);
272 260 _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) { 261 if (!_recording) {
282 OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); 262 OSStatus result = AudioOutputUnitStart(_vpioUnit);
283 if (result != noErr) { 263 if (result != noErr) {
284 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result; 264 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result;
285 return -1; 265 return -1;
286 } 266 }
287 } 267 }
288 _playing = true; 268 rtc::AtomicOps::ReleaseStore(&_playing, 1);
289 return 0; 269 return 0;
290 } 270 }
291 271
292 int32_t AudioDeviceIOS::StopPlayout() { 272 int32_t AudioDeviceIOS::StopPlayout() {
293 LOGI() << "StopPlayout"; 273 LOGI() << "StopPlayout";
294 DCHECK(thread_checker_.CalledOnValidThread()); 274 DCHECK(_threadChecker.CalledOnValidThread());
295 if (!_playIsInitialized || !_playing) { 275 if (!_playIsInitialized || !_playing) {
296 return 0; 276 return 0;
297 } 277 }
298
299 CriticalSectionScoped lock(&_critSect);
300
301 if (!_recording) { 278 if (!_recording) {
302 // Both playout and recording has stopped, shutdown the device.
303 ShutdownPlayOrRecord(); 279 ShutdownPlayOrRecord();
304 } 280 }
305 _playIsInitialized = false; 281 _playIsInitialized = false;
306 _playing = false; 282 rtc::AtomicOps::ReleaseStore(&_playing, 0);
307 return 0; 283 return 0;
308 } 284 }
309 285
310 int32_t AudioDeviceIOS::StartRecording() { 286 int32_t AudioDeviceIOS::StartRecording() {
311 LOGI() << "StartRecording"; 287 LOGI() << "StartRecording";
312 DCHECK(thread_checker_.CalledOnValidThread()); 288 DCHECK(_threadChecker.CalledOnValidThread());
313 DCHECK(_recIsInitialized); 289 DCHECK(_recIsInitialized);
314 DCHECK(!_recording); 290 DCHECK(!_recording);
315 291 _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) { 292 if (!_playing) {
330 OSStatus result = AudioOutputUnitStart(_auVoiceProcessing); 293 OSStatus result = AudioOutputUnitStart(_vpioUnit);
331 if (result != noErr) { 294 if (result != noErr) {
332 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result; 295 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result;
333 return -1; 296 return -1;
334 } 297 }
335 } 298 }
336 _recording = true; 299 rtc::AtomicOps::ReleaseStore(&_recording, 1);
337 return 0; 300 return 0;
338 } 301 }
339 302
340 int32_t AudioDeviceIOS::StopRecording() { 303 int32_t AudioDeviceIOS::StopRecording() {
341 LOGI() << "StopRecording"; 304 LOGI() << "StopRecording";
342 DCHECK(thread_checker_.CalledOnValidThread()); 305 DCHECK(_threadChecker.CalledOnValidThread());
343 if (!_recIsInitialized || !_recording) { 306 if (!_recIsInitialized || !_recording) {
344 return 0; 307 return 0;
345 } 308 }
346
347 CriticalSectionScoped lock(&_critSect);
348
349 if (!_playing) { 309 if (!_playing) {
350 // Both playout and recording has stopped, shutdown the device.
351 ShutdownPlayOrRecord(); 310 ShutdownPlayOrRecord();
352 } 311 }
353 _recIsInitialized = false; 312 _recIsInitialized = false;
354 _recording = false; 313 rtc::AtomicOps::ReleaseStore(&_recording, 0);
355 return 0; 314 return 0;
356 } 315 }
357 316
358 // Change the default receiver playout route to speaker. 317 // Change the default receiver playout route to speaker.
359 int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) { 318 int32_t AudioDeviceIOS::SetLoudspeakerStatus(bool enable) {
360 LOGI() << "SetLoudspeakerStatus(" << enable << ")"; 319 LOGI() << "SetLoudspeakerStatus(" << enable << ")";
361 320
362 AVAudioSession* session = [AVAudioSession sharedInstance]; 321 AVAudioSession* session = [AVAudioSession sharedInstance];
363 NSString* category = session.category; 322 NSString* category = session.category;
364 AVAudioSessionCategoryOptions options = session.categoryOptions; 323 AVAudioSessionCategoryOptions options = session.categoryOptions;
(...skipping 19 matching lines...) Expand all
384 343
385 int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const { 344 int32_t AudioDeviceIOS::GetLoudspeakerStatus(bool& enabled) const {
386 LOGI() << "GetLoudspeakerStatus"; 345 LOGI() << "GetLoudspeakerStatus";
387 AVAudioSession* session = [AVAudioSession sharedInstance]; 346 AVAudioSession* session = [AVAudioSession sharedInstance];
388 AVAudioSessionCategoryOptions options = session.categoryOptions; 347 AVAudioSessionCategoryOptions options = session.categoryOptions;
389 enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker; 348 enabled = options & AVAudioSessionCategoryOptionDefaultToSpeaker;
390 return 0; 349 return 0;
391 } 350 }
392 351
393 int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const { 352 int32_t AudioDeviceIOS::PlayoutDelay(uint16_t& delayMS) const {
394 delayMS = _playoutDelay; 353 delayMS = kFixedPlayoutDelayEstimate;
395 return 0; 354 return 0;
396 } 355 }
397 356
398 int32_t AudioDeviceIOS::RecordingDelay(uint16_t& delayMS) const { 357 int32_t AudioDeviceIOS::RecordingDelay(uint16_t& delayMS) const {
399 delayMS = _recordingDelay; 358 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; 359 return 0;
408 } 360 }
409 361
410 int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const { 362 int AudioDeviceIOS::GetPlayoutAudioParameters(AudioParameters* params) const {
411 CHECK(playout_parameters_.is_valid()); 363 DCHECK(_playoutParameters.is_valid());
412 DCHECK(thread_checker_.CalledOnValidThread()); 364 DCHECK(_threadChecker.CalledOnValidThread());
413 *params = playout_parameters_; 365 *params = _playoutParameters;
414 return 0; 366 return 0;
415 } 367 }
416 368
417 int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const { 369 int AudioDeviceIOS::GetRecordAudioParameters(AudioParameters* params) const {
418 CHECK(record_parameters_.is_valid()); 370 DCHECK(_recordParameters.is_valid());
419 DCHECK(thread_checker_.CalledOnValidThread()); 371 DCHECK(_threadChecker.CalledOnValidThread());
420 *params = record_parameters_; 372 *params = _recordParameters;
421 return 0; 373 return 0;
422 } 374 }
423 375
424 // ============================================================================ 376 void AudioDeviceIOS::UpdateAudioDeviceBuffer() {
425 // Private Methods 377 LOGI() << "UpdateAudioDevicebuffer";
426 // ============================================================================ 378 // AttachAudioBuffer() is called at construction by the main class but check
427 379 // just in case.
428 int32_t AudioDeviceIOS::InitPlayOrRecord() { 380 DCHECK(_audioDeviceBuffer) << "AttachAudioBuffer must be called first";
429 LOGI() << "AudioDeviceIOS::InitPlayOrRecord"; 381 // Inform the audio device buffer (ADB) about the new audio format.
430 DCHECK(!_auVoiceProcessing); 382 _audioDeviceBuffer->SetPlayoutSampleRate(_playoutParameters.sample_rate());
431 383 _audioDeviceBuffer->SetPlayoutChannels(_playoutParameters.channels());
432 OSStatus result = -1; 384 _audioDeviceBuffer->SetRecordingSampleRate(_recordParameters.sample_rate());
433 385 _audioDeviceBuffer->SetRecordingChannels(_recordParameters.channels());
434 // Create Voice Processing Audio Unit 386 }
435 AudioComponentDescription desc; 387
436 AudioComponent comp; 388 void AudioDeviceIOS::VerifyAudioParametersForActiveAudioSession() {
tkchin_webrtc 2015/09/01 20:54:50 This does a bit more than verifying parameters, co
henrika_webrtc 2015/09/03 13:44:41 Done.
437 389 LOGI() << "VerifyAudioParametersForActiveAudioSession";
438 desc.componentType = kAudioUnitType_Output;
439 desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
440 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
441 desc.componentFlags = 0;
442 desc.componentFlagsMask = 0;
443
444 comp = AudioComponentFindNext(nullptr, &desc);
445 if (nullptr == comp) {
446 LOG_F(LS_ERROR) << "Could not find audio component for Audio Unit";
447 return -1;
448 }
449
450 result = AudioComponentInstanceNew(comp, &_auVoiceProcessing);
451 if (0 != result) {
452 LOG_F(LS_ERROR) << "Failed to create Audio Unit instance: " << result;
453 return -1;
454 }
455
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]; 390 AVAudioSession* session = [AVAudioSession sharedInstance];
464 Float64 preferredSampleRate(playout_parameters_.sample_rate()); 391 // Verify the current values once the audio session has been activated.
tkchin_webrtc 2015/09/01 20:54:50 minor minor nit: line break after comment + block
henrika_webrtc 2015/09/03 13:44:41 Done.
465 [session setPreferredSampleRate:preferredSampleRate error:&error]; 392 LOG(LS_INFO) << " sample rate: " << session.sampleRate;
466 if (error != nil) { 393 LOG(LS_INFO) << " IO buffer duration: " << session.IOBufferDuration;
467 const char* errorString = [[error localizedDescription] UTF8String]; 394 LOG(LS_INFO) << " output channels: " << session.outputNumberOfChannels;
468 LOG_F(LS_ERROR) << "setPreferredSampleRate failed: " << errorString; 395 LOG(LS_INFO) << " input channels: " << session.inputNumberOfChannels;
469 } 396 LOG(LS_INFO) << " output latency: " << session.outputLatency;
470 397 LOG(LS_INFO) << " input latency: " << session.inputLatency;
471 // TODO(henrika): we can reduce latency by setting the IOBufferDuration 398 // TODO(henrika): would it be possible to continue even for the case when
472 // here. Default size for 16kHz is 0.016 sec or 16 msec on an iPhone 6. 399 // we are unable to set the preferred hardware sample rate.
473 400 CHECK_EQ(static_cast<int>(session.sampleRate),
tkchin_webrtc 2015/09/01 20:54:50 It seems overkill to crash if we don't get what we
henrika_webrtc 2015/09/03 13:44:41 Agree. Let me keep the TODO and fix this in combin
tkchin_webrtc 2015/09/04 05:29:16 For next CL, I would just have a simple one that r
henrika_webrtc 2015/09/04 09:51:18 I'll add the non-crashing parts to this CL and BT
474 // Activate the audio session. 401 _playoutParameters.sample_rate())
402 << "Failed to enable an audio session with the preferred sample rate!";
403 // At this stage, we also know the exact IO buffer duration and can add
404 // that info to the existing audio parameters where it is converted into
405 // number of audio frames.
406 // Example: IO buffer size = 0.008 seconds <=> 128 audio frames at 16kHz.
407 // Hence, 128 is the size we expect to see in upcoming render callbacks.
408 _playoutParameters.reset(_playoutParameters.sample_rate(),
409 _playoutParameters.channels(),
410 session.IOBufferDuration);
411 DCHECK(_playoutParameters.is_complete());
412 _recordParameters.reset(_recordParameters.sample_rate(),
413 _recordParameters.channels(),
414 session.IOBufferDuration);
415 DCHECK(_recordParameters.is_complete());
416 LOG(LS_INFO) << " frames per I/O buffer: "
417 << _playoutParameters.frames_per_buffer();
418 LOG(LS_INFO) << " bytes per I/O buffer: "
419 << _playoutParameters.GetBytesPerBuffer();
420 DCHECK_EQ(_playoutParameters.GetBytesPerBuffer(),
421 _recordParameters.GetBytesPerBuffer());
422 // Create a modified audio buffer class which allows us to ask for any number
423 // of samples (and not only multiple of 10ms) to match the native audio unit
424 // buffer size.
425 DCHECK(_audioDeviceBuffer);
426 _fineAudioBuffer.reset(new FineAudioBuffer(
427 _audioDeviceBuffer, _playoutParameters.GetBytesPerBuffer(),
428 _playoutParameters.sample_rate()));
429 // The extra/temporary playoutbuffer must be of this size to avoid
430 // unnecessary memcpy while caching data between successive callbacks.
431 const int requiredPlayoutBufferSize =
432 _fineAudioBuffer->RequiredPlayoutBufferSizeBytes();
433 LOG(LS_INFO) << " required playout buffer size: "
434 << requiredPlayoutBufferSize;
435 _playoutAudioBuffer.reset(new SInt8[requiredPlayoutBufferSize]);
436 // Allocate AudioBuffers to be used as storage for the received audio.
437 // The AudioBufferList structure works as a placeholder for the
438 // AudioBuffer structure, which holds a pointer to the actual data buffer
439 // in |_recordAudioBuffer|. Recorded audio will be rendered into this memory
440 // at each input callback when calling AudioUnitRender().
441 const int dataByteSize = _recordParameters.GetBytesPerBuffer();
442 _recordAudioBuffer.reset(new SInt8[dataByteSize]);
443 _audioRecordBufferList.mNumberBuffers = 1;
444 AudioBuffer* audioBuffer = &_audioRecordBufferList.mBuffers[0];
445 audioBuffer->mNumberChannels = _recordParameters.channels();
446 audioBuffer->mDataByteSize = dataByteSize;
447 audioBuffer->mData = _recordAudioBuffer.get();
448 }
449
450 void AudioDeviceIOS::SetupAndInitializeVoiceProcessingAudioUnit() {
451 LOGI() << "SetupAndInitializeVoiceProcessingAudioUnit";
452 DCHECK(!_vpioUnit);
453 // Create an audio component description to identify the Voice-Processing
454 // I/O audio unit.
455 AudioComponentDescription vpioUnitDescription;
456 vpioUnitDescription.componentType = kAudioUnitType_Output;
457 vpioUnitDescription.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
458 vpioUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
459 vpioUnitDescription.componentFlags = 0;
460 vpioUnitDescription.componentFlagsMask = 0;
461 // Obtain an audio unit instance given the description.
462 AudioComponent foundVpioUnitRef =
463 AudioComponentFindNext(nullptr, &vpioUnitDescription);
464 // Create a Voice-Processing IO audio unit.
465 CHECK_EQ(AudioComponentInstanceNew(foundVpioUnitRef, &_vpioUnit), noErr)
tkchin_webrtc 2015/09/01 20:54:50 ditto system call failures shouldn't crash applica
henrika_webrtc 2015/09/03 13:44:41 OK; I will fix that. Must leave soon but will fix
466 << "Failed to create a VoiceProcessingIO audio unit";
467 // A VP I/O unit's bus 1 connects to input hardware (microphone). Enable
468 // input on the input scope of the input element.
469 AudioUnitElement inputBus = 1;
470 UInt32 enableInput = 1;
471 CHECK_EQ(AudioUnitSetProperty(_vpioUnit, kAudioOutputUnitProperty_EnableIO,
472 kAudioUnitScope_Input, inputBus, &enableInput,
473 sizeof(enableInput)),
474 noErr)
475 << "Failed to enable input on input scope of input element";
476 // A VP I/O unit's bus 0 connects to output hardware (speaker). Enable
477 // output on the output scope of the output element.
478 AudioUnitElement outputBus = 0;
479 UInt32 enableOutput = 1;
480 CHECK_EQ(AudioUnitSetProperty(_vpioUnit, kAudioOutputUnitProperty_EnableIO,
481 kAudioUnitScope_Output, outputBus,
482 &enableOutput, sizeof(enableOutput)),
483 noErr)
484 << "Failed to enable output on output scope of output element";
485 // Set the application formats for input and output:
486 // - use same format in both directions
487 // - avoid resampling in the I/O unit by using the hardware sample rate
488 // - linear PCM => noncompressed audio data format with one frame per packet
489 // - no need to specify interleaving since only mono is supported
490 AudioStreamBasicDescription applicationFormat = {0};
491 UInt32 size = sizeof(applicationFormat);
492 DCHECK_EQ(_playoutParameters.sample_rate(), _recordParameters.sample_rate());
493 DCHECK_EQ(1, kPreferredNumberOfChannels);
494 applicationFormat.mSampleRate = _playoutParameters.sample_rate();
495 applicationFormat.mFormatID = kAudioFormatLinearPCM;
496 applicationFormat.mFormatFlags =
497 kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
498 applicationFormat.mBytesPerPacket = kBytesPerSample;
499 applicationFormat.mFramesPerPacket = 1; // uncompressed
500 applicationFormat.mBytesPerFrame = kBytesPerSample;
501 applicationFormat.mChannelsPerFrame = kPreferredNumberOfChannels;
502 applicationFormat.mBitsPerChannel = 8 * kBytesPerSample;
503 #if !defined(NDEBUG)
504 LogABSD(applicationFormat);
505 #endif
506 // Set the application format on the output scope of the input element/bus.
507 CHECK_EQ(AudioUnitSetProperty(_vpioUnit, kAudioUnitProperty_StreamFormat,
508 kAudioUnitScope_Output, inputBus,
509 &applicationFormat, size),
510
511 noErr)
512 << "Failed to set application format on output scope of input element";
513 // Set the application format on the input scope of the output element/bus.
514 CHECK_EQ(AudioUnitSetProperty(_vpioUnit, kAudioUnitProperty_StreamFormat,
515 kAudioUnitScope_Input, outputBus,
516 &applicationFormat, size),
517 noErr)
518 << "Failed to set application format on input scope of output element";
519 // Specify the callback function that provides audio samples to the audio
520 // unit.
521 AURenderCallbackStruct renderCallback;
522 renderCallback.inputProc = GetPlayoutData;
523 renderCallback.inputProcRefCon = this;
524 CHECK_EQ(AudioUnitSetProperty(_vpioUnit, kAudioUnitProperty_SetRenderCallback,
525 kAudioUnitScope_Input, outputBus,
526 &renderCallback, sizeof(renderCallback)),
527 noErr)
528 << "Failed to specify the render callback on the output element";
529 // Disable AU buffer allocation for the recorder, we allocate our own.
530 // TODO(henrika): not sure that it actually saves resource to make this call.
531 UInt32 flag = 0;
532 CHECK_EQ(AudioUnitSetProperty(
533 _vpioUnit, kAudioUnitProperty_ShouldAllocateBuffer,
534 kAudioUnitScope_Output, inputBus, &flag, sizeof(flag)),
535 noErr)
536 << "Failed to disable buffer allocation on the input element";
537 // Specify the callback to be called by the I/O thread to us when input audio
538 // is available. The recorded samples can then be obtained by calling the
539 // AudioUnitRender() method.
540 AURenderCallbackStruct inputCallback;
541 inputCallback.inputProc = RecordedDataIsAvailable;
542 inputCallback.inputProcRefCon = this;
543 CHECK_EQ(
544 AudioUnitSetProperty(_vpioUnit, kAudioOutputUnitProperty_SetInputCallback,
545 kAudioUnitScope_Global, inputBus, &inputCallback,
546 sizeof(inputCallback)),
547 noErr)
548 << "Failed to specify the input callback on the input element";
549 // Initialize the Voice-Processing I/O unit instance.
550 CHECK_EQ(AudioUnitInitialize(_vpioUnit), noErr)
551 << "Failed to initialize the Voice-Processing I/O unit";
552 }
553
554 bool AudioDeviceIOS::InitPlayOrRecord() {
555 LOGI() << "InitPlayOrRecord";
556 AVAudioSession* session = [AVAudioSession sharedInstance];
557 // Activate the audio session and ask for a set of preferred audio parameters.
475 ActivateAudioSession(session, true); 558 ActivateAudioSession(session, true);
476 559 // Ensure that we got what what we asked for in our active audio session.
477 UInt32 enableIO = 1; 560 VerifyAudioParametersForActiveAudioSession();
478 result = AudioUnitSetProperty(_auVoiceProcessing, 561 // Create, setup and initialize a new Voice-Processing I/O unit.
479 kAudioOutputUnitProperty_EnableIO, 562 SetupAndInitializeVoiceProcessingAudioUnit();
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. 563 // Listen to audio interruptions.
629 // TODO(henrika): learn this area better. 564 // TODO(henrika): learn this area better.
630 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 565 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
631 id observer = [center 566 id observer = [center
632 addObserverForName:AVAudioSessionInterruptionNotification 567 addObserverForName:AVAudioSessionInterruptionNotification
633 object:nil 568 object:nil
634 queue:[NSOperationQueue mainQueue] 569 queue:[NSOperationQueue mainQueue]
635 usingBlock:^(NSNotification* notification) { 570 usingBlock:^(NSNotification* notification) {
636 NSNumber* typeNumber = 571 NSNumber* typeNumber =
637 [notification userInfo][AVAudioSessionInterruptionTypeKey]; 572 [notification userInfo][AVAudioSessionInterruptionTypeKey];
(...skipping 10 matching lines...) Expand all
648 case AVAudioSessionInterruptionTypeEnded: { 583 case AVAudioSessionInterruptionTypeEnded: {
649 NSError* error = nil; 584 NSError* error = nil;
650 AVAudioSession* session = [AVAudioSession sharedInstance]; 585 AVAudioSession* session = [AVAudioSession sharedInstance];
651 [session setActive:YES error:&error]; 586 [session setActive:YES error:&error];
652 if (error != nil) { 587 if (error != nil) {
653 LOG_F(LS_ERROR) << "Failed to active audio session"; 588 LOG_F(LS_ERROR) << "Failed to active audio session";
654 } 589 }
655 // Post interruption the audio unit render callbacks don't 590 // Post interruption the audio unit render callbacks don't
656 // automatically continue, so we restart the unit manually 591 // automatically continue, so we restart the unit manually
657 // here. 592 // here.
658 AudioOutputUnitStop(_auVoiceProcessing); 593 AudioOutputUnitStop(_vpioUnit);
659 AudioOutputUnitStart(_auVoiceProcessing); 594 AudioOutputUnitStart(_vpioUnit);
660 break; 595 break;
661 } 596 }
662 } 597 }
663 }]; 598 }];
664 // Increment refcount on observer using ARC bridge. Instance variable is a 599 // 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++ 600 // void* instead of an id because header is included in other pure C++
666 // files. 601 // files.
667 _audioInterruptionObserver = (__bridge_retained void*)observer; 602 _audioInterruptionObserver = (__bridge_retained void*)observer;
668 603 return true;
669 return 0;
670 } 604 }
671 605
672 int32_t AudioDeviceIOS::ShutdownPlayOrRecord() { 606 bool AudioDeviceIOS::ShutdownPlayOrRecord() {
673 LOGI() << "ShutdownPlayOrRecord"; 607 LOGI() << "ShutdownPlayOrRecord";
674
675 if (_audioInterruptionObserver != nullptr) { 608 if (_audioInterruptionObserver != nullptr) {
676 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; 609 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
677 // Transfer ownership of observer back to ARC, which will dealloc the 610 // Transfer ownership of observer back to ARC, which will dealloc the
678 // observer once it exits this scope. 611 // observer once it exits this scope.
679 id observer = (__bridge_transfer id)_audioInterruptionObserver; 612 id observer = (__bridge_transfer id)_audioInterruptionObserver;
680 [center removeObserver:observer]; 613 [center removeObserver:observer];
681 _audioInterruptionObserver = nullptr; 614 _audioInterruptionObserver = nullptr;
682 } 615 }
683 616 // Close and delete the voice-processing I/O unit.
684 // Close and delete AU.
685 OSStatus result = -1; 617 OSStatus result = -1;
686 if (nullptr != _auVoiceProcessing) { 618 if (nullptr != _vpioUnit) {
687 result = AudioOutputUnitStop(_auVoiceProcessing); 619 result = AudioOutputUnitStop(_vpioUnit);
688 if (0 != result) { 620 if (result != noErr) {
689 LOG_F(LS_ERROR) << "AudioOutputUnitStop failed: " << result; 621 LOG_F(LS_ERROR) << "AudioOutputUnitStop failed: " << result;
690 } 622 }
691 result = AudioComponentInstanceDispose(_auVoiceProcessing); 623 result = AudioComponentInstanceDispose(_vpioUnit);
692 if (0 != result) { 624 if (result != noErr) {
693 LOG_F(LS_ERROR) << "AudioComponentInstanceDispose failed: " << result; 625 LOG_F(LS_ERROR) << "AudioComponentInstanceDispose failed: " << result;
694 } 626 }
695 _auVoiceProcessing = nullptr; 627 _vpioUnit = nullptr;
696 } 628 }
697
698 // All I/O should be stopped or paused prior to deactivating the audio 629 // All I/O should be stopped or paused prior to deactivating the audio
699 // session, hence we deactivate as last action. 630 // session, hence we deactivate as last action.
700 AVAudioSession* session = [AVAudioSession sharedInstance]; 631 AVAudioSession* session = [AVAudioSession sharedInstance];
701 ActivateAudioSession(session, false); 632 ActivateAudioSession(session, false);
702 return 0; 633 return true;
703 } 634 }
704 635
705 // ============================================================================ 636 OSStatus AudioDeviceIOS::RecordedDataIsAvailable(
706 // Thread Methods
707 // ============================================================================
708
709 OSStatus AudioDeviceIOS::RecordProcess(
710 void* inRefCon, 637 void* inRefCon,
711 AudioUnitRenderActionFlags* ioActionFlags, 638 AudioUnitRenderActionFlags* ioActionFlags,
712 const AudioTimeStamp* inTimeStamp, 639 const AudioTimeStamp* inTimeStamp,
713 UInt32 inBusNumber, 640 UInt32 inBusNumber,
714 UInt32 inNumberFrames, 641 UInt32 inNumberFrames,
715 AudioBufferList* ioData) { 642 AudioBufferList* ioData) {
716 AudioDeviceIOS* ptrThis = static_cast<AudioDeviceIOS*>(inRefCon); 643 DCHECK_EQ(1u, inBusNumber);
717 return ptrThis->RecordProcessImpl(ioActionFlags, inTimeStamp, inBusNumber, 644 DCHECK(!ioData); // no buffer should be allocated for input at this stage
718 inNumberFrames); 645 AudioDeviceIOS* thiz = static_cast<AudioDeviceIOS*>(inRefCon);
tkchin_webrtc 2015/09/01 20:54:50 nit: would just call it audio_device_ios instead o
henrika_webrtc 2015/09/03 13:44:41 Done.
646 return thiz->OnRecordedDataIsAvailable(ioActionFlags, inTimeStamp,
647 inBusNumber, inNumberFrames);
719 } 648 }
720 649
721 OSStatus AudioDeviceIOS::RecordProcessImpl( 650 OSStatus AudioDeviceIOS::OnRecordedDataIsAvailable(
722 AudioUnitRenderActionFlags* ioActionFlags, 651 AudioUnitRenderActionFlags* ioActionFlags,
723 const AudioTimeStamp* inTimeStamp, 652 const AudioTimeStamp* inTimeStamp,
724 uint32_t inBusNumber, 653 UInt32 inBusNumber,
725 uint32_t inNumberFrames) { 654 UInt32 inNumberFrames) {
726 // Setup some basic stuff 655 DCHECK_EQ(_recordParameters.frames_per_buffer(), inNumberFrames);
727 // Use temp buffer not to lock up recording buffer more than necessary 656 OSStatus result = noErr;
728 // todo: Make dataTmp a member variable with static size that holds 657 // Simply return if recording is not enabled.
729 // max possible frames? 658 if (!rtc::AtomicOps::AcquireLoad(&_recording))
730 int16_t* dataTmp = new int16_t[inNumberFrames]; 659 return result;
731 memset(dataTmp, 0, 2 * inNumberFrames); 660 // Obtain the recorded audio samples by initiating a rendering cycle.
732 661 // Since it happens on he input bus, the |ioData| parameter is a reference
tkchin_webrtc 2015/09/01 20:54:50 nit: the input bus
henrika_webrtc 2015/09/03 13:44:41 Done.
733 AudioBufferList abList; 662 // to the preallocated audio buffer list that the audio unit renders into.
734 abList.mNumberBuffers = 1; 663 // TODO(henrika): should error handling be improved?
735 abList.mBuffers[0].mData = dataTmp; 664 AudioBufferList* ioData = &_audioRecordBufferList;
736 abList.mBuffers[0].mDataByteSize = 2 * inNumberFrames; // 2 bytes/sample 665 result = AudioUnitRender(_vpioUnit, ioActionFlags, inTimeStamp, inBusNumber,
737 abList.mBuffers[0].mNumberChannels = 1; 666 inNumberFrames, ioData);
738 667 if (result != noErr) {
739 // Get data from mic 668 LOG_F(LS_ERROR) << "AudioOutputUnitStart failed: " << result;
tkchin_webrtc 2015/09/01 20:54:50 Is this only called during start? I read it as a c
henrika_webrtc 2015/09/03 13:44:41 It is not a callback on the capture side. The call
tkchin_webrtc 2015/09/04 05:29:16 Ah I gotcha.
henrika_webrtc 2015/09/04 09:51:18 Acknowledged.
740 OSStatus res = AudioUnitRender(_auVoiceProcessing, ioActionFlags, inTimeStamp, 669 return result;
741 inBusNumber, inNumberFrames, &abList);
742 if (res != 0) {
743 // TODO(henrika): improve error handling.
744 delete[] dataTmp;
745 return 0;
746 } 670 }
747 671 // Get a pointer to the recorded audio and send it to the WebRTC ADB.
748 if (_recording) { 672 // Use the FineAudioBuffer instance to convert between native buffer size
749 // Insert all data in temp buffer into recording buffers 673 // and the 10ms buffer size used by WebRTC.
750 // There is zero or one buffer partially full at any given time, 674 const UInt32 dataSizeInBytes = ioData->mBuffers[0].mDataByteSize;
751 // all others are full or empty 675 CHECK_EQ(dataSizeInBytes / kBytesPerSample, inNumberFrames);
752 // Full means filled with noSamp10ms samples. 676 SInt8* data = static_cast<SInt8*>(ioData->mBuffers[0].mData);
753 677 _fineAudioBuffer->DeliverRecordedData(data, dataSizeInBytes,
tkchin_webrtc 2015/09/01 20:54:50 What threading requirements are there for DeliverR
henrika_webrtc 2015/09/03 13:44:41 The data is copied to an internal buffer inside th
754 const unsigned int noSamp10ms = _adbSampFreq / 100; 678 kFixedPlayoutDelayEstimate,
755 unsigned int dataPos = 0; 679 kFixedRecordDelayEstimate);
756 uint16_t bufPos = 0; 680 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 } 681 }
810 682
811 OSStatus AudioDeviceIOS::PlayoutProcess( 683 OSStatus AudioDeviceIOS::GetPlayoutData(
812 void* inRefCon, 684 void* inRefCon,
813 AudioUnitRenderActionFlags* ioActionFlags, 685 AudioUnitRenderActionFlags* ioActionFlags,
814 const AudioTimeStamp* inTimeStamp, 686 const AudioTimeStamp* inTimeStamp,
815 UInt32 inBusNumber, 687 UInt32 inBusNumber,
816 UInt32 inNumberFrames, 688 UInt32 inNumberFrames,
817 AudioBufferList* ioData) { 689 AudioBufferList* ioData) {
818 AudioDeviceIOS* ptrThis = static_cast<AudioDeviceIOS*>(inRefCon); 690 DCHECK_EQ(0u, inBusNumber);
819 return ptrThis->PlayoutProcessImpl(inNumberFrames, ioData); 691 DCHECK(ioData);
692 AudioDeviceIOS* thiz = static_cast<AudioDeviceIOS*>(inRefCon);
693 return thiz->OnGetPlayoutData(ioActionFlags, inNumberFrames, ioData);
tkchin_webrtc 2015/09/01 20:54:50 ditto audio_device_ios
henrika_webrtc 2015/09/03 13:44:41 Done.
820 } 694 }
821 695
822 OSStatus AudioDeviceIOS::PlayoutProcessImpl(uint32_t inNumberFrames, 696 OSStatus AudioDeviceIOS::OnGetPlayoutData(
823 AudioBufferList* ioData) { 697 AudioUnitRenderActionFlags* ioActionFlags,
824 int16_t* data = static_cast<int16_t*>(ioData->mBuffers[0].mData); 698 UInt32 inNumberFrames,
825 unsigned int dataSizeBytes = ioData->mBuffers[0].mDataByteSize; 699 AudioBufferList* ioData) {
826 unsigned int dataSize = dataSizeBytes / 2; // Number of samples 700 // Verify 16-bit, noninterleaved mono PCM signal format.
827 CHECK_EQ(dataSize, inNumberFrames); 701 DCHECK_EQ(1u, ioData->mNumberBuffers);
828 memset(data, 0, dataSizeBytes); // Start with empty buffer 702 DCHECK_EQ(1u, ioData->mBuffers[0].mNumberChannels);
829 703 // Get pointer to internal audio buffer to which new audio data shall be
830 // Get playout data from Audio Device Buffer 704 // written.
831 705 const UInt32 dataSizeInBytes = ioData->mBuffers[0].mDataByteSize;
832 if (_playing) { 706 CHECK_EQ(dataSizeInBytes / kBytesPerSample, inNumberFrames);
833 unsigned int noSamp10ms = _adbSampFreq / 100; 707 SInt8* destination = static_cast<SInt8*>(ioData->mBuffers[0].mData);
834 // todo: Member variable and allocate when samp freq is determined 708 // Produce silence and give audio unit a hint about it if playout is not
835 int16_t* dataTmp = new int16_t[noSamp10ms]; 709 // activated.
836 memset(dataTmp, 0, 2 * noSamp10ms); 710 if (!rtc::AtomicOps::AcquireLoad(&_playing)) {
837 unsigned int dataPos = 0; 711 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
838 int noSamplesOut = 0; 712 memset(destination, 0, dataSizeInBytes);
839 unsigned int nCopy = 0; 713 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 } 714 }
881 return 0; 715 // Read decoded 16-bit PCM samples from WebRTC (using a size that matches
882 } 716 // the native I/O audio unit) to a preallocated intermediate buffer and
883 717 // copy the result to the audio buffer in the |ioData| destination.
884 // TODO(henrika): can either be removed or simplified. 718 SInt8* source = _playoutAudioBuffer.get();
885 void AudioDeviceIOS::UpdatePlayoutDelay() { 719 _fineAudioBuffer->GetPlayoutData(source);
886 ++_playoutDelayMeasurementCounter; 720 memcpy(destination, source, dataSizeInBytes);
887 721 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 } 722 }
1053 723
1054 } // namespace webrtc 724 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698