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