Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / media / audio / mac / audio_low_latency_input_mac.cc
blob4d8de904654aef911933ade5629d520e871b7def
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 static std::ostream& operator<<(std::ostream& os,
23 const AudioStreamBasicDescription& format) {
24 os << "sample rate : " << format.mSampleRate << std::endl
25 << "format ID : " << format.mFormatID << std::endl
26 << "format flags : " << format.mFormatFlags << std::endl
27 << "bytes per packet : " << format.mBytesPerPacket << std::endl
28 << "frames per packet : " << format.mFramesPerPacket << std::endl
29 << "bytes per frame : " << format.mBytesPerFrame << std::endl
30 << "channels per frame: " << format.mChannelsPerFrame << std::endl
31 << "bits per channel : " << format.mBitsPerChannel;
32 return os;
35 // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit"
36 // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
37 // for more details and background regarding this implementation.
39 AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager,
40 const AudioParameters& input_params,
41 AudioDeviceID audio_device_id)
42 : manager_(manager),
43 number_of_frames_(input_params.frames_per_buffer()),
44 sink_(NULL),
45 audio_unit_(0),
46 input_device_id_(audio_device_id),
47 started_(false),
48 hardware_latency_frames_(0),
49 number_of_channels_in_frame_(0),
50 fifo_(input_params.channels(),
51 number_of_frames_,
52 kNumberOfBlocksBufferInFifo) {
53 DCHECK(manager_);
55 // Set up the desired (output) format specified by the client.
56 format_.mSampleRate = input_params.sample_rate();
57 format_.mFormatID = kAudioFormatLinearPCM;
58 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
59 kLinearPCMFormatFlagIsSignedInteger;
60 format_.mBitsPerChannel = input_params.bits_per_sample();
61 format_.mChannelsPerFrame = input_params.channels();
62 format_.mFramesPerPacket = 1; // uncompressed audio
63 format_.mBytesPerPacket = (format_.mBitsPerChannel *
64 input_params.channels()) / 8;
65 format_.mBytesPerFrame = format_.mBytesPerPacket;
66 format_.mReserved = 0;
68 DVLOG(1) << "Desired ouput format: " << format_;
70 // Derive size (in bytes) of the buffers that we will render to.
71 UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
72 DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size;
74 // Allocate AudioBuffers to be used as storage for the received audio.
75 // The AudioBufferList structure works as a placeholder for the
76 // AudioBuffer structure, which holds a pointer to the actual data buffer.
77 audio_data_buffer_.reset(new uint8[data_byte_size]);
78 audio_buffer_list_.mNumberBuffers = 1;
80 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
81 audio_buffer->mNumberChannels = input_params.channels();
82 audio_buffer->mDataByteSize = data_byte_size;
83 audio_buffer->mData = audio_data_buffer_.get();
86 AUAudioInputStream::~AUAudioInputStream() {}
88 // Obtain and open the AUHAL AudioOutputUnit for recording.
89 bool AUAudioInputStream::Open() {
90 // Verify that we are not already opened.
91 if (audio_unit_)
92 return false;
94 // Verify that we have a valid device.
95 if (input_device_id_ == kAudioObjectUnknown) {
96 NOTREACHED() << "Device ID is unknown";
97 return false;
100 // Start by obtaining an AudioOuputUnit using an AUHAL component description.
102 // Description for the Audio Unit we want to use (AUHAL in this case).
103 AudioComponentDescription desc = {
104 kAudioUnitType_Output,
105 kAudioUnitSubType_HALOutput,
106 kAudioUnitManufacturer_Apple,
111 AudioComponent comp = AudioComponentFindNext(0, &desc);
112 DCHECK(comp);
114 // Get access to the service provided by the specified Audio Unit.
115 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit_);
116 if (result) {
117 HandleError(result);
118 return false;
121 // Enable IO on the input scope of the Audio Unit.
123 // After creating the AUHAL object, we must enable IO on the input scope
124 // of the Audio Unit to obtain the device input. Input must be explicitly
125 // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1
126 // of the AUHAL. Beacause the AUHAL can be used for both input and output,
127 // we must also disable IO on the output scope.
129 UInt32 enableIO = 1;
131 // Enable input on the AUHAL.
132 result = AudioUnitSetProperty(audio_unit_,
133 kAudioOutputUnitProperty_EnableIO,
134 kAudioUnitScope_Input,
135 1, // input element 1
136 &enableIO, // enable
137 sizeof(enableIO));
138 if (result) {
139 HandleError(result);
140 return false;
143 // Disable output on the AUHAL.
144 enableIO = 0;
145 result = AudioUnitSetProperty(audio_unit_,
146 kAudioOutputUnitProperty_EnableIO,
147 kAudioUnitScope_Output,
148 0, // output element 0
149 &enableIO, // disable
150 sizeof(enableIO));
151 if (result) {
152 HandleError(result);
153 return false;
156 // Next, set the audio device to be the Audio Unit's current device.
157 // Note that, devices can only be set to the AUHAL after enabling IO.
158 result = AudioUnitSetProperty(audio_unit_,
159 kAudioOutputUnitProperty_CurrentDevice,
160 kAudioUnitScope_Global,
162 &input_device_id_,
163 sizeof(input_device_id_));
164 if (result) {
165 HandleError(result);
166 return false;
169 // Set up the the desired (output) format.
170 // For obtaining input from a device, the device format is always expressed
171 // on the output scope of the AUHAL's Element 1.
172 result = AudioUnitSetProperty(audio_unit_,
173 kAudioUnitProperty_StreamFormat,
174 kAudioUnitScope_Output,
176 &format_,
177 sizeof(format_));
178 if (result) {
179 HandleError(result);
180 return false;
183 if (!manager_->MaybeChangeBufferSize(
184 input_device_id_, audio_unit_, 1, number_of_frames_))
185 return false;
187 // Register the input procedure for the AUHAL.
188 // This procedure will be called when the AUHAL has received new data
189 // from the input device.
190 AURenderCallbackStruct callback;
191 callback.inputProc = InputProc;
192 callback.inputProcRefCon = this;
193 result = AudioUnitSetProperty(audio_unit_,
194 kAudioOutputUnitProperty_SetInputCallback,
195 kAudioUnitScope_Global,
197 &callback,
198 sizeof(callback));
199 if (result) {
200 HandleError(result);
201 return false;
204 // Finally, initialize the audio unit and ensure that it is ready to render.
205 // Allocates memory according to the maximum number of audio frames
206 // it can produce in response to a single render call.
207 result = AudioUnitInitialize(audio_unit_);
208 if (result) {
209 HandleError(result);
210 return false;
213 // The hardware latency is fixed and will not change during the call.
214 hardware_latency_frames_ = GetHardwareLatency();
216 // The master channel is 0, Left and right are channels 1 and 2.
217 // And the master channel is not counted in |number_of_channels_in_frame_|.
218 number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
220 return true;
223 void AUAudioInputStream::Start(AudioInputCallback* callback) {
224 DCHECK(callback);
225 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
226 if (started_ || !audio_unit_)
227 return;
229 // Check if we should defer Start() for http://crbug.com/160920.
230 if (manager_->ShouldDeferStreamStart()) {
231 // Use a cancellable closure so that if Stop() is called before Start()
232 // actually runs, we can cancel the pending start.
233 deferred_start_cb_.Reset(base::Bind(
234 &AUAudioInputStream::Start, base::Unretained(this), callback));
235 manager_->GetTaskRunner()->PostDelayedTask(
236 FROM_HERE,
237 deferred_start_cb_.callback(),
238 base::TimeDelta::FromSeconds(
239 AudioManagerMac::kStartDelayInSecsForPowerEvents));
240 return;
243 sink_ = callback;
244 StartAgc();
245 OSStatus result = AudioOutputUnitStart(audio_unit_);
246 if (result == noErr) {
247 started_ = true;
249 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
250 << "Failed to start acquiring data";
253 void AUAudioInputStream::Stop() {
254 if (!started_)
255 return;
256 StopAgc();
257 OSStatus result = AudioOutputUnitStop(audio_unit_);
258 DCHECK_EQ(result, noErr);
259 started_ = false;
260 sink_ = NULL;
261 fifo_.Clear();
263 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
264 << "Failed to stop acquiring data";
267 void AUAudioInputStream::Close() {
268 // It is valid to call Close() before calling open or Start().
269 // It is also valid to call Close() after Start() has been called.
270 if (started_) {
271 Stop();
273 if (audio_unit_) {
274 // Deallocate the audio unit’s resources.
275 OSStatus result = AudioUnitUninitialize(audio_unit_);
276 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
277 << "AudioUnitUninitialize() failed.";
279 result = AudioComponentInstanceDispose(audio_unit_);
280 OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
281 << "AudioComponentInstanceDispose() failed.";
283 audio_unit_ = 0;
286 // Inform the audio manager that we have been closed. This can cause our
287 // destruction.
288 manager_->ReleaseInputStream(this);
291 double AUAudioInputStream::GetMaxVolume() {
292 // Verify that we have a valid device.
293 if (input_device_id_ == kAudioObjectUnknown) {
294 NOTREACHED() << "Device ID is unknown";
295 return 0.0;
298 // Query if any of the master, left or right channels has volume control.
299 for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
300 // If the volume is settable, the valid volume range is [0.0, 1.0].
301 if (IsVolumeSettableOnChannel(i))
302 return 1.0;
305 // Volume control is not available for the audio stream.
306 return 0.0;
309 void AUAudioInputStream::SetVolume(double volume) {
310 DVLOG(1) << "SetVolume(volume=" << volume << ")";
311 DCHECK_GE(volume, 0.0);
312 DCHECK_LE(volume, 1.0);
314 // Verify that we have a valid device.
315 if (input_device_id_ == kAudioObjectUnknown) {
316 NOTREACHED() << "Device ID is unknown";
317 return;
320 Float32 volume_float32 = static_cast<Float32>(volume);
321 AudioObjectPropertyAddress property_address = {
322 kAudioDevicePropertyVolumeScalar,
323 kAudioDevicePropertyScopeInput,
324 kAudioObjectPropertyElementMaster
327 // Try to set the volume for master volume channel.
328 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
329 OSStatus result = AudioObjectSetPropertyData(input_device_id_,
330 &property_address,
332 NULL,
333 sizeof(volume_float32),
334 &volume_float32);
335 if (result != noErr) {
336 DLOG(WARNING) << "Failed to set volume to " << volume_float32;
338 return;
341 // There is no master volume control, try to set volume for each channel.
342 int successful_channels = 0;
343 for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
344 property_address.mElement = static_cast<UInt32>(i);
345 if (IsVolumeSettableOnChannel(i)) {
346 OSStatus result = AudioObjectSetPropertyData(input_device_id_,
347 &property_address,
349 NULL,
350 sizeof(volume_float32),
351 &volume_float32);
352 if (result == noErr)
353 ++successful_channels;
357 DLOG_IF(WARNING, successful_channels == 0)
358 << "Failed to set volume to " << volume_float32;
360 // Update the AGC volume level based on the last setting above. Note that,
361 // the volume-level resolution is not infinite and it is therefore not
362 // possible to assume that the volume provided as input parameter can be
363 // used directly. Instead, a new query to the audio hardware is required.
364 // This method does nothing if AGC is disabled.
365 UpdateAgcVolume();
368 double AUAudioInputStream::GetVolume() {
369 // Verify that we have a valid device.
370 if (input_device_id_ == kAudioObjectUnknown){
371 NOTREACHED() << "Device ID is unknown";
372 return 0.0;
375 AudioObjectPropertyAddress property_address = {
376 kAudioDevicePropertyVolumeScalar,
377 kAudioDevicePropertyScopeInput,
378 kAudioObjectPropertyElementMaster
381 if (AudioObjectHasProperty(input_device_id_, &property_address)) {
382 // The device supports master volume control, get the volume from the
383 // master channel.
384 Float32 volume_float32 = 0.0;
385 UInt32 size = sizeof(volume_float32);
386 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
387 &property_address,
389 NULL,
390 &size,
391 &volume_float32);
392 if (result == noErr)
393 return static_cast<double>(volume_float32);
394 } else {
395 // There is no master volume control, try to get the average volume of
396 // all the channels.
397 Float32 volume_float32 = 0.0;
398 int successful_channels = 0;
399 for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
400 property_address.mElement = static_cast<UInt32>(i);
401 if (AudioObjectHasProperty(input_device_id_, &property_address)) {
402 Float32 channel_volume = 0;
403 UInt32 size = sizeof(channel_volume);
404 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
405 &property_address,
407 NULL,
408 &size,
409 &channel_volume);
410 if (result == noErr) {
411 volume_float32 += channel_volume;
412 ++successful_channels;
417 // Get the average volume of the channels.
418 if (successful_channels != 0)
419 return static_cast<double>(volume_float32 / successful_channels);
422 DLOG(WARNING) << "Failed to get volume";
423 return 0.0;
426 bool AUAudioInputStream::IsMuted() {
427 // Verify that we have a valid device.
428 DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown";
430 AudioObjectPropertyAddress property_address = {
431 kAudioDevicePropertyMute,
432 kAudioDevicePropertyScopeInput,
433 kAudioObjectPropertyElementMaster
436 if (!AudioObjectHasProperty(input_device_id_, &property_address)) {
437 DLOG(ERROR) << "Device does not support checking master mute state";
438 return false;
441 UInt32 muted = 0;
442 UInt32 size = sizeof(muted);
443 OSStatus result = AudioObjectGetPropertyData(
444 input_device_id_, &property_address, 0, NULL, &size, &muted);
445 DLOG_IF(WARNING, result != noErr) << "Failed to get mute state";
446 return result == noErr && muted != 0;
449 // AUHAL AudioDeviceOutput unit callback
450 OSStatus AUAudioInputStream::InputProc(void* user_data,
451 AudioUnitRenderActionFlags* flags,
452 const AudioTimeStamp* time_stamp,
453 UInt32 bus_number,
454 UInt32 number_of_frames,
455 AudioBufferList* io_data) {
456 // Verify that the correct bus is used (Input bus/Element 1)
457 DCHECK_EQ(bus_number, static_cast<UInt32>(1));
458 AUAudioInputStream* audio_input =
459 reinterpret_cast<AUAudioInputStream*>(user_data);
460 DCHECK(audio_input);
461 if (!audio_input)
462 return kAudioUnitErr_InvalidElement;
464 // Update the |mDataByteSize| value in the audio_buffer_list() since
465 // |number_of_frames| can be changed on the fly.
466 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|,
467 // otherwise it will put CoreAudio into bad state and results in
468 // AudioUnitRender() returning -50 for the new created stream.
469 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and
470 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes.
471 // See crbug/428706 for details.
472 UInt32 new_size = number_of_frames * audio_input->format_.mBytesPerFrame;
473 AudioBuffer* audio_buffer = audio_input->audio_buffer_list()->mBuffers;
474 if (new_size != audio_buffer->mDataByteSize) {
475 if (new_size > audio_buffer->mDataByteSize) {
476 // This can happen if the device is unpluged during recording. We
477 // allocate enough memory here to avoid depending on how CoreAudio
478 // handles it.
479 // See See http://www.crbug.com/434681 for one example when we can enter
480 // this scope.
481 audio_input->audio_data_buffer_.reset(new uint8[new_size]);
482 audio_buffer->mData = audio_input->audio_data_buffer_.get();
485 // Update the |mDataByteSize| to match |number_of_frames|.
486 audio_buffer->mDataByteSize = new_size;
489 // Receive audio from the AUHAL from the output scope of the Audio Unit.
490 OSStatus result = AudioUnitRender(audio_input->audio_unit(),
491 flags,
492 time_stamp,
493 bus_number,
494 number_of_frames,
495 audio_input->audio_buffer_list());
496 if (result) {
497 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.AudioInputCbErrorMac", result);
498 OSSTATUS_DLOG(ERROR, result) << "AudioUnitRender() failed ";
499 audio_input->HandleError(result);
500 return result;
503 // Deliver recorded data to the consumer as a callback.
504 return audio_input->Provide(number_of_frames,
505 audio_input->audio_buffer_list(),
506 time_stamp);
509 OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
510 AudioBufferList* io_data,
511 const AudioTimeStamp* time_stamp) {
512 // Update the capture latency.
513 double capture_latency_frames = GetCaptureLatency(time_stamp);
515 // The AGC volume level is updated once every second on a separate thread.
516 // Note that, |volume| is also updated each time SetVolume() is called
517 // through IPC by the render-side AGC.
518 double normalized_volume = 0.0;
519 GetAgcVolume(&normalized_volume);
521 AudioBuffer& buffer = io_data->mBuffers[0];
522 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
523 uint32 capture_delay_bytes = static_cast<uint32>
524 ((capture_latency_frames + 0.5) * format_.mBytesPerFrame);
525 DCHECK(audio_data);
526 if (!audio_data)
527 return kAudioUnitErr_InvalidElement;
529 // Dynamically increase capacity of the FIFO to handle larger buffers from
530 // CoreAudio. This can happen in combination with Apple Thunderbolt Displays
531 // when the Display Audio is used as capture source and the cable is first
532 // remove and then inserted again.
533 // See http://www.crbug.com/434681 for details.
534 if (static_cast<int>(number_of_frames) > fifo_.GetUnfilledFrames()) {
535 // Derive required increase in number of FIFO blocks. The increase is
536 // typically one block.
537 const int blocks =
538 static_cast<int>((number_of_frames - fifo_.GetUnfilledFrames()) /
539 number_of_frames_) + 1;
540 DLOG(WARNING) << "Increasing FIFO capacity by " << blocks << " blocks";
541 fifo_.IncreaseCapacity(blocks);
544 // Copy captured (and interleaved) data into FIFO.
545 fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8);
547 // Consume and deliver the data when the FIFO has a block of available data.
548 while (fifo_.available_blocks()) {
549 const AudioBus* audio_bus = fifo_.Consume();
550 DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_));
552 // Compensate the audio delay caused by the FIFO.
553 capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame;
554 sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume);
557 return noErr;
560 int AUAudioInputStream::HardwareSampleRate() {
561 // Determine the default input device's sample-rate.
562 AudioDeviceID device_id = kAudioObjectUnknown;
563 UInt32 info_size = sizeof(device_id);
565 AudioObjectPropertyAddress default_input_device_address = {
566 kAudioHardwarePropertyDefaultInputDevice,
567 kAudioObjectPropertyScopeGlobal,
568 kAudioObjectPropertyElementMaster
570 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
571 &default_input_device_address,
574 &info_size,
575 &device_id);
576 if (result != noErr)
577 return 0.0;
579 Float64 nominal_sample_rate;
580 info_size = sizeof(nominal_sample_rate);
582 AudioObjectPropertyAddress nominal_sample_rate_address = {
583 kAudioDevicePropertyNominalSampleRate,
584 kAudioObjectPropertyScopeGlobal,
585 kAudioObjectPropertyElementMaster
587 result = AudioObjectGetPropertyData(device_id,
588 &nominal_sample_rate_address,
591 &info_size,
592 &nominal_sample_rate);
593 if (result != noErr)
594 return 0.0;
596 return static_cast<int>(nominal_sample_rate);
599 double AUAudioInputStream::GetHardwareLatency() {
600 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) {
601 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
602 return 0.0;
605 // Get audio unit latency.
606 Float64 audio_unit_latency_sec = 0.0;
607 UInt32 size = sizeof(audio_unit_latency_sec);
608 OSStatus result = AudioUnitGetProperty(audio_unit_,
609 kAudioUnitProperty_Latency,
610 kAudioUnitScope_Global,
612 &audio_unit_latency_sec,
613 &size);
614 OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
615 << "Could not get audio unit latency";
617 // Get input audio device latency.
618 AudioObjectPropertyAddress property_address = {
619 kAudioDevicePropertyLatency,
620 kAudioDevicePropertyScopeInput,
621 kAudioObjectPropertyElementMaster
623 UInt32 device_latency_frames = 0;
624 size = sizeof(device_latency_frames);
625 result = AudioObjectGetPropertyData(input_device_id_,
626 &property_address,
628 NULL,
629 &size,
630 &device_latency_frames);
631 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
633 return static_cast<double>((audio_unit_latency_sec *
634 format_.mSampleRate) + device_latency_frames);
637 double AUAudioInputStream::GetCaptureLatency(
638 const AudioTimeStamp* input_time_stamp) {
639 // Get the delay between between the actual recording instant and the time
640 // when the data packet is provided as a callback.
641 UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
642 input_time_stamp->mHostTime);
643 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
644 double delay_frames = static_cast<double>
645 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
647 // Total latency is composed by the dynamic latency and the fixed
648 // hardware latency.
649 return (delay_frames + hardware_latency_frames_);
652 int AUAudioInputStream::GetNumberOfChannelsFromStream() {
653 // Get the stream format, to be able to read the number of channels.
654 AudioObjectPropertyAddress property_address = {
655 kAudioDevicePropertyStreamFormat,
656 kAudioDevicePropertyScopeInput,
657 kAudioObjectPropertyElementMaster
659 AudioStreamBasicDescription stream_format;
660 UInt32 size = sizeof(stream_format);
661 OSStatus result = AudioObjectGetPropertyData(input_device_id_,
662 &property_address,
664 NULL,
665 &size,
666 &stream_format);
667 if (result != noErr) {
668 DLOG(WARNING) << "Could not get stream format";
669 return 0;
672 return static_cast<int>(stream_format.mChannelsPerFrame);
675 void AUAudioInputStream::HandleError(OSStatus err) {
676 NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
677 << " (" << err << ")";
678 if (sink_)
679 sink_->OnError(this);
682 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
683 Boolean is_settable = false;
684 AudioObjectPropertyAddress property_address = {
685 kAudioDevicePropertyVolumeScalar,
686 kAudioDevicePropertyScopeInput,
687 static_cast<UInt32>(channel)
689 OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
690 &property_address,
691 &is_settable);
692 return (result == noErr) ? is_settable : false;
695 } // namespace media