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