1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/audio/mac/audio_low_latency_input_mac.h"
6
7 #include <CoreServices/CoreServices.h>
8
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/mac/mac_logging.h"
12 #include "media/audio/mac/audio_manager_mac.h"
13 #include "media/base/audio_bus.h"
14 #include "media/base/data_buffer.h"
15
16 namespace media {
17
operator <<(std::ostream & os,const AudioStreamBasicDescription & format)18 static std::ostream& operator<<(std::ostream& os,
19 const AudioStreamBasicDescription& format) {
20 os << "sample rate : " << format.mSampleRate << std::endl
21 << "format ID : " << format.mFormatID << std::endl
22 << "format flags : " << format.mFormatFlags << std::endl
23 << "bytes per packet : " << format.mBytesPerPacket << std::endl
24 << "frames per packet : " << format.mFramesPerPacket << std::endl
25 << "bytes per frame : " << format.mBytesPerFrame << std::endl
26 << "channels per frame: " << format.mChannelsPerFrame << std::endl
27 << "bits per channel : " << format.mBitsPerChannel;
28 return os;
29 }
30
31 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit"
32 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
33 // for more details and background regarding this implementation.
34
AUAudioInputStream(AudioManagerMac * manager,const AudioParameters & input_params,const AudioParameters & output_params,AudioDeviceID audio_device_id)35 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager,
36 const AudioParameters& input_params,
37 const AudioParameters& output_params,
38 AudioDeviceID audio_device_id)
39 : manager_(manager),
40 sink_(NULL),
41 audio_unit_(0),
42 input_device_id_(audio_device_id),
43 started_(false),
44 hardware_latency_frames_(0),
45 fifo_delay_bytes_(0),
46 number_of_channels_in_frame_(0),
47 audio_bus_(media::AudioBus::Create(input_params)) {
48 DCHECK(manager_);
49
50 // Set up the desired (output) format specified by the client.
51 format_.mSampleRate = input_params.sample_rate();
52 format_.mFormatID = kAudioFormatLinearPCM;
53 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
54 kLinearPCMFormatFlagIsSignedInteger;
55 format_.mBitsPerChannel = input_params.bits_per_sample();
56 format_.mChannelsPerFrame = input_params.channels();
57 format_.mFramesPerPacket = 1; // uncompressed audio
58 format_.mBytesPerPacket = (format_.mBitsPerChannel *
59 input_params.channels()) / 8;
60 format_.mBytesPerFrame = format_.mBytesPerPacket;
61 format_.mReserved = 0;
62
63 DVLOG(1) << "Desired ouput format: " << format_;
64
65 // Set number of sample frames per callback used by the internal audio layer.
66 // An internal FIFO is then utilized to adapt the internal size to the size
67 // requested by the client.
68 number_of_frames_ = output_params.frames_per_buffer();
69 DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_;
70
71 // Derive size (in bytes) of the buffers that we will render to.
72 UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
73 DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size;
74
75 // Allocate AudioBuffers to be used as storage for the received audio.
76 // The AudioBufferList structure works as a placeholder for the
77 // AudioBuffer structure, which holds a pointer to the actual data buffer.
78 audio_data_buffer_.reset(new uint8[data_byte_size]);
79 audio_buffer_list_.mNumberBuffers = 1;
80
81 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
82 audio_buffer->mNumberChannels = input_params.channels();
83 audio_buffer->mDataByteSize = data_byte_size;
84 audio_buffer->mData = audio_data_buffer_.get();
85
86 // Set up an internal FIFO buffer that will accumulate recorded audio frames
87 // until a requested size is ready to be sent to the client.
88 // It is not possible to ask for less than |kAudioFramesPerCallback| number of
89 // audio frames.
90 size_t requested_size_frames =
91 input_params.GetBytesPerBuffer() / format_.mBytesPerPacket;
92 if (requested_size_frames < number_of_frames_) {
93 // For devices that only support a low sample rate like 8kHz, we adjust the
94 // buffer size to match number_of_frames_. The value of number_of_frames_
95 // in this case has not been calculated based on hardware settings but
96 // rather our hardcoded defaults (see ChooseBufferSize).
97 requested_size_frames = number_of_frames_;
98 }
99
100 requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame;
101 DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_;
102 DVLOG_IF(0, requested_size_frames > number_of_frames_) << "FIFO is used";
103
104 const int number_of_bytes = number_of_frames_ * format_.mBytesPerFrame;
105 fifo_delay_bytes_ = requested_size_bytes_ - number_of_bytes;
106
107 // Allocate some extra memory to avoid memory reallocations.
108 // Ensure that the size is an even multiple of |number_of_frames_ and
109 // larger than |requested_size_frames|.
110 // Example: number_of_frames_=128, requested_size_frames=480 =>
111 // allocated space equals 4*128=512 audio frames
112 const int max_forward_capacity = number_of_bytes *
113 ((requested_size_frames / number_of_frames_) + 1);
114 fifo_.reset(new media::SeekableBuffer(0, max_forward_capacity));
115
116 data_ = new media::DataBuffer(requested_size_bytes_);
117 }
118
~AUAudioInputStream()119 AUAudioInputStream::~AUAudioInputStream() {}
120
121 // Obtain and open the AUHAL AudioOutputUnit for recording.
Open()122 bool AUAudioInputStream::Open() {
123 // Verify that we are not already opened.
124 if (audio_unit_)
125 return false;
126
127 // Verify that we have a valid device.
128 if (input_device_id_ == kAudioObjectUnknown) {
129 NOTREACHED() << "Device ID is unknown";
130 return false;
131 }
132
133 // Start by obtaining an AudioOuputUnit using an AUHAL component description.
134
135 Component comp;
136 ComponentDescription desc;
137
138 // Description for the Audio Unit we want to use (AUHAL in this case).
139 desc.componentType = kAudioUnitType_Output;
140 desc.componentSubType = kAudioUnitSubType_HALOutput;
141 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
142 desc.componentFlags = 0;
143 desc.componentFlagsMask = 0;
144 comp = FindNextComponent(0, &desc);
145 DCHECK(comp);
146
147 // Get access to the service provided by the specified Audio Unit.
148 OSStatus result = OpenAComponent(comp, &audio_unit_);
149 if (result) {
150 HandleError(result);
151 return false;
152 }
153
154 // Enable IO on the input scope of the Audio Unit.
155
156 // After creating the AUHAL object, we must enable IO on the input scope
157 // of the Audio Unit to obtain the device input. Input must be explicitly
158 // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1
159 // of the AUHAL. Beacause the AUHAL can be used for both input and output,
160 // we must also disable IO on the output scope.
161
162 UInt32 enableIO = 1;
163
164 // Enable input on the AUHAL.
165 result = AudioUnitSetProperty(audio_unit_,
166 kAudioOutputUnitProperty_EnableIO,
167 kAudioUnitScope_Input,
168 1, // input element 1
169 &enableIO, // enable
170 sizeof(enableIO));
171 if (result) {
172 HandleError(result);
173 return false;
174 }
175
176 // Disable output on the AUHAL.
177 enableIO = 0;
178 result = AudioUnitSetProperty(audio_unit_,
179 kAudioOutputUnitProperty_EnableIO,
180 kAudioUnitScope_Output,
181 0, // output element 0
182 &enableIO, // disable
183 sizeof(enableIO));
184 if (result) {
185 HandleError(result);
186 return false;
187 }
188
189 // Next, set the audio device to be the Audio Unit's current device.
190 // Note that, devices can only be set to the AUHAL after enabling IO.
191 result = AudioUnitSetProperty(audio_unit_,
192 kAudioOutputUnitProperty_CurrentDevice,
193 kAudioUnitScope_Global,
194 0,
195 &input_device_id_,
196 sizeof(input_device_id_));
197 if (result) {
198 HandleError(result);
199 return false;
200 }
201
202 // Register the input procedure for the AUHAL.
203 // This procedure will be called when the AUHAL has received new data
204 // from the input device.
205 AURenderCallbackStruct callback;
206 callback.inputProc = InputProc;
207 callback.inputProcRefCon = this;
208 result = AudioUnitSetProperty(audio_unit_,
209 kAudioOutputUnitProperty_SetInputCallback,
210 kAudioUnitScope_Global,
211 0,
212 &callback,
213 sizeof(callback));
214 if (result) {
215 HandleError(result);
216 return false;
217 }
218
219 // Set up the the desired (output) format.
220 // For obtaining input from a device, the device format is always expressed
221 // on the output scope of the AUHAL's Element 1.
222 result = AudioUnitSetProperty(audio_unit_,
223 kAudioUnitProperty_StreamFormat,
224 kAudioUnitScope_Output,
225 1,
226 &format_,
227 sizeof(format_));
228 if (result) {
229 HandleError(result);
230 return false;
231 }
232
233 // Set the desired number of frames in the IO buffer (output scope).
234 // WARNING: Setting this value changes the frame size for all input audio
235 // units in the current process. As a result, the AURenderCallback must be
236 // able to handle arbitrary buffer sizes and FIFO appropriately.
237 UInt32 buffer_size = 0;
238 UInt32 property_size = sizeof(buffer_size);
239 result = AudioUnitGetProperty(audio_unit_,
240 kAudioDevicePropertyBufferFrameSize,
241 kAudioUnitScope_Output,
242 1,
243 &buffer_size,
244 &property_size);
245 if (result != noErr) {
246 HandleError(result);
247 return false;
248 }
249
250 // Only set the buffer size if we're the only active stream or the buffer size
251 // is lower than the current buffer size.
252 if (manager_->input_stream_count() == 1 || number_of_frames_ < buffer_size) {
253 buffer_size = number_of_frames_;
254 result = AudioUnitSetProperty(audio_unit_,
255 kAudioDevicePropertyBufferFrameSize,
256 kAudioUnitScope_Output,
257 1,
258 &buffer_size,
259 sizeof(buffer_size));
260 if (result != noErr) {
261 HandleError(result);
262 return false;
263 }
264 }
265
266 // Finally, initialize the audio unit and ensure that it is ready to render.
267 // Allocates memory according to the maximum number of audio frames
268 // it can produce in response to a single render call.
269 result = AudioUnitInitialize(audio_unit_);
270 if (result) {
271 HandleError(result);
272 return false;
273 }
274
275 // The hardware latency is fixed and will not change during the call.
276 hardware_latency_frames_ = GetHardwareLatency();
277
278 // The master channel is 0, Left and right are channels 1 and 2.
279 // And the master channel is not counted in |number_of_channels_in_frame_|.
280 number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
281
282 return true;
283 }
284
Start(AudioInputCallback * callback)285 void AUAudioInputStream::Start(AudioInputCallback* callback) {
286 DCHECK(callback);
287 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
288 if (started_ || !audio_unit_)
289 return;
290
291 // Check if we should defer Start() for http://crbug.com/160920.
292 if (manager_->ShouldDeferStreamStart()) {
293 // Use a cancellable closure so that if Stop() is called before Start()
294 // actually runs, we can cancel the pending start.
295 deferred_start_cb_.Reset(base::Bind(
296 &AUAudioInputStream::Start, base::Unretained(this), callback));
297 manager_->GetTaskRunner()->PostDelayedTask(
298 FROM_HERE,
299 deferred_start_cb_.callback(),
300 base::TimeDelta::FromSeconds(
301 AudioManagerMac::kStartDelayInSecsForPowerEvents));
302 return;
303 }
304
305 sink_ = callback;
306 StartAgc();
307 OSStatus result = AudioOutputUnitStart(audio_unit_);
308 if (result == noErr) {
309 started_ = true;
310 }
311 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
312 << "Failed to start acquiring data";
313 }
314
Stop()315 void AUAudioInputStream::Stop() {
316 if (!started_)
317 return;
318 StopAgc();
319 OSStatus result = AudioOutputUnitStop(audio_unit_);
320 DCHECK_EQ(result, noErr);
321 started_ = false;
322 sink_ = NULL;
323
324 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
325 << "Failed to stop acquiring data";
326 }
327
Close()328 void AUAudioInputStream::Close() {
329 // It is valid to call Close() before calling open or Start().
330 // It is also valid to call Close() after Start() has been called.
331 if (started_) {
332 Stop();
333 }
334 if (audio_unit_) {
335 // Deallocate the audio unit’s resources.
336 AudioUnitUninitialize(audio_unit_);
337
338 // Terminates our connection to the AUHAL component.
339 CloseComponent(audio_unit_);
340 audio_unit_ = 0;
341 }
342
343 // Inform the audio manager that we have been closed. This can cause our
344 // destruction.
345 manager_->ReleaseInputStream(this);
346 }
347
GetMaxVolume()348 double AUAudioInputStream::GetMaxVolume() {
349 // Verify that we have a valid device.
350 if (input_device_id_ == kAudioObjectUnknown) {
351 NOTREACHED() << "Device ID is unknown";
352 return 0.0;
353 }
354
355 // Query if any of the master, left or right channels has volume control.
356 for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
357 // If the volume is settable, the valid volume range is [0.0, 1.0].
358 if (IsVolumeSettableOnChannel(i))
359 return 1.0;
360 }
361
362 // Volume control is not available for the audio stream.
363 return 0.0;
364 }
365
SetVolume(double volume)366 void AUAudioInputStream::SetVolume(double volume) {
367 DVLOG(1) << "SetVolume(volume=" << volume << ")";
368 DCHECK_GE(volume, 0.0);
369 DCHECK_LE(volume, 1.0);
370
371 // Verify that we have a valid device.
372 if (input_device_id_ == kAudioObjectUnknown) {
373 NOTREACHED() << "Device ID is unknown";
374 return;
375 }
376
377 Float32 volume_float32 = static_cast<Float32>(volume);
378 AudioObjectPropertyAddress property_address = {
379 kAudioDevicePropertyVolumeScalar,
380 kAudioDevicePropertyScopeInput,
381 kAudioObjectPropertyElementMaster
382 };
383
384 // Try to set the volume for master volume channel.
385 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
386 OSStatus result = AudioObjectSetPropertyData(input_device_id_,
387 &property_address,
388 0,
389 NULL,
390 sizeof(volume_float32),
391 &volume_float32);
392 if (result != noErr) {
393 DLOG(WARNING) << "Failed to set volume to " << volume_float32;
394 }
395 return;
396 }
397
398 // There is no master volume control, try to set volume for each channel.
399 int successful_channels = 0;
400 for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
401 property_address.mElement = static_cast<UInt32>(i);
402 if (IsVolumeSettableOnChannel(i)) {
403 OSStatus result = AudioObjectSetPropertyData(input_device_id_,
404 &property_address,
405 0,
406 NULL,
407 sizeof(volume_float32),
408 &volume_float32);
409 if (result == noErr)
410 ++successful_channels;
411 }
412 }
413
414 DLOG_IF(WARNING, successful_channels == 0)
415 << "Failed to set volume to " << volume_float32;
416
417 // Update the AGC volume level based on the last setting above. Note that,
418 // the volume-level resolution is not infinite and it is therefore not
419 // possible to assume that the volume provided as input parameter can be
420 // used directly. Instead, a new query to the audio hardware is required.
421 // This method does nothing if AGC is disabled.
422 UpdateAgcVolume();
423 }
424
GetVolume()425 double AUAudioInputStream::GetVolume() {
426 // Verify that we have a valid device.
427 if (input_device_id_ == kAudioObjectUnknown){
428 NOTREACHED() << "Device ID is unknown";
429 return 0.0;
430 }
431
432 AudioObjectPropertyAddress property_address = {
433 kAudioDevicePropertyVolumeScalar,
434 kAudioDevicePropertyScopeInput,
435 kAudioObjectPropertyElementMaster
436 };
437
438 if (AudioObjectHasProperty(input_device_id_, &property_address)) {
439 // The device supports master volume control, get the volume from the
440 // master channel.
441 Float32 volume_float32 = 0.0;
442 UInt32 size = sizeof(volume_float32);
443 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
444 &property_address,
445 0,
446 NULL,
447 &size,
448 &volume_float32);
449 if (result == noErr)
450 return static_cast<double>(volume_float32);
451 } else {
452 // There is no master volume control, try to get the average volume of
453 // all the channels.
454 Float32 volume_float32 = 0.0;
455 int successful_channels = 0;
456 for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
457 property_address.mElement = static_cast<UInt32>(i);
458 if (AudioObjectHasProperty(input_device_id_, &property_address)) {
459 Float32 channel_volume = 0;
460 UInt32 size = sizeof(channel_volume);
461 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
462 &property_address,
463 0,
464 NULL,
465 &size,
466 &channel_volume);
467 if (result == noErr) {
468 volume_float32 += channel_volume;
469 ++successful_channels;
470 }
471 }
472 }
473
474 // Get the average volume of the channels.
475 if (successful_channels != 0)
476 return static_cast<double>(volume_float32 / successful_channels);
477 }
478
479 DLOG(WARNING) << "Failed to get volume";
480 return 0.0;
481 }
482
483 // AUHAL AudioDeviceOutput unit callback
InputProc(void * user_data,AudioUnitRenderActionFlags * flags,const AudioTimeStamp * time_stamp,UInt32 bus_number,UInt32 number_of_frames,AudioBufferList * io_data)484 OSStatus AUAudioInputStream::InputProc(void* user_data,
485 AudioUnitRenderActionFlags* flags,
486 const AudioTimeStamp* time_stamp,
487 UInt32 bus_number,
488 UInt32 number_of_frames,
489 AudioBufferList* io_data) {
490 // Verify that the correct bus is used (Input bus/Element 1)
491 DCHECK_EQ(bus_number, static_cast<UInt32>(1));
492 AUAudioInputStream* audio_input =
493 reinterpret_cast<AUAudioInputStream*>(user_data);
494 DCHECK(audio_input);
495 if (!audio_input)
496 return kAudioUnitErr_InvalidElement;
497
498 // Receive audio from the AUHAL from the output scope of the Audio Unit.
499 OSStatus result = AudioUnitRender(audio_input->audio_unit(),
500 flags,
501 time_stamp,
502 bus_number,
503 number_of_frames,
504 audio_input->audio_buffer_list());
505 if (result)
506 return result;
507
508 // Deliver recorded data to the consumer as a callback.
509 return audio_input->Provide(number_of_frames,
510 audio_input->audio_buffer_list(),
511 time_stamp);
512 }
513
Provide(UInt32 number_of_frames,AudioBufferList * io_data,const AudioTimeStamp * time_stamp)514 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
515 AudioBufferList* io_data,
516 const AudioTimeStamp* time_stamp) {
517 // Update the capture latency.
518 double capture_latency_frames = GetCaptureLatency(time_stamp);
519
520 // The AGC volume level is updated once every second on a separate thread.
521 // Note that, |volume| is also updated each time SetVolume() is called
522 // through IPC by the render-side AGC.
523 double normalized_volume = 0.0;
524 GetAgcVolume(&normalized_volume);
525
526 AudioBuffer& buffer = io_data->mBuffers[0];
527 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
528 uint32 capture_delay_bytes = static_cast<uint32>
529 ((capture_latency_frames + 0.5) * format_.mBytesPerFrame);
530 // Account for the extra delay added by the FIFO.
531 capture_delay_bytes += fifo_delay_bytes_;
532 DCHECK(audio_data);
533 if (!audio_data)
534 return kAudioUnitErr_InvalidElement;
535
536 // Accumulate captured audio in FIFO until we can match the output size
537 // requested by the client.
538 fifo_->Append(audio_data, buffer.mDataByteSize);
539
540 // Deliver recorded data to the client as soon as the FIFO contains a
541 // sufficient amount.
542 if (fifo_->forward_bytes() >= requested_size_bytes_) {
543 // Read from FIFO into temporary data buffer.
544 fifo_->Read(data_->writable_data(), requested_size_bytes_);
545
546 // Copy captured (and interleaved) data into deinterleaved audio bus.
547 audio_bus_->FromInterleaved(
548 data_->data(), audio_bus_->frames(), format_.mBitsPerChannel / 8);
549
550 // Deliver data packet, delay estimation and volume level to the user.
551 sink_->OnData(
552 this, audio_bus_.get(), capture_delay_bytes, normalized_volume);
553 }
554
555 return noErr;
556 }
557
HardwareSampleRate()558 int AUAudioInputStream::HardwareSampleRate() {
559 // Determine the default input device's sample-rate.
560 AudioDeviceID device_id = kAudioObjectUnknown;
561 UInt32 info_size = sizeof(device_id);
562
563 AudioObjectPropertyAddress default_input_device_address = {
564 kAudioHardwarePropertyDefaultInputDevice,
565 kAudioObjectPropertyScopeGlobal,
566 kAudioObjectPropertyElementMaster
567 };
568 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
569 &default_input_device_address,
570 0,
571 0,
572 &info_size,
573 &device_id);
574 if (result != noErr)
575 return 0.0;
576
577 Float64 nominal_sample_rate;
578 info_size = sizeof(nominal_sample_rate);
579
580 AudioObjectPropertyAddress nominal_sample_rate_address = {
581 kAudioDevicePropertyNominalSampleRate,
582 kAudioObjectPropertyScopeGlobal,
583 kAudioObjectPropertyElementMaster
584 };
585 result = AudioObjectGetPropertyData(device_id,
586 &nominal_sample_rate_address,
587 0,
588 0,
589 &info_size,
590 &nominal_sample_rate);
591 if (result != noErr)
592 return 0.0;
593
594 return static_cast<int>(nominal_sample_rate);
595 }
596
GetHardwareLatency()597 double AUAudioInputStream::GetHardwareLatency() {
598 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) {
599 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
600 return 0.0;
601 }
602
603 // Get audio unit latency.
604 Float64 audio_unit_latency_sec = 0.0;
605 UInt32 size = sizeof(audio_unit_latency_sec);
606 OSStatus result = AudioUnitGetProperty(audio_unit_,
607 kAudioUnitProperty_Latency,
608 kAudioUnitScope_Global,
609 0,
610 &audio_unit_latency_sec,
611 &size);
612 OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
613 << "Could not get audio unit latency";
614
615 // Get input audio device latency.
616 AudioObjectPropertyAddress property_address = {
617 kAudioDevicePropertyLatency,
618 kAudioDevicePropertyScopeInput,
619 kAudioObjectPropertyElementMaster
620 };
621 UInt32 device_latency_frames = 0;
622 size = sizeof(device_latency_frames);
623 result = AudioObjectGetPropertyData(input_device_id_,
624 &property_address,
625 0,
626 NULL,
627 &size,
628 &device_latency_frames);
629 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
630
631 return static_cast<double>((audio_unit_latency_sec *
632 format_.mSampleRate) + device_latency_frames);
633 }
634
GetCaptureLatency(const AudioTimeStamp * input_time_stamp)635 double AUAudioInputStream::GetCaptureLatency(
636 const AudioTimeStamp* input_time_stamp) {
637 // Get the delay between between the actual recording instant and the time
638 // when the data packet is provided as a callback.
639 UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
640 input_time_stamp->mHostTime);
641 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
642 double delay_frames = static_cast<double>
643 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
644
645 // Total latency is composed by the dynamic latency and the fixed
646 // hardware latency.
647 return (delay_frames + hardware_latency_frames_);
648 }
649
GetNumberOfChannelsFromStream()650 int AUAudioInputStream::GetNumberOfChannelsFromStream() {
651 // Get the stream format, to be able to read the number of channels.
652 AudioObjectPropertyAddress property_address = {
653 kAudioDevicePropertyStreamFormat,
654 kAudioDevicePropertyScopeInput,
655 kAudioObjectPropertyElementMaster
656 };
657 AudioStreamBasicDescription stream_format;
658 UInt32 size = sizeof(stream_format);
659 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
660 &property_address,
661 0,
662 NULL,
663 &size,
664 &stream_format);
665 if (result != noErr) {
666 DLOG(WARNING) << "Could not get stream format";
667 return 0;
668 }
669
670 return static_cast<int>(stream_format.mChannelsPerFrame);
671 }
672
HandleError(OSStatus err)673 void AUAudioInputStream::HandleError(OSStatus err) {
674 NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
675 << " (" << err << ")";
676 if (sink_)
677 sink_->OnError(this);
678 }
679
IsVolumeSettableOnChannel(int channel)680 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
681 Boolean is_settable = false;
682 AudioObjectPropertyAddress property_address = {
683 kAudioDevicePropertyVolumeScalar,
684 kAudioDevicePropertyScopeInput,
685 static_cast<UInt32>(channel)
686 };
687 OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
688 &property_address,
689 &is_settable);
690 return (result == noErr) ? is_settable : false;
691 }
692
693 } // namespace media
694