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