audio_low_latency_input_mac.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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  number_of_frames_ = output_params.frames_per_buffer();
68  DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_;
69
70  // Derive size (in bytes) of the buffers that we will render to.
71  UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
72  DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size;
73
74  // Allocate AudioBuffers to be used as storage for the received audio.
75  // The AudioBufferList structure works as a placeholder for the
76  // AudioBuffer structure, which holds a pointer to the actual data buffer.
77  audio_data_buffer_.reset(new uint8[data_byte_size]);
78  audio_buffer_list_.mNumberBuffers = 1;
79
80  AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
81  audio_buffer->mNumberChannels = input_params.channels();
82  audio_buffer->mDataByteSize = data_byte_size;
83  audio_buffer->mData = audio_data_buffer_.get();
84
85  // Set up an internal FIFO buffer that will accumulate recorded audio frames
86  // until a requested size is ready to be sent to the client.
87  // It is not possible to ask for less than |kAudioFramesPerCallback| number of
88  // audio frames.
89  size_t requested_size_frames =
90      input_params.GetBytesPerBuffer() / format_.mBytesPerPacket;
91  if (requested_size_frames < number_of_frames_) {
92    // For devices that only support a low sample rate like 8kHz, we adjust the
93    // buffer size to match number_of_frames_.  The value of number_of_frames_
94    // in this case has not been calculated based on hardware settings but
95    // rather our hardcoded defaults (see ChooseBufferSize).
96    requested_size_frames = number_of_frames_;
97  }
98
99  requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame;
100  DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_;
101  DVLOG_IF(0, 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 input audio
234  // units in the current process.  As a result, the AURenderCallback must be
235  // able to handle arbitrary buffer sizes and FIFO appropriately.
236  UInt32 buffer_size = 0;
237  UInt32 property_size = sizeof(buffer_size);
238  result = AudioUnitGetProperty(audio_unit_,
239                                kAudioDevicePropertyBufferFrameSize,
240                                kAudioUnitScope_Output,
241                                1,
242                                &buffer_size,
243                                &property_size);
244  if (result != noErr) {
245    HandleError(result);
246    return false;
247  }
248
249  // Only set the buffer size if we're the only active stream or the buffer size
250  // is lower than the current buffer size.
251  if (manager_->input_stream_count() == 1 || number_of_frames_ < buffer_size) {
252    buffer_size = number_of_frames_;
253    result = AudioUnitSetProperty(audio_unit_,
254                                  kAudioDevicePropertyBufferFrameSize,
255                                  kAudioUnitScope_Output,
256                                  1,
257                                  &buffer_size,
258                                  sizeof(buffer_size));
259    if (result != noErr) {
260      HandleError(result);
261      return false;
262    }
263  }
264
265  // Finally, initialize the audio unit and ensure that it is ready to render.
266  // Allocates memory according to the maximum number of audio frames
267  // it can produce in response to a single render call.
268  result = AudioUnitInitialize(audio_unit_);
269  if (result) {
270    HandleError(result);
271    return false;
272  }
273
274  // The hardware latency is fixed and will not change during the call.
275  hardware_latency_frames_ = GetHardwareLatency();
276
277  // The master channel is 0, Left and right are channels 1 and 2.
278  // And the master channel is not counted in |number_of_channels_in_frame_|.
279  number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
280
281  return true;
282}
283
284void AUAudioInputStream::Start(AudioInputCallback* callback) {
285  DCHECK(callback);
286  DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
287  if (started_ || !audio_unit_)
288    return;
289
290  // Check if we should defer Start() for http://crbug.com/160920.
291  if (manager_->ShouldDeferStreamStart()) {
292    // Use a cancellable closure so that if Stop() is called before Start()
293    // actually runs, we can cancel the pending start.
294    deferred_start_cb_.Reset(base::Bind(
295        &AUAudioInputStream::Start, base::Unretained(this), callback));
296    manager_->GetTaskRunner()->PostDelayedTask(
297        FROM_HERE,
298        deferred_start_cb_.callback(),
299        base::TimeDelta::FromSeconds(
300            AudioManagerMac::kStartDelayInSecsForPowerEvents));
301    return;
302  }
303
304  sink_ = callback;
305  StartAgc();
306  OSStatus result = AudioOutputUnitStart(audio_unit_);
307  if (result == noErr) {
308    started_ = true;
309  }
310  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
311      << "Failed to start acquiring data";
312}
313
314void AUAudioInputStream::Stop() {
315  if (!started_)
316    return;
317  StopAgc();
318  OSStatus result = AudioOutputUnitStop(audio_unit_);
319  DCHECK_EQ(result, noErr);
320  started_ = false;
321  sink_ = NULL;
322
323  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
324      << "Failed to stop acquiring data";
325}
326
327void AUAudioInputStream::Close() {
328  // It is valid to call Close() before calling open or Start().
329  // It is also valid to call Close() after Start() has been called.
330  if (started_) {
331    Stop();
332  }
333  if (audio_unit_) {
334    // Deallocate the audio unit’s resources.
335    AudioUnitUninitialize(audio_unit_);
336
337    // Terminates our connection to the AUHAL component.
338    CloseComponent(audio_unit_);
339    audio_unit_ = 0;
340  }
341
342  // Inform the audio manager that we have been closed. This can cause our
343  // destruction.
344  manager_->ReleaseInputStream(this);
345}
346
347double AUAudioInputStream::GetMaxVolume() {
348  // Verify that we have a valid device.
349  if (input_device_id_ == kAudioObjectUnknown) {
350    NOTREACHED() << "Device ID is unknown";
351    return 0.0;
352  }
353
354  // Query if any of the master, left or right channels has volume control.
355  for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
356    // If the volume is settable, the  valid volume range is [0.0, 1.0].
357    if (IsVolumeSettableOnChannel(i))
358      return 1.0;
359  }
360
361  // Volume control is not available for the audio stream.
362  return 0.0;
363}
364
365void AUAudioInputStream::SetVolume(double volume) {
366  DVLOG(1) << "SetVolume(volume=" << volume << ")";
367  DCHECK_GE(volume, 0.0);
368  DCHECK_LE(volume, 1.0);
369
370  // Verify that we have a valid device.
371  if (input_device_id_ == kAudioObjectUnknown) {
372    NOTREACHED() << "Device ID is unknown";
373    return;
374  }
375
376  Float32 volume_float32 = static_cast<Float32>(volume);
377  AudioObjectPropertyAddress property_address = {
378    kAudioDevicePropertyVolumeScalar,
379    kAudioDevicePropertyScopeInput,
380    kAudioObjectPropertyElementMaster
381  };
382
383  // Try to set the volume for master volume channel.
384  if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
385    OSStatus result = AudioObjectSetPropertyData(input_device_id_,
386                                                 &property_address,
387                                                 0,
388                                                 NULL,
389                                                 sizeof(volume_float32),
390                                                 &volume_float32);
391    if (result != noErr) {
392      DLOG(WARNING) << "Failed to set volume to " << volume_float32;
393    }
394    return;
395  }
396
397  // There is no master volume control, try to set volume for each channel.
398  int successful_channels = 0;
399  for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
400    property_address.mElement = static_cast<UInt32>(i);
401    if (IsVolumeSettableOnChannel(i)) {
402      OSStatus result = AudioObjectSetPropertyData(input_device_id_,
403                                                   &property_address,
404                                                   0,
405                                                   NULL,
406                                                   sizeof(volume_float32),
407                                                   &volume_float32);
408      if (result == noErr)
409        ++successful_channels;
410    }
411  }
412
413  DLOG_IF(WARNING, successful_channels == 0)
414      << "Failed to set volume to " << volume_float32;
415
416  // Update the AGC volume level based on the last setting above. Note that,
417  // the volume-level resolution is not infinite and it is therefore not
418  // possible to assume that the volume provided as input parameter can be
419  // used directly. Instead, a new query to the audio hardware is required.
420  // This method does nothing if AGC is disabled.
421  UpdateAgcVolume();
422}
423
424double AUAudioInputStream::GetVolume() {
425  // Verify that we have a valid device.
426  if (input_device_id_ == kAudioObjectUnknown){
427    NOTREACHED() << "Device ID is unknown";
428    return 0.0;
429  }
430
431  AudioObjectPropertyAddress property_address = {
432    kAudioDevicePropertyVolumeScalar,
433    kAudioDevicePropertyScopeInput,
434    kAudioObjectPropertyElementMaster
435  };
436
437  if (AudioObjectHasProperty(input_device_id_, &property_address)) {
438    // The device supports master volume control, get the volume from the
439    // master channel.
440    Float32 volume_float32 = 0.0;
441    UInt32 size = sizeof(volume_float32);
442    OSStatus result = AudioObjectGetPropertyData(input_device_id_,
443                                                 &property_address,
444                                                 0,
445                                                 NULL,
446                                                 &size,
447                                                 &volume_float32);
448    if (result == noErr)
449      return static_cast<double>(volume_float32);
450  } else {
451    // There is no master volume control, try to get the average volume of
452    // all the channels.
453    Float32 volume_float32 = 0.0;
454    int successful_channels = 0;
455    for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
456      property_address.mElement = static_cast<UInt32>(i);
457      if (AudioObjectHasProperty(input_device_id_, &property_address)) {
458        Float32 channel_volume = 0;
459        UInt32 size = sizeof(channel_volume);
460        OSStatus result = AudioObjectGetPropertyData(input_device_id_,
461                                                     &property_address,
462                                                     0,
463                                                     NULL,
464                                                     &size,
465                                                     &channel_volume);
466        if (result == noErr) {
467          volume_float32 += channel_volume;
468          ++successful_channels;
469        }
470      }
471    }
472
473    // Get the average volume of the channels.
474    if (successful_channels != 0)
475      return static_cast<double>(volume_float32 / successful_channels);
476  }
477
478  DLOG(WARNING) << "Failed to get volume";
479  return 0.0;
480}
481
482// AUHAL AudioDeviceOutput unit callback
483OSStatus AUAudioInputStream::InputProc(void* user_data,
484                                       AudioUnitRenderActionFlags* flags,
485                                       const AudioTimeStamp* time_stamp,
486                                       UInt32 bus_number,
487                                       UInt32 number_of_frames,
488                                       AudioBufferList* io_data) {
489  // Verify that the correct bus is used (Input bus/Element 1)
490  DCHECK_EQ(bus_number, static_cast<UInt32>(1));
491  AUAudioInputStream* audio_input =
492      reinterpret_cast<AUAudioInputStream*>(user_data);
493  DCHECK(audio_input);
494  if (!audio_input)
495    return kAudioUnitErr_InvalidElement;
496
497  // Receive audio from the AUHAL from the output scope of the Audio Unit.
498  OSStatus result = AudioUnitRender(audio_input->audio_unit(),
499                                    flags,
500                                    time_stamp,
501                                    bus_number,
502                                    number_of_frames,
503                                    audio_input->audio_buffer_list());
504  if (result)
505    return result;
506
507  // Deliver recorded data to the consumer as a callback.
508  return audio_input->Provide(number_of_frames,
509                              audio_input->audio_buffer_list(),
510                              time_stamp);
511}
512
513OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
514                                     AudioBufferList* io_data,
515                                     const AudioTimeStamp* time_stamp) {
516  // Update the capture latency.
517  double capture_latency_frames = GetCaptureLatency(time_stamp);
518
519  // The AGC volume level is updated once every second on a separate thread.
520  // Note that, |volume| is also updated each time SetVolume() is called
521  // through IPC by the render-side AGC.
522  double normalized_volume = 0.0;
523  GetAgcVolume(&normalized_volume);
524
525  AudioBuffer& buffer = io_data->mBuffers[0];
526  uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
527  uint32 capture_delay_bytes = static_cast<uint32>
528      ((capture_latency_frames + 0.5) * format_.mBytesPerFrame);
529  // Account for the extra delay added by the FIFO.
530  capture_delay_bytes += fifo_delay_bytes_;
531  DCHECK(audio_data);
532  if (!audio_data)
533    return kAudioUnitErr_InvalidElement;
534
535  // Accumulate captured audio in FIFO until we can match the output size
536  // requested by the client.
537  fifo_->Append(audio_data, buffer.mDataByteSize);
538
539  // Deliver recorded data to the client as soon as the FIFO contains a
540  // sufficient amount.
541  if (fifo_->forward_bytes() >= requested_size_bytes_) {
542    // Read from FIFO into temporary data buffer.
543    fifo_->Read(data_->writable_data(), requested_size_bytes_);
544
545    // Deliver data packet, delay estimation and volume level to the user.
546    sink_->OnData(this,
547                  data_->data(),
548                  requested_size_bytes_,
549                  capture_delay_bytes,
550                  normalized_volume);
551  }
552
553  return noErr;
554}
555
556int AUAudioInputStream::HardwareSampleRate() {
557  // Determine the default input device's sample-rate.
558  AudioDeviceID device_id = kAudioObjectUnknown;
559  UInt32 info_size = sizeof(device_id);
560
561  AudioObjectPropertyAddress default_input_device_address = {
562    kAudioHardwarePropertyDefaultInputDevice,
563    kAudioObjectPropertyScopeGlobal,
564    kAudioObjectPropertyElementMaster
565  };
566  OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
567                                               &default_input_device_address,
568                                               0,
569                                               0,
570                                               &info_size,
571                                               &device_id);
572  if (result != noErr)
573    return 0.0;
574
575  Float64 nominal_sample_rate;
576  info_size = sizeof(nominal_sample_rate);
577
578  AudioObjectPropertyAddress nominal_sample_rate_address = {
579    kAudioDevicePropertyNominalSampleRate,
580    kAudioObjectPropertyScopeGlobal,
581    kAudioObjectPropertyElementMaster
582  };
583  result = AudioObjectGetPropertyData(device_id,
584                                      &nominal_sample_rate_address,
585                                      0,
586                                      0,
587                                      &info_size,
588                                      &nominal_sample_rate);
589  if (result != noErr)
590    return 0.0;
591
592  return static_cast<int>(nominal_sample_rate);
593}
594
595double AUAudioInputStream::GetHardwareLatency() {
596  if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) {
597    DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
598    return 0.0;
599  }
600
601  // Get audio unit latency.
602  Float64 audio_unit_latency_sec = 0.0;
603  UInt32 size = sizeof(audio_unit_latency_sec);
604  OSStatus result = AudioUnitGetProperty(audio_unit_,
605                                         kAudioUnitProperty_Latency,
606                                         kAudioUnitScope_Global,
607                                         0,
608                                         &audio_unit_latency_sec,
609                                         &size);
610  OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
611      << "Could not get audio unit latency";
612
613  // Get input audio device latency.
614  AudioObjectPropertyAddress property_address = {
615    kAudioDevicePropertyLatency,
616    kAudioDevicePropertyScopeInput,
617    kAudioObjectPropertyElementMaster
618  };
619  UInt32 device_latency_frames = 0;
620  size = sizeof(device_latency_frames);
621  result = AudioObjectGetPropertyData(input_device_id_,
622                                      &property_address,
623                                      0,
624                                      NULL,
625                                      &size,
626                                      &device_latency_frames);
627  DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
628
629  return static_cast<double>((audio_unit_latency_sec *
630      format_.mSampleRate) + device_latency_frames);
631}
632
633double AUAudioInputStream::GetCaptureLatency(
634    const AudioTimeStamp* input_time_stamp) {
635  // Get the delay between between the actual recording instant and the time
636  // when the data packet is provided as a callback.
637  UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
638      input_time_stamp->mHostTime);
639  UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
640  double delay_frames = static_cast<double>
641      (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
642
643  // Total latency is composed by the dynamic latency and the fixed
644  // hardware latency.
645  return (delay_frames + hardware_latency_frames_);
646}
647
648int AUAudioInputStream::GetNumberOfChannelsFromStream() {
649  // Get the stream format, to be able to read the number of channels.
650  AudioObjectPropertyAddress property_address = {
651    kAudioDevicePropertyStreamFormat,
652    kAudioDevicePropertyScopeInput,
653    kAudioObjectPropertyElementMaster
654  };
655  AudioStreamBasicDescription stream_format;
656  UInt32 size = sizeof(stream_format);
657  OSStatus result = AudioObjectGetPropertyData(input_device_id_,
658                                               &property_address,
659                                               0,
660                                               NULL,
661                                               &size,
662                                               &stream_format);
663  if (result != noErr) {
664    DLOG(WARNING) << "Could not get stream format";
665    return 0;
666  }
667
668  return static_cast<int>(stream_format.mChannelsPerFrame);
669}
670
671void AUAudioInputStream::HandleError(OSStatus err) {
672  NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
673               << " (" << err << ")";
674  if (sink_)
675    sink_->OnError(this);
676}
677
678bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
679  Boolean is_settable = false;
680  AudioObjectPropertyAddress property_address = {
681    kAudioDevicePropertyVolumeScalar,
682    kAudioDevicePropertyScopeInput,
683    static_cast<UInt32>(channel)
684  };
685  OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
686                                                  &property_address,
687                                                  &is_settable);
688  return (result == noErr) ? is_settable : false;
689}
690
691}  // namespace media
692