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
operator <<(std::ostream & os,const AudioStreamBasicDescription & format)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
AUAudioInputStream(AudioManagerMac * manager,const AudioParameters & input_params,const AudioParameters & output_params,AudioDeviceID audio_device_id)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 DVLOG_IF(0, 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
~AUAudioInputStream()121 AUAudioInputStream::~AUAudioInputStream() {}
122
123 // Obtain and open the AUHAL AudioOutputUnit for recording.
Open()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
Start(AudioInputCallback * callback)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
Stop()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
Close()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
GetMaxVolume()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
SetVolume(double volume)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
GetVolume()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
InputProc(void * user_data,AudioUnitRenderActionFlags * flags,const AudioTimeStamp * time_stamp,UInt32 bus_number,UInt32 number_of_frames,AudioBufferList * io_data)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
Provide(UInt32 number_of_frames,AudioBufferList * io_data,const AudioTimeStamp * time_stamp)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
HardwareSampleRate()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
GetHardwareLatency()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
GetCaptureLatency(const AudioTimeStamp * input_time_stamp)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
GetNumberOfChannelsFromStream()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
HandleError(OSStatus err)647 void AUAudioInputStream::HandleError(OSStatus err) {
648 NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
649 << " (" << err << ")";
650 if (sink_)
651 sink_->OnError(this);
652 }
653
IsVolumeSettableOnChannel(int channel)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
668