audio_low_latency_input_mac.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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 DCHECK(deferred_start_cb_.IsCancelled()); 295 deferred_start_cb_.Reset(base::Bind( 296 &AUAudioInputStream::Start, base::Unretained(this), callback)); 297 manager_->GetTaskRunner()->PostDelayedTask( 298 FROM_HERE, 299 deferred_start_cb_.callback(), 300 base::TimeDelta::FromSeconds( 301 AudioManagerMac::kStartDelayInSecsForPowerEvents)); 302 return; 303 } 304 305 sink_ = callback; 306 StartAgc(); 307 OSStatus result = AudioOutputUnitStart(audio_unit_); 308 if (result == noErr) { 309 started_ = true; 310 } 311 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 312 << "Failed to start acquiring data"; 313} 314 315void AUAudioInputStream::Stop() { 316 if (!started_) 317 return; 318 StopAgc(); 319 OSStatus result = AudioOutputUnitStop(audio_unit_); 320 DCHECK_EQ(result, noErr); 321 started_ = false; 322 sink_ = NULL; 323 324 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) 325 << "Failed to stop acquiring data"; 326} 327 328void AUAudioInputStream::Close() { 329 // It is valid to call Close() before calling open or Start(). 330 // It is also valid to call Close() after Start() has been called. 331 if (started_) { 332 Stop(); 333 } 334 if (audio_unit_) { 335 // Deallocate the audio unit’s resources. 336 AudioUnitUninitialize(audio_unit_); 337 338 // Terminates our connection to the AUHAL component. 339 CloseComponent(audio_unit_); 340 audio_unit_ = 0; 341 } 342 343 // Inform the audio manager that we have been closed. This can cause our 344 // destruction. 345 manager_->ReleaseInputStream(this); 346} 347 348double AUAudioInputStream::GetMaxVolume() { 349 // Verify that we have a valid device. 350 if (input_device_id_ == kAudioObjectUnknown) { 351 NOTREACHED() << "Device ID is unknown"; 352 return 0.0; 353 } 354 355 // Query if any of the master, left or right channels has volume control. 356 for (int i = 0; i <= number_of_channels_in_frame_; ++i) { 357 // If the volume is settable, the valid volume range is [0.0, 1.0]. 358 if (IsVolumeSettableOnChannel(i)) 359 return 1.0; 360 } 361 362 // Volume control is not available for the audio stream. 363 return 0.0; 364} 365 366void AUAudioInputStream::SetVolume(double volume) { 367 DVLOG(1) << "SetVolume(volume=" << volume << ")"; 368 DCHECK_GE(volume, 0.0); 369 DCHECK_LE(volume, 1.0); 370 371 // Verify that we have a valid device. 372 if (input_device_id_ == kAudioObjectUnknown) { 373 NOTREACHED() << "Device ID is unknown"; 374 return; 375 } 376 377 Float32 volume_float32 = static_cast<Float32>(volume); 378 AudioObjectPropertyAddress property_address = { 379 kAudioDevicePropertyVolumeScalar, 380 kAudioDevicePropertyScopeInput, 381 kAudioObjectPropertyElementMaster 382 }; 383 384 // Try to set the volume for master volume channel. 385 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) { 386 OSStatus result = AudioObjectSetPropertyData(input_device_id_, 387 &property_address, 388 0, 389 NULL, 390 sizeof(volume_float32), 391 &volume_float32); 392 if (result != noErr) { 393 DLOG(WARNING) << "Failed to set volume to " << volume_float32; 394 } 395 return; 396 } 397 398 // There is no master volume control, try to set volume for each channel. 399 int successful_channels = 0; 400 for (int i = 1; i <= number_of_channels_in_frame_; ++i) { 401 property_address.mElement = static_cast<UInt32>(i); 402 if (IsVolumeSettableOnChannel(i)) { 403 OSStatus result = AudioObjectSetPropertyData(input_device_id_, 404 &property_address, 405 0, 406 NULL, 407 sizeof(volume_float32), 408 &volume_float32); 409 if (result == noErr) 410 ++successful_channels; 411 } 412 } 413 414 DLOG_IF(WARNING, successful_channels == 0) 415 << "Failed to set volume to " << volume_float32; 416 417 // Update the AGC volume level based on the last setting above. Note that, 418 // the volume-level resolution is not infinite and it is therefore not 419 // possible to assume that the volume provided as input parameter can be 420 // used directly. Instead, a new query to the audio hardware is required. 421 // This method does nothing if AGC is disabled. 422 UpdateAgcVolume(); 423} 424 425double AUAudioInputStream::GetVolume() { 426 // Verify that we have a valid device. 427 if (input_device_id_ == kAudioObjectUnknown){ 428 NOTREACHED() << "Device ID is unknown"; 429 return 0.0; 430 } 431 432 AudioObjectPropertyAddress property_address = { 433 kAudioDevicePropertyVolumeScalar, 434 kAudioDevicePropertyScopeInput, 435 kAudioObjectPropertyElementMaster 436 }; 437 438 if (AudioObjectHasProperty(input_device_id_, &property_address)) { 439 // The device supports master volume control, get the volume from the 440 // master channel. 441 Float32 volume_float32 = 0.0; 442 UInt32 size = sizeof(volume_float32); 443 OSStatus result = AudioObjectGetPropertyData(input_device_id_, 444 &property_address, 445 0, 446 NULL, 447 &size, 448 &volume_float32); 449 if (result == noErr) 450 return static_cast<double>(volume_float32); 451 } else { 452 // There is no master volume control, try to get the average volume of 453 // all the channels. 454 Float32 volume_float32 = 0.0; 455 int successful_channels = 0; 456 for (int i = 1; i <= number_of_channels_in_frame_; ++i) { 457 property_address.mElement = static_cast<UInt32>(i); 458 if (AudioObjectHasProperty(input_device_id_, &property_address)) { 459 Float32 channel_volume = 0; 460 UInt32 size = sizeof(channel_volume); 461 OSStatus result = AudioObjectGetPropertyData(input_device_id_, 462 &property_address, 463 0, 464 NULL, 465 &size, 466 &channel_volume); 467 if (result == noErr) { 468 volume_float32 += channel_volume; 469 ++successful_channels; 470 } 471 } 472 } 473 474 // Get the average volume of the channels. 475 if (successful_channels != 0) 476 return static_cast<double>(volume_float32 / successful_channels); 477 } 478 479 DLOG(WARNING) << "Failed to get volume"; 480 return 0.0; 481} 482 483// AUHAL AudioDeviceOutput unit callback 484OSStatus AUAudioInputStream::InputProc(void* user_data, 485 AudioUnitRenderActionFlags* flags, 486 const AudioTimeStamp* time_stamp, 487 UInt32 bus_number, 488 UInt32 number_of_frames, 489 AudioBufferList* io_data) { 490 // Verify that the correct bus is used (Input bus/Element 1) 491 DCHECK_EQ(bus_number, static_cast<UInt32>(1)); 492 AUAudioInputStream* audio_input = 493 reinterpret_cast<AUAudioInputStream*>(user_data); 494 DCHECK(audio_input); 495 if (!audio_input) 496 return kAudioUnitErr_InvalidElement; 497 498 // Receive audio from the AUHAL from the output scope of the Audio Unit. 499 OSStatus result = AudioUnitRender(audio_input->audio_unit(), 500 flags, 501 time_stamp, 502 bus_number, 503 number_of_frames, 504 audio_input->audio_buffer_list()); 505 if (result) 506 return result; 507 508 // Deliver recorded data to the consumer as a callback. 509 return audio_input->Provide(number_of_frames, 510 audio_input->audio_buffer_list(), 511 time_stamp); 512} 513 514OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, 515 AudioBufferList* io_data, 516 const AudioTimeStamp* time_stamp) { 517 // Update the capture latency. 518 double capture_latency_frames = GetCaptureLatency(time_stamp); 519 520 // The AGC volume level is updated once every second on a separate thread. 521 // Note that, |volume| is also updated each time SetVolume() is called 522 // through IPC by the render-side AGC. 523 double normalized_volume = 0.0; 524 GetAgcVolume(&normalized_volume); 525 526 AudioBuffer& buffer = io_data->mBuffers[0]; 527 uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); 528 uint32 capture_delay_bytes = static_cast<uint32> 529 ((capture_latency_frames + 0.5) * format_.mBytesPerFrame); 530 // Account for the extra delay added by the FIFO. 531 capture_delay_bytes += fifo_delay_bytes_; 532 DCHECK(audio_data); 533 if (!audio_data) 534 return kAudioUnitErr_InvalidElement; 535 536 // Accumulate captured audio in FIFO until we can match the output size 537 // requested by the client. 538 fifo_->Append(audio_data, buffer.mDataByteSize); 539 540 // Deliver recorded data to the client as soon as the FIFO contains a 541 // sufficient amount. 542 if (fifo_->forward_bytes() >= requested_size_bytes_) { 543 // Read from FIFO into temporary data buffer. 544 fifo_->Read(data_->writable_data(), requested_size_bytes_); 545 546 // Deliver data packet, delay estimation and volume level to the user. 547 sink_->OnData(this, 548 data_->data(), 549 requested_size_bytes_, 550 capture_delay_bytes, 551 normalized_volume); 552 } 553 554 return noErr; 555} 556 557int AUAudioInputStream::HardwareSampleRate() { 558 // Determine the default input device's sample-rate. 559 AudioDeviceID device_id = kAudioObjectUnknown; 560 UInt32 info_size = sizeof(device_id); 561 562 AudioObjectPropertyAddress default_input_device_address = { 563 kAudioHardwarePropertyDefaultInputDevice, 564 kAudioObjectPropertyScopeGlobal, 565 kAudioObjectPropertyElementMaster 566 }; 567 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 568 &default_input_device_address, 569 0, 570 0, 571 &info_size, 572 &device_id); 573 if (result != noErr) 574 return 0.0; 575 576 Float64 nominal_sample_rate; 577 info_size = sizeof(nominal_sample_rate); 578 579 AudioObjectPropertyAddress nominal_sample_rate_address = { 580 kAudioDevicePropertyNominalSampleRate, 581 kAudioObjectPropertyScopeGlobal, 582 kAudioObjectPropertyElementMaster 583 }; 584 result = AudioObjectGetPropertyData(device_id, 585 &nominal_sample_rate_address, 586 0, 587 0, 588 &info_size, 589 &nominal_sample_rate); 590 if (result != noErr) 591 return 0.0; 592 593 return static_cast<int>(nominal_sample_rate); 594} 595 596double AUAudioInputStream::GetHardwareLatency() { 597 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) { 598 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown"; 599 return 0.0; 600 } 601 602 // Get audio unit latency. 603 Float64 audio_unit_latency_sec = 0.0; 604 UInt32 size = sizeof(audio_unit_latency_sec); 605 OSStatus result = AudioUnitGetProperty(audio_unit_, 606 kAudioUnitProperty_Latency, 607 kAudioUnitScope_Global, 608 0, 609 &audio_unit_latency_sec, 610 &size); 611 OSSTATUS_DLOG_IF(WARNING, result != noErr, result) 612 << "Could not get audio unit latency"; 613 614 // Get input audio device latency. 615 AudioObjectPropertyAddress property_address = { 616 kAudioDevicePropertyLatency, 617 kAudioDevicePropertyScopeInput, 618 kAudioObjectPropertyElementMaster 619 }; 620 UInt32 device_latency_frames = 0; 621 size = sizeof(device_latency_frames); 622 result = AudioObjectGetPropertyData(input_device_id_, 623 &property_address, 624 0, 625 NULL, 626 &size, 627 &device_latency_frames); 628 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; 629 630 return static_cast<double>((audio_unit_latency_sec * 631 format_.mSampleRate) + device_latency_frames); 632} 633 634double AUAudioInputStream::GetCaptureLatency( 635 const AudioTimeStamp* input_time_stamp) { 636 // Get the delay between between the actual recording instant and the time 637 // when the data packet is provided as a callback. 638 UInt64 capture_time_ns = AudioConvertHostTimeToNanos( 639 input_time_stamp->mHostTime); 640 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); 641 double delay_frames = static_cast<double> 642 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); 643 644 // Total latency is composed by the dynamic latency and the fixed 645 // hardware latency. 646 return (delay_frames + hardware_latency_frames_); 647} 648 649int AUAudioInputStream::GetNumberOfChannelsFromStream() { 650 // Get the stream format, to be able to read the number of channels. 651 AudioObjectPropertyAddress property_address = { 652 kAudioDevicePropertyStreamFormat, 653 kAudioDevicePropertyScopeInput, 654 kAudioObjectPropertyElementMaster 655 }; 656 AudioStreamBasicDescription stream_format; 657 UInt32 size = sizeof(stream_format); 658 OSStatus result = AudioObjectGetPropertyData(input_device_id_, 659 &property_address, 660 0, 661 NULL, 662 &size, 663 &stream_format); 664 if (result != noErr) { 665 DLOG(WARNING) << "Could not get stream format"; 666 return 0; 667 } 668 669 return static_cast<int>(stream_format.mChannelsPerFrame); 670} 671 672void AUAudioInputStream::HandleError(OSStatus err) { 673 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) 674 << " (" << err << ")"; 675 if (sink_) 676 sink_->OnError(this); 677} 678 679bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { 680 Boolean is_settable = false; 681 AudioObjectPropertyAddress property_address = { 682 kAudioDevicePropertyVolumeScalar, 683 kAudioDevicePropertyScopeInput, 684 static_cast<UInt32>(channel) 685 }; 686 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, 687 &property_address, 688 &is_settable); 689 return (result == noErr) ? is_settable : false; 690} 691 692} // namespace media 693