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