Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / media / audio / mac / audio_low_latency_input_mac.cc
blobe5e51bb3404538184952b17fb1d04a905322fb10
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.
5 #include "media/audio/mac/audio_low_latency_input_mac.h"
7 #include <CoreServices/CoreServices.h>
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/mac/mac_logging.h"
12 #include "base/metrics/sparse_histogram.h"
13 #include "media/audio/mac/audio_manager_mac.h"
14 #include "media/base/audio_bus.h"
15 #include "media/base/data_buffer.h"
17 namespace media {
19 // Number of blocks of buffers used in the |fifo_|.
20 const int kNumberOfBlocksBufferInFifo = 2;
22 // Max length of sequence of TooManyFramesToProcessError errors.
23 // The stream will be stopped as soon as this time limit is passed.
24 const int kMaxErrorTimeoutInSeconds = 1;
26 static std::ostream& operator<<(std::ostream& os,
27 const AudioStreamBasicDescription& format) {
28 os << "sample rate : " << format.mSampleRate << std::endl
29 << "format ID : " << format.mFormatID << std::endl
30 << "format flags : " << format.mFormatFlags << std::endl
31 << "bytes per packet : " << format.mBytesPerPacket << std::endl
32 << "frames per packet : " << format.mFramesPerPacket << std::endl
33 << "bytes per frame : " << format.mBytesPerFrame << std::endl
34 << "channels per frame: " << format.mChannelsPerFrame << std::endl
35 << "bits per channel : " << format.mBitsPerChannel;
36 return os;
39 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit"
40 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
41 // for more details and background regarding this implementation.
43 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager,
44 const AudioParameters& input_params,
45 AudioDeviceID audio_device_id)
46 : manager_(manager),
47 number_of_frames_(input_params.frames_per_buffer()),
48 sink_(NULL),
49 audio_unit_(0),
50 input_device_id_(audio_device_id),
51 started_(false),
52 hardware_latency_frames_(0),
53 number_of_channels_in_frame_(0),
54 fifo_(input_params.channels(),
55 number_of_frames_,
56 kNumberOfBlocksBufferInFifo) {
57 DCHECK(manager_);
59 // Set up the desired (output) format specified by the client.
60 format_.mSampleRate = input_params.sample_rate();
61 format_.mFormatID = kAudioFormatLinearPCM;
62 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
63 kLinearPCMFormatFlagIsSignedInteger;
64 format_.mBitsPerChannel = input_params.bits_per_sample();
65 format_.mChannelsPerFrame = input_params.channels();
66 format_.mFramesPerPacket = 1; // uncompressed audio
67 format_.mBytesPerPacket = (format_.mBitsPerChannel *
68 input_params.channels()) / 8;
69 format_.mBytesPerFrame = format_.mBytesPerPacket;
70 format_.mReserved = 0;
72 DVLOG(1) << "Desired ouput format: " << format_;
74 // Derive size (in bytes) of the buffers that we will render to.
75 UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
76 DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size;
78 // Allocate AudioBuffers to be used as storage for the received audio.
79 // The AudioBufferList structure works as a placeholder for the
80 // AudioBuffer structure, which holds a pointer to the actual data buffer.
81 audio_data_buffer_.reset(new uint8[data_byte_size]);
82 audio_buffer_list_.mNumberBuffers = 1;
84 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
85 audio_buffer->mNumberChannels = input_params.channels();
86 audio_buffer->mDataByteSize = data_byte_size;
87 audio_buffer->mData = audio_data_buffer_.get();
90 AUAudioInputStream::~AUAudioInputStream() {}
92 // Obtain and open the AUHAL AudioOutputUnit for recording.
93 bool AUAudioInputStream::Open() {
94 // Verify that we are not already opened.
95 if (audio_unit_)
96 return false;
98 // Verify that we have a valid device.
99 if (input_device_id_ == kAudioObjectUnknown) {
100 NOTREACHED() << "Device ID is unknown";
101 return false;
104 // Start by obtaining an AudioOuputUnit using an AUHAL component description.
106 // Description for the Audio Unit we want to use (AUHAL in this case).
107 AudioComponentDescription desc = {
108 kAudioUnitType_Output,
109 kAudioUnitSubType_HALOutput,
110 kAudioUnitManufacturer_Apple,
115 AudioComponent comp = AudioComponentFindNext(0, &desc);
116 DCHECK(comp);
118 // Get access to the service provided by the specified Audio Unit.
119 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
120 if (result) {
121 HandleError(result);
122 return false;
125 // Enable IO on the input scope of the Audio Unit.
127 // After creating the AUHAL object, we must enable IO on the input scope
128 // of the Audio Unit to obtain the device input. Input must be explicitly
129 // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1
130 // of the AUHAL. Beacause the AUHAL can be used for both input and output,
131 // we must also disable IO on the output scope.
133 UInt32 enableIO = 1;
135 // Enable input on the AUHAL.
136 result = AudioUnitSetProperty(audio_unit_,
137 kAudioOutputUnitProperty_EnableIO,
138 kAudioUnitScope_Input,
139 1, // input element 1
140 &enableIO, // enable
141 sizeof(enableIO));
142 if (result) {
143 HandleError(result);
144 return false;
147 // Disable output on the AUHAL.
148 enableIO = 0;
149 result = AudioUnitSetProperty(audio_unit_,
150 kAudioOutputUnitProperty_EnableIO,
151 kAudioUnitScope_Output,
152 0, // output element 0
153 &enableIO, // disable
154 sizeof(enableIO));
155 if (result) {
156 HandleError(result);
157 return false;
160 // Next, set the audio device to be the Audio Unit's current device.
161 // Note that, devices can only be set to the AUHAL after enabling IO.
162 result = AudioUnitSetProperty(audio_unit_,
163 kAudioOutputUnitProperty_CurrentDevice,
164 kAudioUnitScope_Global,
166 &input_device_id_,
167 sizeof(input_device_id_));
168 if (result) {
169 HandleError(result);
170 return false;
173 // Set up the the desired (output) format.
174 // For obtaining input from a device, the device format is always expressed
175 // on the output scope of the AUHAL's Element 1.
176 result = AudioUnitSetProperty(audio_unit_,
177 kAudioUnitProperty_StreamFormat,
178 kAudioUnitScope_Output,
180 &format_,
181 sizeof(format_));
182 if (result) {
183 HandleError(result);
184 return false;
187 if (!manager_->MaybeChangeBufferSize(
188 input_device_id_, audio_unit_, 1, number_of_frames_))
189 return false;
191 // Register the input procedure for the AUHAL.
192 // This procedure will be called when the AUHAL has received new data
193 // from the input device.
194 AURenderCallbackStruct callback;
195 callback.inputProc = InputProc;
196 callback.inputProcRefCon = this;
197 result = AudioUnitSetProperty(audio_unit_,
198 kAudioOutputUnitProperty_SetInputCallback,
199 kAudioUnitScope_Global,
201 &callback,
202 sizeof(callback));
203 if (result) {
204 HandleError(result);
205 return false;
208 // Finally, initialize the audio unit and ensure that it is ready to render.
209 // Allocates memory according to the maximum number of audio frames
210 // it can produce in response to a single render call.
211 result = AudioUnitInitialize(audio_unit_);
212 if (result) {
213 HandleError(result);
214 return false;
217 // The hardware latency is fixed and will not change during the call.
218 hardware_latency_frames_ = GetHardwareLatency();
220 // The master channel is 0, Left and right are channels 1 and 2.
221 // And the master channel is not counted in |number_of_channels_in_frame_|.
222 number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
224 return true;
227 void AUAudioInputStream::Start(AudioInputCallback* callback) {
228 DCHECK(callback);
229 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
230 if (started_ || !audio_unit_)
231 return;
233 // Check if we should defer Start() for http://crbug.com/160920.
234 if (manager_->ShouldDeferStreamStart()) {
235 // Use a cancellable closure so that if Stop() is called before Start()
236 // actually runs, we can cancel the pending start.
237 deferred_start_cb_.Reset(base::Bind(
238 &AUAudioInputStream::Start, base::Unretained(this), callback));
239 manager_->GetTaskRunner()->PostDelayedTask(
240 FROM_HERE,
241 deferred_start_cb_.callback(),
242 base::TimeDelta::FromSeconds(
243 AudioManagerMac::kStartDelayInSecsForPowerEvents));
244 return;
247 sink_ = callback;
248 last_success_time_ = base::TimeTicks::Now();
249 StartAgc();
250 OSStatus result = AudioOutputUnitStart(audio_unit_);
251 if (result == noErr) {
252 started_ = true;
254 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
255 << "Failed to start acquiring data";
258 void AUAudioInputStream::Stop() {
259 if (!started_)
260 return;
261 StopAgc();
262 OSStatus result = AudioOutputUnitStop(audio_unit_);
263 DCHECK_EQ(result, noErr);
264 started_ = false;
265 sink_ = NULL;
266 fifo_.Clear();
268 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
269 << "Failed to stop acquiring data";
272 void AUAudioInputStream::Close() {
273 // It is valid to call Close() before calling open or Start().
274 // It is also valid to call Close() after Start() has been called.
275 if (started_) {
276 Stop();
278 if (audio_unit_) {
279 // Deallocate the audio unit’s resources.
280 OSStatus result = AudioUnitUninitialize(audio_unit_);
281 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
282 << "AudioUnitUninitialize() failed.";
284 result = AudioComponentInstanceDispose(audio_unit_);
285 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
286 << "AudioComponentInstanceDispose() failed.";
288 audio_unit_ = 0;
291 // Inform the audio manager that we have been closed. This can cause our
292 // destruction.
293 manager_->ReleaseInputStream(this);
296 double AUAudioInputStream::GetMaxVolume() {
297 // Verify that we have a valid device.
298 if (input_device_id_ == kAudioObjectUnknown) {
299 NOTREACHED() << "Device ID is unknown";
300 return 0.0;
303 // Query if any of the master, left or right channels has volume control.
304 for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
305 // If the volume is settable, the valid volume range is [0.0, 1.0].
306 if (IsVolumeSettableOnChannel(i))
307 return 1.0;
310 // Volume control is not available for the audio stream.
311 return 0.0;
314 void AUAudioInputStream::SetVolume(double volume) {
315 DVLOG(1) << "SetVolume(volume=" << volume << ")";
316 DCHECK_GE(volume, 0.0);
317 DCHECK_LE(volume, 1.0);
319 // Verify that we have a valid device.
320 if (input_device_id_ == kAudioObjectUnknown) {
321 NOTREACHED() << "Device ID is unknown";
322 return;
325 Float32 volume_float32 = static_cast<Float32>(volume);
326 AudioObjectPropertyAddress property_address = {
327 kAudioDevicePropertyVolumeScalar,
328 kAudioDevicePropertyScopeInput,
329 kAudioObjectPropertyElementMaster
332 // Try to set the volume for master volume channel.
333 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
334 OSStatus result = AudioObjectSetPropertyData(input_device_id_,
335 &property_address,
337 NULL,
338 sizeof(volume_float32),
339 &volume_float32);
340 if (result != noErr) {
341 DLOG(WARNING) << "Failed to set volume to " << volume_float32;
343 return;
346 // There is no master volume control, try to set volume for each channel.
347 int successful_channels = 0;
348 for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
349 property_address.mElement = static_cast<UInt32>(i);
350 if (IsVolumeSettableOnChannel(i)) {
351 OSStatus result = AudioObjectSetPropertyData(input_device_id_,
352 &property_address,
354 NULL,
355 sizeof(volume_float32),
356 &volume_float32);
357 if (result == noErr)
358 ++successful_channels;
362 DLOG_IF(WARNING, successful_channels == 0)
363 << "Failed to set volume to " << volume_float32;
365 // Update the AGC volume level based on the last setting above. Note that,
366 // the volume-level resolution is not infinite and it is therefore not
367 // possible to assume that the volume provided as input parameter can be
368 // used directly. Instead, a new query to the audio hardware is required.
369 // This method does nothing if AGC is disabled.
370 UpdateAgcVolume();
373 double AUAudioInputStream::GetVolume() {
374 // Verify that we have a valid device.
375 if (input_device_id_ == kAudioObjectUnknown){
376 NOTREACHED() << "Device ID is unknown";
377 return 0.0;
380 AudioObjectPropertyAddress property_address = {
381 kAudioDevicePropertyVolumeScalar,
382 kAudioDevicePropertyScopeInput,
383 kAudioObjectPropertyElementMaster
386 if (AudioObjectHasProperty(input_device_id_, &property_address)) {
387 // The device supports master volume control, get the volume from the
388 // master channel.
389 Float32 volume_float32 = 0.0;
390 UInt32 size = sizeof(volume_float32);
391 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
392 &property_address,
394 NULL,
395 &size,
396 &volume_float32);
397 if (result == noErr)
398 return static_cast<double>(volume_float32);
399 } else {
400 // There is no master volume control, try to get the average volume of
401 // all the channels.
402 Float32 volume_float32 = 0.0;
403 int successful_channels = 0;
404 for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
405 property_address.mElement = static_cast<UInt32>(i);
406 if (AudioObjectHasProperty(input_device_id_, &property_address)) {
407 Float32 channel_volume = 0;
408 UInt32 size = sizeof(channel_volume);
409 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
410 &property_address,
412 NULL,
413 &size,
414 &channel_volume);
415 if (result == noErr) {
416 volume_float32 += channel_volume;
417 ++successful_channels;
422 // Get the average volume of the channels.
423 if (successful_channels != 0)
424 return static_cast<double>(volume_float32 / successful_channels);
427 DLOG(WARNING) << "Failed to get volume";
428 return 0.0;
431 bool AUAudioInputStream::IsMuted() {
432 // Verify that we have a valid device.
433 DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown";
435 AudioObjectPropertyAddress property_address = {
436 kAudioDevicePropertyMute,
437 kAudioDevicePropertyScopeInput,
438 kAudioObjectPropertyElementMaster
441 if (!AudioObjectHasProperty(input_device_id_, &property_address)) {
442 DLOG(ERROR) << "Device does not support checking master mute state";
443 return false;
446 UInt32 muted = 0;
447 UInt32 size = sizeof(muted);
448 OSStatus result = AudioObjectGetPropertyData(
449 input_device_id_, &property_address, 0, NULL, &size, &muted);
450 DLOG_IF(WARNING, result != noErr) << "Failed to get mute state";
451 return result == noErr && muted != 0;
454 // AUHAL AudioDeviceOutput unit callback
455 OSStatus AUAudioInputStream::InputProc(void* user_data,
456 AudioUnitRenderActionFlags* flags,
457 const AudioTimeStamp* time_stamp,
458 UInt32 bus_number,
459 UInt32 number_of_frames,
460 AudioBufferList* io_data) {
461 // Verify that the correct bus is used (Input bus/Element 1)
462 DCHECK_EQ(bus_number, static_cast<UInt32>(1));
463 AUAudioInputStream* audio_input =
464 reinterpret_cast<AUAudioInputStream*>(user_data);
465 DCHECK(audio_input);
466 if (!audio_input)
467 return kAudioUnitErr_InvalidElement;
469 // Update the |mDataByteSize| value in the audio_buffer_list() since
470 // |number_of_frames| can be changed on the fly.
471 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|,
472 // otherwise it will put CoreAudio into bad state and results in
473 // AudioUnitRender() returning -50 for the new created stream.
474 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and
475 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes.
476 // See crbug/428706 for details.
477 UInt32 new_size = number_of_frames * audio_input->format_.mBytesPerFrame;
478 AudioBuffer* audio_buffer = audio_input->audio_buffer_list()->mBuffers;
479 if (new_size != audio_buffer->mDataByteSize) {
480 if (new_size > audio_buffer->mDataByteSize) {
481 // This can happen if the device is unpluged during recording. We
482 // allocate enough memory here to avoid depending on how CoreAudio
483 // handles it.
484 // See See http://www.crbug.com/434681 for one example when we can enter
485 // this scope.
486 audio_input->audio_data_buffer_.reset(new uint8[new_size]);
487 audio_buffer->mData = audio_input->audio_data_buffer_.get();
490 // Update the |mDataByteSize| to match |number_of_frames|.
491 audio_buffer->mDataByteSize = new_size;
494 // Receive audio from the AUHAL from the output scope of the Audio Unit.
495 OSStatus result = AudioUnitRender(audio_input->audio_unit(),
496 flags,
497 time_stamp,
498 bus_number,
499 number_of_frames,
500 audio_input->audio_buffer_list());
501 if (result) {
502 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.AudioInputCbErrorMac", result);
503 OSSTATUS_DLOG(ERROR, result) << "AudioUnitRender() failed ";
504 if (result != kAudioUnitErr_TooManyFramesToProcess) {
505 audio_input->HandleError(result);
506 } else {
507 DCHECK(!audio_input->last_success_time_.is_null());
508 // We delay stopping the stream for kAudioUnitErr_TooManyFramesToProcess
509 // since it has been observed that some USB headsets can cause this error
510 // but only for a few initial frames at startup and then then the stream
511 // returns to a stable state again. See b/19524368 for details.
512 // Instead, we measure time since last valid audio frame and call
513 // HandleError() only if a too long error sequence is detected. We do
514 // this to avoid ending up in a non recoverable bad core audio state.
515 base::TimeDelta time_since_last_success =
516 base::TimeTicks::Now() - audio_input->last_success_time_;
517 if ((time_since_last_success >
518 base::TimeDelta::FromSeconds(kMaxErrorTimeoutInSeconds))) {
519 DLOG(ERROR) << "Too long sequence of TooManyFramesToProcess errors!";
520 audio_input->HandleError(result);
523 return result;
525 // Update time of successful call to AudioUnitRender().
526 audio_input->last_success_time_ = base::TimeTicks::Now();
528 // Deliver recorded data to the consumer as a callback.
529 return audio_input->Provide(number_of_frames,
530 audio_input->audio_buffer_list(),
531 time_stamp);
534 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
535 AudioBufferList* io_data,
536 const AudioTimeStamp* time_stamp) {
537 // Update the capture latency.
538 double capture_latency_frames = GetCaptureLatency(time_stamp);
540 // The AGC volume level is updated once every second on a separate thread.
541 // Note that, |volume| is also updated each time SetVolume() is called
542 // through IPC by the render-side AGC.
543 double normalized_volume = 0.0;
544 GetAgcVolume(&normalized_volume);
546 AudioBuffer& buffer = io_data->mBuffers[0];
547 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
548 uint32 capture_delay_bytes = static_cast<uint32>
549 ((capture_latency_frames + 0.5) * format_.mBytesPerFrame);
550 DCHECK(audio_data);
551 if (!audio_data)
552 return kAudioUnitErr_InvalidElement;
554 // Dynamically increase capacity of the FIFO to handle larger buffers from
555 // CoreAudio. This can happen in combination with Apple Thunderbolt Displays
556 // when the Display Audio is used as capture source and the cable is first
557 // remove and then inserted again.
558 // See http://www.crbug.com/434681 for details.
559 if (static_cast<int>(number_of_frames) > fifo_.GetUnfilledFrames()) {
560 // Derive required increase in number of FIFO blocks. The increase is
561 // typically one block.
562 const int blocks =
563 static_cast<int>((number_of_frames - fifo_.GetUnfilledFrames()) /
564 number_of_frames_) + 1;
565 DLOG(WARNING) << "Increasing FIFO capacity by " << blocks << " blocks";
566 fifo_.IncreaseCapacity(blocks);
569 // Copy captured (and interleaved) data into FIFO.
570 fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8);
572 // Consume and deliver the data when the FIFO has a block of available data.
573 while (fifo_.available_blocks()) {
574 const AudioBus* audio_bus = fifo_.Consume();
575 DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_));
577 // Compensate the audio delay caused by the FIFO.
578 capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame;
579 sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume);
582 return noErr;
585 int AUAudioInputStream::HardwareSampleRate() {
586 // Determine the default input device's sample-rate.
587 AudioDeviceID device_id = kAudioObjectUnknown;
588 UInt32 info_size = sizeof(device_id);
590 AudioObjectPropertyAddress default_input_device_address = {
591 kAudioHardwarePropertyDefaultInputDevice,
592 kAudioObjectPropertyScopeGlobal,
593 kAudioObjectPropertyElementMaster
595 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
596 &default_input_device_address,
599 &info_size,
600 &device_id);
601 if (result != noErr)
602 return 0.0;
604 Float64 nominal_sample_rate;
605 info_size = sizeof(nominal_sample_rate);
607 AudioObjectPropertyAddress nominal_sample_rate_address = {
608 kAudioDevicePropertyNominalSampleRate,
609 kAudioObjectPropertyScopeGlobal,
610 kAudioObjectPropertyElementMaster
612 result = AudioObjectGetPropertyData(device_id,
613 &nominal_sample_rate_address,
616 &info_size,
617 &nominal_sample_rate);
618 if (result != noErr)
619 return 0.0;
621 return static_cast<int>(nominal_sample_rate);
624 double AUAudioInputStream::GetHardwareLatency() {
625 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) {
626 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
627 return 0.0;
630 // Get audio unit latency.
631 Float64 audio_unit_latency_sec = 0.0;
632 UInt32 size = sizeof(audio_unit_latency_sec);
633 OSStatus result = AudioUnitGetProperty(audio_unit_,
634 kAudioUnitProperty_Latency,
635 kAudioUnitScope_Global,
637 &audio_unit_latency_sec,
638 &size);
639 OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
640 << "Could not get audio unit latency";
642 // Get input audio device latency.
643 AudioObjectPropertyAddress property_address = {
644 kAudioDevicePropertyLatency,
645 kAudioDevicePropertyScopeInput,
646 kAudioObjectPropertyElementMaster
648 UInt32 device_latency_frames = 0;
649 size = sizeof(device_latency_frames);
650 result = AudioObjectGetPropertyData(input_device_id_,
651 &property_address,
653 NULL,
654 &size,
655 &device_latency_frames);
656 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
658 return static_cast<double>((audio_unit_latency_sec *
659 format_.mSampleRate) + device_latency_frames);
662 double AUAudioInputStream::GetCaptureLatency(
663 const AudioTimeStamp* input_time_stamp) {
664 // Get the delay between between the actual recording instant and the time
665 // when the data packet is provided as a callback.
666 UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
667 input_time_stamp->mHostTime);
668 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
669 double delay_frames = static_cast<double>
670 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
672 // Total latency is composed by the dynamic latency and the fixed
673 // hardware latency.
674 return (delay_frames + hardware_latency_frames_);
677 int AUAudioInputStream::GetNumberOfChannelsFromStream() {
678 // Get the stream format, to be able to read the number of channels.
679 AudioObjectPropertyAddress property_address = {
680 kAudioDevicePropertyStreamFormat,
681 kAudioDevicePropertyScopeInput,
682 kAudioObjectPropertyElementMaster
684 AudioStreamBasicDescription stream_format;
685 UInt32 size = sizeof(stream_format);
686 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
687 &property_address,
689 NULL,
690 &size,
691 &stream_format);
692 if (result != noErr) {
693 DLOG(WARNING) << "Could not get stream format";
694 return 0;
697 return static_cast<int>(stream_format.mChannelsPerFrame);
700 void AUAudioInputStream::HandleError(OSStatus err) {
701 NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
702 << " (" << err << ")";
703 if (sink_)
704 sink_->OnError(this);
707 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
708 Boolean is_settable = false;
709 AudioObjectPropertyAddress property_address = {
710 kAudioDevicePropertyVolumeScalar,
711 kAudioDevicePropertyScopeInput,
712 static_cast<UInt32>(channel)
714 OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
715 &property_address,
716 &is_settable);
717 return (result == noErr) ? is_settable : false;
720 } // namespace media