| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 #include "media/audio/mac/audio_low_latency_input_mac.h" |
| 4 | 5 |
| 5 #include "media/audio/mac/audio_low_latency_input_mac.h" | |
| 6 #include <CoreServices/CoreServices.h> | 6 #include <CoreServices/CoreServices.h> |
| 7 #include <mach/mach.h> | 7 #include <mach/mach.h> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/mac/mac_logging.h" | 12 #include "base/mac/mac_logging.h" |
| 13 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
| 14 #include "base/metrics/sparse_histogram.h" | 14 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 269 number_of_restart_attempts_(0), | 269 number_of_restart_attempts_(0), |
| 270 total_number_of_restart_attempts_(0), | 270 total_number_of_restart_attempts_(0), |
| 271 log_callback_(log_callback), | 271 log_callback_(log_callback), |
| 272 weak_factory_(this) { | 272 weak_factory_(this) { |
| 273 DCHECK(manager_); | 273 DCHECK(manager_); |
| 274 CHECK(!log_callback_.Equals(AudioManager::LogCallback())); | 274 CHECK(!log_callback_.Equals(AudioManager::LogCallback())); |
| 275 | 275 |
| 276 // Set up the desired (output) format specified by the client. | 276 // Set up the desired (output) format specified by the client. |
| 277 format_.mSampleRate = input_params.sample_rate(); | 277 format_.mSampleRate = input_params.sample_rate(); |
| 278 format_.mFormatID = kAudioFormatLinearPCM; | 278 format_.mFormatID = kAudioFormatLinearPCM; |
| 279 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 279 format_.mFormatFlags = |
| 280 kLinearPCMFormatFlagIsSignedInteger; | 280 kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; |
| 281 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); | 281 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); |
| 282 format_.mBitsPerChannel = input_params.bits_per_sample(); | 282 format_.mBitsPerChannel = input_params.bits_per_sample(); |
| 283 format_.mChannelsPerFrame = input_params.channels(); | 283 format_.mChannelsPerFrame = input_params.channels(); |
| 284 format_.mFramesPerPacket = 1; // uncompressed audio | 284 format_.mFramesPerPacket = 1; // uncompressed audio |
| 285 format_.mBytesPerPacket = (format_.mBitsPerChannel * | 285 format_.mBytesPerPacket = |
| 286 input_params.channels()) / 8; | 286 (format_.mBitsPerChannel * input_params.channels()) / 8; |
| 287 format_.mBytesPerFrame = format_.mBytesPerPacket; | 287 format_.mBytesPerFrame = format_.mBytesPerPacket; |
| 288 format_.mReserved = 0; | 288 format_.mReserved = 0; |
| 289 | 289 |
| 290 DVLOG(1) << "ctor"; | 290 DVLOG(1) << "ctor"; |
| 291 DVLOG(1) << "device ID: 0x" << std::hex << audio_device_id; | 291 DVLOG(1) << "device ID: 0x" << std::hex << audio_device_id; |
| 292 DVLOG(1) << "buffer size : " << number_of_frames_; | 292 DVLOG(1) << "buffer size : " << number_of_frames_; |
| 293 DVLOG(1) << "channels : " << input_params.channels(); | 293 DVLOG(1) << "channels : " << input_params.channels(); |
| 294 DVLOG(1) << "desired output format: " << format_; | 294 DVLOG(1) << "desired output format: " << format_; |
| 295 | 295 |
| 296 // Derive size (in bytes) of the buffers that we will render to. | 296 // Derive size (in bytes) of the buffers that we will render to. |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 AudioManagerMac::HardwareSampleRateForDevice(input_device_id_); | 338 AudioManagerMac::HardwareSampleRateForDevice(input_device_id_); |
| 339 DCHECK_EQ(sample_rate, format_.mSampleRate); | 339 DCHECK_EQ(sample_rate, format_.mSampleRate); |
| 340 | 340 |
| 341 // Start by obtaining an AudioOuputUnit using an AUHAL component description. | 341 // Start by obtaining an AudioOuputUnit using an AUHAL component description. |
| 342 | 342 |
| 343 // Description for the Audio Unit we want to use (AUHAL in this case). | 343 // Description for the Audio Unit we want to use (AUHAL in this case). |
| 344 // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device. | 344 // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device. |
| 345 // The user specifies which audio device to track. The audio unit can do | 345 // The user specifies which audio device to track. The audio unit can do |
| 346 // input from the device as well as output to the device. Bus 0 is used for | 346 // input from the device as well as output to the device. Bus 0 is used for |
| 347 // the output side, bus 1 is used to get audio input from the device. | 347 // the output side, bus 1 is used to get audio input from the device. |
| 348 AudioComponentDescription desc = { | 348 AudioComponentDescription desc = {kAudioUnitType_Output, |
| 349 kAudioUnitType_Output, | 349 kAudioUnitSubType_HALOutput, |
| 350 kAudioUnitSubType_HALOutput, | 350 kAudioUnitManufacturer_Apple, 0, 0}; |
| 351 kAudioUnitManufacturer_Apple, | |
| 352 0, | |
| 353 0 | |
| 354 }; | |
| 355 | 351 |
| 356 // Find a component that meets the description in |desc|. | 352 // Find a component that meets the description in |desc|. |
| 357 AudioComponent comp = AudioComponentFindNext(nullptr, &desc); | 353 AudioComponent comp = AudioComponentFindNext(nullptr, &desc); |
| 358 DCHECK(comp); | 354 DCHECK(comp); |
| 359 if (!comp) { | 355 if (!comp) { |
| 360 HandleError(kAudioUnitErr_NoConnection); | 356 HandleError(kAudioUnitErr_NoConnection); |
| 361 return false; | 357 return false; |
| 362 } | 358 } |
| 363 | 359 |
| 364 // Get access to the service provided by the specified Audio Unit. | 360 // Get access to the service provided by the specified Audio Unit. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 387 | 383 |
| 388 // After creating the AUHAL object, we must enable IO on the input scope | 384 // After creating the AUHAL object, we must enable IO on the input scope |
| 389 // of the Audio Unit to obtain the device input. Input must be explicitly | 385 // of the Audio Unit to obtain the device input. Input must be explicitly |
| 390 // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1 | 386 // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1 |
| 391 // of the AUHAL. Because the AUHAL can be used for both input and output, | 387 // of the AUHAL. Because the AUHAL can be used for both input and output, |
| 392 // we must also disable IO on the output scope. | 388 // we must also disable IO on the output scope. |
| 393 | 389 |
| 394 UInt32 enableIO = 1; | 390 UInt32 enableIO = 1; |
| 395 | 391 |
| 396 // Enable input on the AUHAL. | 392 // Enable input on the AUHAL. |
| 397 result = AudioUnitSetProperty(audio_unit_, | 393 result = AudioUnitSetProperty(audio_unit_, kAudioOutputUnitProperty_EnableIO, |
| 398 kAudioOutputUnitProperty_EnableIO, | |
| 399 kAudioUnitScope_Input, | 394 kAudioUnitScope_Input, |
| 400 1, // input element 1 | 395 1, // input element 1 |
| 401 &enableIO, // enable | 396 &enableIO, // enable |
| 402 sizeof(enableIO)); | 397 sizeof(enableIO)); |
| 403 if (result != noErr) { | 398 if (result != noErr) { |
| 404 HandleError(result); | 399 HandleError(result); |
| 405 return false; | 400 return false; |
| 406 } | 401 } |
| 407 | 402 |
| 408 // Disable output on the AUHAL. | 403 // Disable output on the AUHAL. |
| 409 enableIO = 0; | 404 enableIO = 0; |
| 410 result = AudioUnitSetProperty(audio_unit_, | 405 result = AudioUnitSetProperty(audio_unit_, kAudioOutputUnitProperty_EnableIO, |
| 411 kAudioOutputUnitProperty_EnableIO, | |
| 412 kAudioUnitScope_Output, | 406 kAudioUnitScope_Output, |
| 413 0, // output element 0 | 407 0, // output element 0 |
| 414 &enableIO, // disable | 408 &enableIO, // disable |
| 415 sizeof(enableIO)); | 409 sizeof(enableIO)); |
| 416 if (result != noErr) { | 410 if (result != noErr) { |
| 417 HandleError(result); | 411 HandleError(result); |
| 418 return false; | 412 return false; |
| 419 } | 413 } |
| 420 | 414 |
| 421 // Next, set the audio device to be the Audio Unit's current device. | 415 // Next, set the audio device to be the Audio Unit's current device. |
| 422 // Note that, devices can only be set to the AUHAL after enabling IO. | 416 // Note that, devices can only be set to the AUHAL after enabling IO. |
| 423 result = AudioUnitSetProperty(audio_unit_, | 417 result = AudioUnitSetProperty( |
| 424 kAudioOutputUnitProperty_CurrentDevice, | 418 audio_unit_, kAudioOutputUnitProperty_CurrentDevice, |
| 425 kAudioUnitScope_Global, | 419 kAudioUnitScope_Global, 0, &input_device_id_, sizeof(input_device_id_)); |
| 426 0, | |
| 427 &input_device_id_, | |
| 428 sizeof(input_device_id_)); | |
| 429 if (result != noErr) { | 420 if (result != noErr) { |
| 430 HandleError(result); | 421 HandleError(result); |
| 431 return false; | 422 return false; |
| 432 } | 423 } |
| 433 | 424 |
| 434 // Register the input procedure for the AUHAL. This procedure will be called | 425 // Register the input procedure for the AUHAL. This procedure will be called |
| 435 // when the AUHAL has received new data from the input device. | 426 // when the AUHAL has received new data from the input device. |
| 436 AURenderCallbackStruct callback; | 427 AURenderCallbackStruct callback; |
| 437 callback.inputProc = &DataIsAvailable; | 428 callback.inputProc = &DataIsAvailable; |
| 438 callback.inputProcRefCon = this; | 429 callback.inputProcRefCon = this; |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully"; | 525 DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully"; |
| 535 if (IsRunning()) | 526 if (IsRunning()) |
| 536 return; | 527 return; |
| 537 | 528 |
| 538 // Check if we should defer Start() for http://crbug.com/160920. | 529 // Check if we should defer Start() for http://crbug.com/160920. |
| 539 if (manager_->ShouldDeferStreamStart()) { | 530 if (manager_->ShouldDeferStreamStart()) { |
| 540 LOG(WARNING) << "Start of input audio is deferred"; | 531 LOG(WARNING) << "Start of input audio is deferred"; |
| 541 start_was_deferred_ = true; | 532 start_was_deferred_ = true; |
| 542 // Use a cancellable closure so that if Stop() is called before Start() | 533 // Use a cancellable closure so that if Stop() is called before Start() |
| 543 // actually runs, we can cancel the pending start. | 534 // actually runs, we can cancel the pending start. |
| 544 deferred_start_cb_.Reset(base::Bind( | 535 deferred_start_cb_.Reset(base::Bind(&AUAudioInputStream::Start, |
| 545 &AUAudioInputStream::Start, base::Unretained(this), callback)); | 536 base::Unretained(this), callback)); |
| 546 manager_->GetTaskRunner()->PostDelayedTask( | 537 manager_->GetTaskRunner()->PostDelayedTask( |
| 547 FROM_HERE, deferred_start_cb_.callback(), | 538 FROM_HERE, deferred_start_cb_.callback(), |
| 548 base::TimeDelta::FromSeconds( | 539 base::TimeDelta::FromSeconds( |
| 549 AudioManagerMac::kStartDelayInSecsForPowerEvents)); | 540 AudioManagerMac::kStartDelayInSecsForPowerEvents)); |
| 550 return; | 541 return; |
| 551 } | 542 } |
| 552 | 543 |
| 553 sink_ = callback; | 544 sink_ = callback; |
| 554 last_success_time_ = base::TimeTicks::Now(); | 545 last_success_time_ = base::TimeTicks::Now(); |
| 555 last_callback_time_ = base::TimeTicks::Now(); | 546 last_callback_time_ = base::TimeTicks::Now(); |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 684 DCHECK_LE(volume, 1.0); | 675 DCHECK_LE(volume, 1.0); |
| 685 | 676 |
| 686 // Verify that we have a valid device. | 677 // Verify that we have a valid device. |
| 687 if (input_device_id_ == kAudioObjectUnknown) { | 678 if (input_device_id_ == kAudioObjectUnknown) { |
| 688 NOTREACHED() << "Device ID is unknown"; | 679 NOTREACHED() << "Device ID is unknown"; |
| 689 return; | 680 return; |
| 690 } | 681 } |
| 691 | 682 |
| 692 Float32 volume_float32 = static_cast<Float32>(volume); | 683 Float32 volume_float32 = static_cast<Float32>(volume); |
| 693 AudioObjectPropertyAddress property_address = { | 684 AudioObjectPropertyAddress property_address = { |
| 694 kAudioDevicePropertyVolumeScalar, | 685 kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, |
| 695 kAudioDevicePropertyScopeInput, | 686 kAudioObjectPropertyElementMaster}; |
| 696 kAudioObjectPropertyElementMaster | |
| 697 }; | |
| 698 | 687 |
| 699 // Try to set the volume for master volume channel. | 688 // Try to set the volume for master volume channel. |
| 700 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) { | 689 if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) { |
| 701 OSStatus result = AudioObjectSetPropertyData( | 690 OSStatus result = AudioObjectSetPropertyData( |
| 702 input_device_id_, &property_address, 0, nullptr, sizeof(volume_float32), | 691 input_device_id_, &property_address, 0, nullptr, sizeof(volume_float32), |
| 703 &volume_float32); | 692 &volume_float32); |
| 704 if (result != noErr) { | 693 if (result != noErr) { |
| 705 DLOG(WARNING) << "Failed to set volume to " << volume_float32; | 694 DLOG(WARNING) << "Failed to set volume to " << volume_float32; |
| 706 } | 695 } |
| 707 return; | 696 return; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 732 } | 721 } |
| 733 | 722 |
| 734 double AUAudioInputStream::GetVolume() { | 723 double AUAudioInputStream::GetVolume() { |
| 735 // Verify that we have a valid device. | 724 // Verify that we have a valid device. |
| 736 if (input_device_id_ == kAudioObjectUnknown) { | 725 if (input_device_id_ == kAudioObjectUnknown) { |
| 737 NOTREACHED() << "Device ID is unknown"; | 726 NOTREACHED() << "Device ID is unknown"; |
| 738 return 0.0; | 727 return 0.0; |
| 739 } | 728 } |
| 740 | 729 |
| 741 AudioObjectPropertyAddress property_address = { | 730 AudioObjectPropertyAddress property_address = { |
| 742 kAudioDevicePropertyVolumeScalar, | 731 kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, |
| 743 kAudioDevicePropertyScopeInput, | 732 kAudioObjectPropertyElementMaster}; |
| 744 kAudioObjectPropertyElementMaster | |
| 745 }; | |
| 746 | 733 |
| 747 if (AudioObjectHasProperty(input_device_id_, &property_address)) { | 734 if (AudioObjectHasProperty(input_device_id_, &property_address)) { |
| 748 // The device supports master volume control, get the volume from the | 735 // The device supports master volume control, get the volume from the |
| 749 // master channel. | 736 // master channel. |
| 750 Float32 volume_float32 = 0.0; | 737 Float32 volume_float32 = 0.0; |
| 751 UInt32 size = sizeof(volume_float32); | 738 UInt32 size = sizeof(volume_float32); |
| 752 OSStatus result = | 739 OSStatus result = |
| 753 AudioObjectGetPropertyData(input_device_id_, &property_address, 0, | 740 AudioObjectGetPropertyData(input_device_id_, &property_address, 0, |
| 754 nullptr, &size, &volume_float32); | 741 nullptr, &size, &volume_float32); |
| 755 if (result == noErr) | 742 if (result == noErr) |
| (...skipping 25 matching lines...) Expand all Loading... |
| 781 | 768 |
| 782 DLOG(WARNING) << "Failed to get volume"; | 769 DLOG(WARNING) << "Failed to get volume"; |
| 783 return 0.0; | 770 return 0.0; |
| 784 } | 771 } |
| 785 | 772 |
| 786 bool AUAudioInputStream::IsMuted() { | 773 bool AUAudioInputStream::IsMuted() { |
| 787 // Verify that we have a valid device. | 774 // Verify that we have a valid device. |
| 788 DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown"; | 775 DCHECK_NE(input_device_id_, kAudioObjectUnknown) << "Device ID is unknown"; |
| 789 | 776 |
| 790 AudioObjectPropertyAddress property_address = { | 777 AudioObjectPropertyAddress property_address = { |
| 791 kAudioDevicePropertyMute, | 778 kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, |
| 792 kAudioDevicePropertyScopeInput, | 779 kAudioObjectPropertyElementMaster}; |
| 793 kAudioObjectPropertyElementMaster | |
| 794 }; | |
| 795 | 780 |
| 796 if (!AudioObjectHasProperty(input_device_id_, &property_address)) { | 781 if (!AudioObjectHasProperty(input_device_id_, &property_address)) { |
| 797 DLOG(ERROR) << "Device does not support checking master mute state"; | 782 DLOG(ERROR) << "Device does not support checking master mute state"; |
| 798 return false; | 783 return false; |
| 799 } | 784 } |
| 800 | 785 |
| 801 UInt32 muted = 0; | 786 UInt32 muted = 0; |
| 802 UInt32 size = sizeof(muted); | 787 UInt32 size = sizeof(muted); |
| 803 OSStatus result = AudioObjectGetPropertyData( | 788 OSStatus result = AudioObjectGetPropertyData( |
| 804 input_device_id_, &property_address, 0, nullptr, &size, &muted); | 789 input_device_id_, &property_address, 0, nullptr, &size, &muted); |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 979 // Dynamically increase capacity of the FIFO to handle larger buffers from | 964 // Dynamically increase capacity of the FIFO to handle larger buffers from |
| 980 // CoreAudio. This can happen in combination with Apple Thunderbolt Displays | 965 // CoreAudio. This can happen in combination with Apple Thunderbolt Displays |
| 981 // when the Display Audio is used as capture source and the cable is first | 966 // when the Display Audio is used as capture source and the cable is first |
| 982 // remove and then inserted again. | 967 // remove and then inserted again. |
| 983 // See http://www.crbug.com/434681 for details. | 968 // See http://www.crbug.com/434681 for details. |
| 984 if (static_cast<int>(number_of_frames) > fifo_.GetUnfilledFrames()) { | 969 if (static_cast<int>(number_of_frames) > fifo_.GetUnfilledFrames()) { |
| 985 // Derive required increase in number of FIFO blocks. The increase is | 970 // Derive required increase in number of FIFO blocks. The increase is |
| 986 // typically one block. | 971 // typically one block. |
| 987 const int blocks = | 972 const int blocks = |
| 988 static_cast<int>((number_of_frames - fifo_.GetUnfilledFrames()) / | 973 static_cast<int>((number_of_frames - fifo_.GetUnfilledFrames()) / |
| 989 number_of_frames_) + 1; | 974 number_of_frames_) + |
| 975 1; |
| 990 DLOG(WARNING) << "Increasing FIFO capacity by " << blocks << " blocks"; | 976 DLOG(WARNING) << "Increasing FIFO capacity by " << blocks << " blocks"; |
| 991 TRACE_EVENT_INSTANT1("audio", "Increasing FIFO capacity", | 977 TRACE_EVENT_INSTANT1("audio", "Increasing FIFO capacity", |
| 992 TRACE_EVENT_SCOPE_THREAD, "increased by", blocks); | 978 TRACE_EVENT_SCOPE_THREAD, "increased by", blocks); |
| 993 fifo_.IncreaseCapacity(blocks); | 979 fifo_.IncreaseCapacity(blocks); |
| 994 } | 980 } |
| 995 | 981 |
| 996 // Copy captured (and interleaved) data into FIFO. | 982 // Copy captured (and interleaved) data into FIFO. |
| 997 fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8); | 983 fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8); |
| 998 | 984 |
| 999 // Consume and deliver the data when the FIFO has a block of available data. | 985 // Consume and deliver the data when the FIFO has a block of available data. |
| (...skipping 681 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1681 | 1667 |
| 1682 number_of_frames_provided_ = 0; | 1668 number_of_frames_provided_ = 0; |
| 1683 glitches_detected_ = 0; | 1669 glitches_detected_ = 0; |
| 1684 last_sample_time_ = 0; | 1670 last_sample_time_ = 0; |
| 1685 last_number_of_frames_ = 0; | 1671 last_number_of_frames_ = 0; |
| 1686 total_lost_frames_ = 0; | 1672 total_lost_frames_ = 0; |
| 1687 largest_glitch_frames_ = 0; | 1673 largest_glitch_frames_ = 0; |
| 1688 } | 1674 } |
| 1689 | 1675 |
| 1690 } // namespace media | 1676 } // namespace media |
| OLD | NEW |