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