- add sources.
[platform/framework/web/crosswalk.git] / src / media / audio / mac / audio_low_latency_input_mac.cc
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
15 namespace media {
16
17 static 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
34 AUAudioInputStream::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
121 AUAudioInputStream::~AUAudioInputStream() {}
122
123 // Obtain and open the AUHAL AudioOutputUnit for recording.
124 bool 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
272 void 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
287 void 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
299 void 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
323 double 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
341 void 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
400 double 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
459 OSStatus 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
489 OSStatus 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
532 int 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
571 double 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
609 double 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
624 int 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
647 void AUAudioInputStream::HandleError(OSStatus err) {
648   NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
649                << " (" << err << ")";
650   if (sink_)
651     sink_->OnError(this);
652 }
653
654 bool 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