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