Chromium Code Reviews| 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 | 4 |
| 5 #include "media/audio/mac/audio_low_latency_input_mac.h" | 5 #include "media/audio/mac/audio_low_latency_input_mac.h" |
| 6 | 6 |
| 7 #include <CoreServices/CoreServices.h> | 7 #include <CoreServices/CoreServices.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/mac/mac_logging.h" | 10 #include "base/mac/mac_logging.h" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 51 << "frames per packet : " << format.mFramesPerPacket << std::endl | 51 << "frames per packet : " << format.mFramesPerPacket << std::endl |
| 52 << "bytes per frame : " << format.mBytesPerFrame << std::endl | 52 << "bytes per frame : " << format.mBytesPerFrame << std::endl |
| 53 << "channels per frame: " << format.mChannelsPerFrame << std::endl | 53 << "channels per frame: " << format.mChannelsPerFrame << std::endl |
| 54 << "bits per channel : " << format.mBitsPerChannel << std::endl | 54 << "bits per channel : " << format.mBitsPerChannel << std::endl |
| 55 << "reserved : " << format.mReserved << std::endl | 55 << "reserved : " << format.mReserved << std::endl |
| 56 << "interleaved : " | 56 << "interleaved : " |
| 57 << (FormatIsInterleaved(format.mFormatFlags) ? "yes" : "no"); | 57 << (FormatIsInterleaved(format.mFormatFlags) ? "yes" : "no"); |
| 58 return os; | 58 return os; |
| 59 } | 59 } |
| 60 | 60 |
| 61 // Property address to monitor device changes. Use wildcards to match any and | |
| 62 // all values for their associated type. Filtering for device-specific | |
| 63 // notifications will take place in the callback. | |
| 64 const AudioObjectPropertyAddress | |
| 65 AUAudioInputStream::kDeviceChangePropertyAddress = { | |
| 66 kAudioObjectPropertySelectorWildcard, kAudioObjectPropertyScopeWildcard, | |
| 67 kAudioObjectPropertyElementWildcard}; | |
| 68 | |
| 69 // TODO(henrika): add comments... | |
| 70 enum AudioDevicePropertyResult { | |
| 71 PROPERTY_DEVICE_HAS_CHANGED = 0, | |
| 72 PROPERTY_IO_STOPPED_ABNORMALLY = 1, | |
|
o1ka
2016/02/18 13:46:23
I would not assign values to any but the first one
henrika (OOO until Aug 14)
2016/02/18 15:58:03
This style is used all over Chrome in combination
o1ka
2016/02/18 16:54:30
Thank you for the info, will save me some time in
henrika (OOO until Aug 14)
2016/02/19 12:46:11
Acknowledged.
| |
| 73 PROPERTY_HOG_MODE = 2, | |
| 74 PROPERTY_BUFFER_FRAME_SIZE = 3, | |
| 75 PROPERTY_BUFFER_FRAME_SIZE_RANGE = 4, | |
| 76 PROPERTY_STREAM_CONFIGURATION = 5, | |
| 77 PROPERTY_ACTUAL_SAMPLE_RATE = 6, | |
| 78 PROPERTY_NOMINAL_SAMPLE_RATE = 7, | |
| 79 PROPERTY_DEVICE_IS_RUNNING_SOMEWHERE = 8, | |
| 80 PROPERTY_DEVICE_IS_RUNNING = 9, | |
| 81 PROPERTY_DEVICE_IS_ALIVE = 10, | |
| 82 PROPERTY_MAX = PROPERTY_DEVICE_IS_ALIVE | |
| 83 }; | |
| 84 | |
| 85 // TODO(henrika): add comments... | |
| 86 static void LogDevicePropertyChange(AudioDevicePropertyResult result) { | |
| 87 UMA_HISTOGRAM_ENUMERATION("Media.Audio.InputDevicePropertyChangedMac", result, | |
| 88 PROPERTY_MAX + 1); | |
| 89 } | |
| 90 | |
| 61 static OSStatus GetInputDeviceStreamFormat( | 91 static OSStatus GetInputDeviceStreamFormat( |
| 62 AudioUnit audio_unit, | 92 AudioUnit audio_unit, |
| 63 AudioStreamBasicDescription* format) { | 93 AudioStreamBasicDescription* format) { |
| 64 DCHECK(audio_unit); | 94 DCHECK(audio_unit); |
| 65 UInt32 property_size = sizeof(*format); | 95 UInt32 property_size = sizeof(*format); |
| 66 // Get the audio stream data format on the input scope of the input element | 96 // Get the audio stream data format on the input scope of the input element |
| 67 // since it is connected to the current input device. | 97 // since it is connected to the current input device. |
| 68 OSStatus result = | 98 OSStatus result = |
| 69 AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, | 99 AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, |
| 70 kAudioUnitScope_Input, 1, format, &property_size); | 100 kAudioUnitScope_Input, 1, format, &property_size); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 86 audio_unit_(0), | 116 audio_unit_(0), |
| 87 input_device_id_(audio_device_id), | 117 input_device_id_(audio_device_id), |
| 88 hardware_latency_frames_(0), | 118 hardware_latency_frames_(0), |
| 89 number_of_channels_in_frame_(0), | 119 number_of_channels_in_frame_(0), |
| 90 fifo_(input_params.channels(), | 120 fifo_(input_params.channels(), |
| 91 number_of_frames_, | 121 number_of_frames_, |
| 92 kNumberOfBlocksBufferInFifo), | 122 kNumberOfBlocksBufferInFifo), |
| 93 input_callback_is_active_(false), | 123 input_callback_is_active_(false), |
| 94 start_was_deferred_(false), | 124 start_was_deferred_(false), |
| 95 buffer_size_was_changed_(false), | 125 buffer_size_was_changed_(false), |
| 96 audio_unit_render_has_worked_(false) { | 126 audio_unit_render_has_worked_(false), |
| 127 device_listener_is_active_(false) { | |
| 97 DCHECK(manager_); | 128 DCHECK(manager_); |
| 98 | 129 |
| 99 // Set up the desired (output) format specified by the client. | 130 // Set up the desired (output) format specified by the client. |
| 100 format_.mSampleRate = input_params.sample_rate(); | 131 format_.mSampleRate = input_params.sample_rate(); |
| 101 format_.mFormatID = kAudioFormatLinearPCM; | 132 format_.mFormatID = kAudioFormatLinearPCM; |
| 102 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 133 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
| 103 kLinearPCMFormatFlagIsSignedInteger; | 134 kLinearPCMFormatFlagIsSignedInteger; |
| 104 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); | 135 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); |
| 105 format_.mBitsPerChannel = input_params.bits_per_sample(); | 136 format_.mBitsPerChannel = input_params.bits_per_sample(); |
| 106 format_.mChannelsPerFrame = input_params.channels(); | 137 format_.mChannelsPerFrame = input_params.channels(); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 128 audio_buffer_list_.mNumberBuffers = 1; | 159 audio_buffer_list_.mNumberBuffers = 1; |
| 129 | 160 |
| 130 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; | 161 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; |
| 131 audio_buffer->mNumberChannels = input_params.channels(); | 162 audio_buffer->mNumberChannels = input_params.channels(); |
| 132 audio_buffer->mDataByteSize = data_byte_size; | 163 audio_buffer->mDataByteSize = data_byte_size; |
| 133 audio_buffer->mData = audio_data_buffer_.get(); | 164 audio_buffer->mData = audio_data_buffer_.get(); |
| 134 } | 165 } |
| 135 | 166 |
| 136 AUAudioInputStream::~AUAudioInputStream() { | 167 AUAudioInputStream::~AUAudioInputStream() { |
| 137 DVLOG(1) << "~dtor"; | 168 DVLOG(1) << "~dtor"; |
| 169 DeRegisterDeviceChangeListener(); | |
| 138 } | 170 } |
| 139 | 171 |
| 140 // Obtain and open the AUHAL AudioOutputUnit for recording. | 172 // Obtain and open the AUHAL AudioOutputUnit for recording. |
| 141 bool AUAudioInputStream::Open() { | 173 bool AUAudioInputStream::Open() { |
| 142 DCHECK(thread_checker_.CalledOnValidThread()); | 174 DCHECK(thread_checker_.CalledOnValidThread()); |
| 143 DVLOG(1) << "Open"; | 175 DVLOG(1) << "Open"; |
| 144 DCHECK(!audio_unit_); | 176 DCHECK(!audio_unit_); |
| 145 | 177 |
| 146 // Verify that we have a valid device. Send appropriate error code to | 178 // Verify that we have a valid device. Send appropriate error code to |
| 147 // HandleError() to ensure that the error type is added to UMA stats. | 179 // HandleError() to ensure that the error type is added to UMA stats. |
| 148 if (input_device_id_ == kAudioObjectUnknown) { | 180 if (input_device_id_ == kAudioObjectUnknown) { |
| 149 NOTREACHED() << "Device ID is unknown"; | 181 NOTREACHED() << "Device ID is unknown"; |
| 150 HandleError(kAudioUnitErr_InvalidElement); | 182 HandleError(kAudioUnitErr_InvalidElement); |
| 151 return false; | 183 return false; |
| 152 } | 184 } |
| 153 | 185 |
| 186 RegisterDeviceChangeListener(); | |
| 187 | |
| 154 // The requested sample-rate must match the hardware sample-rate. | 188 // The requested sample-rate must match the hardware sample-rate. |
| 155 int sample_rate = | 189 int sample_rate = |
| 156 AudioManagerMac::HardwareSampleRateForDevice(input_device_id_); | 190 AudioManagerMac::HardwareSampleRateForDevice(input_device_id_); |
| 157 DCHECK_EQ(sample_rate, format_.mSampleRate); | 191 DCHECK_EQ(sample_rate, format_.mSampleRate); |
| 158 | 192 |
| 159 // Start by obtaining an AudioOuputUnit using an AUHAL component description. | 193 // Start by obtaining an AudioOuputUnit using an AUHAL component description. |
| 160 | 194 |
| 161 // Description for the Audio Unit we want to use (AUHAL in this case). | 195 // Description for the Audio Unit we want to use (AUHAL in this case). |
| 162 // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device. | 196 // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device. |
| 163 // The user specifies which audio device to track. The audio unit can do | 197 // The user specifies which audio device to track. The audio unit can do |
| (...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 421 | 455 |
| 422 void AUAudioInputStream::Close() { | 456 void AUAudioInputStream::Close() { |
| 423 DCHECK(thread_checker_.CalledOnValidThread()); | 457 DCHECK(thread_checker_.CalledOnValidThread()); |
| 424 DVLOG(1) << "Close"; | 458 DVLOG(1) << "Close"; |
| 425 // It is valid to call Close() before calling open or Start(). | 459 // It is valid to call Close() before calling open or Start(). |
| 426 // It is also valid to call Close() after Start() has been called. | 460 // It is also valid to call Close() after Start() has been called. |
| 427 if (IsRunning()) { | 461 if (IsRunning()) { |
| 428 Stop(); | 462 Stop(); |
| 429 } | 463 } |
| 430 CloseAudioUnit(); | 464 CloseAudioUnit(); |
| 465 DeRegisterDeviceChangeListener(); | |
| 431 // Inform the audio manager that we have been closed. This will cause our | 466 // Inform the audio manager that we have been closed. This will cause our |
| 432 // destruction. | 467 // destruction. |
| 433 manager_->ReleaseInputStream(this); | 468 manager_->ReleaseInputStream(this); |
| 434 } | 469 } |
| 435 | 470 |
| 436 double AUAudioInputStream::GetMaxVolume() { | 471 double AUAudioInputStream::GetMaxVolume() { |
| 437 // Verify that we have a valid device. | 472 // Verify that we have a valid device. |
| 438 if (input_device_id_ == kAudioObjectUnknown) { | 473 if (input_device_id_ == kAudioObjectUnknown) { |
| 439 NOTREACHED() << "Device ID is unknown"; | 474 NOTREACHED() << "Device ID is unknown"; |
| 440 return 0.0; | 475 return 0.0; |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 605 UInt32 bus_number, | 640 UInt32 bus_number, |
| 606 UInt32 number_of_frames) { | 641 UInt32 number_of_frames) { |
| 607 // Indicate that input callbacks have started on the internal AUHAL IO | 642 // Indicate that input callbacks have started on the internal AUHAL IO |
| 608 // thread. The |input_callback_is_active_| member is read from the creating | 643 // thread. The |input_callback_is_active_| member is read from the creating |
| 609 // thread when a timer fires once and set to false in Stop() on the same | 644 // thread when a timer fires once and set to false in Stop() on the same |
| 610 // thread. It means that this thread is the only writer of | 645 // thread. It means that this thread is the only writer of |
| 611 // |input_callback_is_active_| once the tread starts and it should therefore | 646 // |input_callback_is_active_| once the tread starts and it should therefore |
| 612 // be safe to modify. | 647 // be safe to modify. |
| 613 SetInputCallbackIsActive(true); | 648 SetInputCallbackIsActive(true); |
| 614 | 649 |
| 650 // We are only interested in device property changes in combination with | |
| 651 // failing input callbacks. Hence, might as well disable the listener now. | |
| 652 DeRegisterDeviceChangeListener(); | |
| 653 | |
| 615 // Update the |mDataByteSize| value in the audio_buffer_list() since | 654 // Update the |mDataByteSize| value in the audio_buffer_list() since |
| 616 // |number_of_frames| can be changed on the fly. | 655 // |number_of_frames| can be changed on the fly. |
| 617 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|, | 656 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|, |
| 618 // otherwise it will put CoreAudio into bad state and results in | 657 // otherwise it will put CoreAudio into bad state and results in |
| 619 // AudioUnitRender() returning -50 for the new created stream. | 658 // AudioUnitRender() returning -50 for the new created stream. |
| 620 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and | 659 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and |
| 621 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes. | 660 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes. |
| 622 // See crbug/428706 for details. | 661 // See crbug/428706 for details. |
| 623 UInt32 new_size = number_of_frames * format_.mBytesPerFrame; | 662 UInt32 new_size = number_of_frames * format_.mBytesPerFrame; |
| 624 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; | 663 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 743 DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_)); | 782 DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_)); |
| 744 | 783 |
| 745 // Compensate the audio delay caused by the FIFO. | 784 // Compensate the audio delay caused by the FIFO. |
| 746 capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame; | 785 capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame; |
| 747 sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume); | 786 sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume); |
| 748 } | 787 } |
| 749 | 788 |
| 750 return noErr; | 789 return noErr; |
| 751 } | 790 } |
| 752 | 791 |
| 792 OSStatus AUAudioInputStream::OnDevicePropertyChanged( | |
|
o1ka
2016/02/18 13:46:22
Can there be a race between this call and deregist
henrika (OOO until Aug 14)
2016/02/18 15:58:03
The call in the destructor is invalid (not needed)
| |
| 793 AudioObjectID object_id, | |
| 794 UInt32 num_addresses, | |
| 795 const AudioObjectPropertyAddress addresses[], | |
| 796 void* context) { | |
| 797 AUAudioInputStream* self = static_cast<AUAudioInputStream*>(context); | |
| 798 return self->DevicePropertyChanged(object_id, num_addresses, addresses); | |
| 799 } | |
| 800 | |
| 801 OSStatus AUAudioInputStream::DevicePropertyChanged( | |
| 802 AudioObjectID object_id, | |
| 803 UInt32 num_addresses, | |
| 804 const AudioObjectPropertyAddress addresses[]) { | |
| 805 if (object_id != input_device_id_) | |
| 806 return noErr; | |
| 807 | |
| 808 // Listeners will be called when possibly many properties have changed. | |
|
o1ka
2016/02/18 13:46:22
Can the same listener be called concurrently?
henrika (OOO until Aug 14)
2016/02/18 15:58:03
The documentation does not say anything about that
| |
| 809 // Consequently, the implementation of a listener must go through the array of | |
| 810 // addresses to see what exactly has changed. | |
| 811 for (UInt32 i = 0; i < num_addresses; ++i) { | |
| 812 const UInt32 property = addresses[i].mSelector; | |
| 813 switch (property) { | |
| 814 // Filter out and store certain property values provided by the | |
| 815 // AudioDevice class. | |
| 816 case kAudioDevicePropertyDeviceHasChanged: | |
|
o1ka
2016/02/18 13:46:22
I would say that having both 'switch' and map is a
henrika (OOO until Aug 14)
2016/02/18 15:58:03
Great comment, thanks. Will add all to the map and
| |
| 817 case kAudioDevicePropertyIOStoppedAbnormally: | |
| 818 case kAudioDevicePropertyHogMode: | |
| 819 case kAudioDevicePropertyBufferFrameSize: | |
| 820 case kAudioDevicePropertyBufferFrameSizeRange: | |
| 821 case kAudioDevicePropertyStreamConfiguration: | |
| 822 case kAudioDevicePropertyActualSampleRate: | |
| 823 case kAudioDevicePropertyNominalSampleRate: | |
| 824 case kAudioDevicePropertyDeviceIsRunningSomewhere: | |
| 825 case kAudioDevicePropertyDeviceIsRunning: | |
| 826 case kAudioDevicePropertyDeviceIsAlive: | |
| 827 // Use selector as key to a map and increase its value. | |
| 828 device_property_changes_map_[property]++; | |
| 829 break; | |
| 830 default: | |
| 831 // TODO(henrika): figure out if more property changes should be tracked. | |
| 832 DVLOG(1) << "No action for kAudioDeviceProperty: " << property; | |
| 833 break; | |
| 834 } | |
| 835 } | |
| 836 return noErr; | |
| 837 } | |
| 838 | |
| 839 void AUAudioInputStream::RegisterDeviceChangeListener() { | |
| 840 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 841 DVLOG(1) << "RegisterDeviceChangeListener"; | |
| 842 if (input_device_id_ == kAudioObjectUnknown) | |
| 843 return; | |
| 844 | |
| 845 device_property_changes_map_.clear(); | |
| 846 | |
| 847 OSStatus result = AudioObjectAddPropertyListener( | |
| 848 input_device_id_, &kDeviceChangePropertyAddress, | |
| 849 &AUAudioInputStream::OnDevicePropertyChanged, this); | |
| 850 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | |
| 851 << "AudioObjectAddPropertyListener() failed!"; | |
| 852 SetDeviceListenerIsActive(result == noErr); | |
| 853 } | |
| 854 | |
| 855 void AUAudioInputStream::DeRegisterDeviceChangeListener() { | |
| 856 if (!GetDeviceListenerIsActive()) | |
| 857 return; | |
| 858 DVLOG(1) << "DeRegisterDeviceChangeListener"; | |
| 859 if (input_device_id_ == kAudioObjectUnknown) | |
| 860 return; | |
| 861 OSStatus result = AudioObjectRemovePropertyListener( | |
|
o1ka
2016/02/18 13:46:23
Is it safe to call it second time if the first att
henrika (OOO until Aug 14)
2016/02/18 15:58:03
I don't know since I've never been able to make th
o1ka
2016/02/18 16:54:30
Yes, I have the same feeling. In this case, it sho
henrika (OOO until Aug 14)
2016/02/19 12:46:12
Done.
| |
| 862 input_device_id_, &kDeviceChangePropertyAddress, | |
| 863 &AUAudioInputStream::OnDevicePropertyChanged, this); | |
| 864 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | |
| 865 << "AudioObjectRemovePropertyListener() failed!"; | |
| 866 SetDeviceListenerIsActive(!(result == noErr)); | |
| 867 } | |
| 868 | |
| 753 int AUAudioInputStream::HardwareSampleRate() { | 869 int AUAudioInputStream::HardwareSampleRate() { |
| 754 // Determine the default input device's sample-rate. | 870 // Determine the default input device's sample-rate. |
| 755 AudioDeviceID device_id = kAudioObjectUnknown; | 871 AudioDeviceID device_id = kAudioObjectUnknown; |
| 756 UInt32 info_size = sizeof(device_id); | 872 UInt32 info_size = sizeof(device_id); |
| 757 | 873 |
| 758 AudioObjectPropertyAddress default_input_device_address = { | 874 AudioObjectPropertyAddress default_input_device_address = { |
| 759 kAudioHardwarePropertyDefaultInputDevice, | 875 kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, |
| 760 kAudioObjectPropertyScopeGlobal, | 876 kAudioObjectPropertyElementMaster}; |
| 761 kAudioObjectPropertyElementMaster | |
| 762 }; | |
| 763 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, | 877 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, |
| 764 &default_input_device_address, | 878 &default_input_device_address, 0, |
| 765 0, | 879 0, &info_size, &device_id); |
| 766 0, | |
| 767 &info_size, | |
| 768 &device_id); | |
| 769 if (result != noErr) | 880 if (result != noErr) |
| 770 return 0.0; | 881 return 0.0; |
| 771 | 882 |
| 772 Float64 nominal_sample_rate; | 883 Float64 nominal_sample_rate; |
| 773 info_size = sizeof(nominal_sample_rate); | 884 info_size = sizeof(nominal_sample_rate); |
| 774 | 885 |
| 775 AudioObjectPropertyAddress nominal_sample_rate_address = { | 886 AudioObjectPropertyAddress nominal_sample_rate_address = { |
| 776 kAudioDevicePropertyNominalSampleRate, | 887 kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, |
| 777 kAudioObjectPropertyScopeGlobal, | 888 kAudioObjectPropertyElementMaster}; |
| 778 kAudioObjectPropertyElementMaster | 889 result = AudioObjectGetPropertyData(device_id, &nominal_sample_rate_address, |
| 779 }; | 890 0, 0, &info_size, &nominal_sample_rate); |
| 780 result = AudioObjectGetPropertyData(device_id, | |
| 781 &nominal_sample_rate_address, | |
| 782 0, | |
| 783 0, | |
| 784 &info_size, | |
| 785 &nominal_sample_rate); | |
| 786 if (result != noErr) | 891 if (result != noErr) |
| 787 return 0.0; | 892 return 0.0; |
| 788 | 893 |
| 789 return static_cast<int>(nominal_sample_rate); | 894 return static_cast<int>(nominal_sample_rate); |
| 790 } | 895 } |
| 791 | 896 |
| 792 double AUAudioInputStream::GetHardwareLatency() { | 897 double AUAudioInputStream::GetHardwareLatency() { |
| 793 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) { | 898 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) { |
| 794 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown"; | 899 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown"; |
| 795 return 0.0; | 900 return 0.0; |
| 796 } | 901 } |
| 797 | 902 |
| 798 // Get audio unit latency. | 903 // Get audio unit latency. |
| 799 Float64 audio_unit_latency_sec = 0.0; | 904 Float64 audio_unit_latency_sec = 0.0; |
| 800 UInt32 size = sizeof(audio_unit_latency_sec); | 905 UInt32 size = sizeof(audio_unit_latency_sec); |
| 801 OSStatus result = AudioUnitGetProperty(audio_unit_, | 906 OSStatus result = AudioUnitGetProperty( |
| 802 kAudioUnitProperty_Latency, | 907 audio_unit_, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, |
| 803 kAudioUnitScope_Global, | 908 &audio_unit_latency_sec, &size); |
| 804 0, | |
| 805 &audio_unit_latency_sec, | |
| 806 &size); | |
| 807 OSSTATUS_DLOG_IF(WARNING, result != noErr, result) | 909 OSSTATUS_DLOG_IF(WARNING, result != noErr, result) |
| 808 << "Could not get audio unit latency"; | 910 << "Could not get audio unit latency"; |
| 809 | 911 |
| 810 // Get input audio device latency. | 912 // Get input audio device latency. |
| 811 AudioObjectPropertyAddress property_address = { | 913 AudioObjectPropertyAddress property_address = { |
| 812 kAudioDevicePropertyLatency, | 914 kAudioDevicePropertyLatency, kAudioDevicePropertyScopeInput, |
| 813 kAudioDevicePropertyScopeInput, | 915 kAudioObjectPropertyElementMaster}; |
| 814 kAudioObjectPropertyElementMaster | |
| 815 }; | |
| 816 UInt32 device_latency_frames = 0; | 916 UInt32 device_latency_frames = 0; |
| 817 size = sizeof(device_latency_frames); | 917 size = sizeof(device_latency_frames); |
| 818 result = AudioObjectGetPropertyData(input_device_id_, &property_address, 0, | 918 result = AudioObjectGetPropertyData(input_device_id_, &property_address, 0, |
| 819 nullptr, &size, &device_latency_frames); | 919 nullptr, &size, &device_latency_frames); |
| 820 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; | 920 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; |
| 821 | 921 |
| 822 return static_cast<double>((audio_unit_latency_sec * | 922 return static_cast<double>((audio_unit_latency_sec * format_.mSampleRate) + |
| 823 format_.mSampleRate) + device_latency_frames); | 923 device_latency_frames); |
| 824 } | 924 } |
| 825 | 925 |
| 826 double AUAudioInputStream::GetCaptureLatency( | 926 double AUAudioInputStream::GetCaptureLatency( |
| 827 const AudioTimeStamp* input_time_stamp) { | 927 const AudioTimeStamp* input_time_stamp) { |
| 828 // Get the delay between between the actual recording instant and the time | 928 // Get the delay between between the actual recording instant and the time |
| 829 // when the data packet is provided as a callback. | 929 // when the data packet is provided as a callback. |
| 830 UInt64 capture_time_ns = AudioConvertHostTimeToNanos( | 930 UInt64 capture_time_ns = |
| 831 input_time_stamp->mHostTime); | 931 AudioConvertHostTimeToNanos(input_time_stamp->mHostTime); |
| 832 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | 932 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); |
| 833 double delay_frames = static_cast<double> | 933 double delay_frames = static_cast<double>(1e-9 * (now_ns - capture_time_ns) * |
| 834 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); | 934 format_.mSampleRate); |
| 835 | 935 |
| 836 // Total latency is composed by the dynamic latency and the fixed | 936 // Total latency is composed by the dynamic latency and the fixed |
| 837 // hardware latency. | 937 // hardware latency. |
| 838 return (delay_frames + hardware_latency_frames_); | 938 return (delay_frames + hardware_latency_frames_); |
| 839 } | 939 } |
| 840 | 940 |
| 841 int AUAudioInputStream::GetNumberOfChannelsFromStream() { | 941 int AUAudioInputStream::GetNumberOfChannelsFromStream() { |
| 842 // Get the stream format, to be able to read the number of channels. | 942 // Get the stream format, to be able to read the number of channels. |
| 843 AudioObjectPropertyAddress property_address = { | 943 AudioObjectPropertyAddress property_address = { |
| 844 kAudioDevicePropertyStreamFormat, | 944 kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, |
| 845 kAudioDevicePropertyScopeInput, | 945 kAudioObjectPropertyElementMaster}; |
| 846 kAudioObjectPropertyElementMaster | |
| 847 }; | |
| 848 AudioStreamBasicDescription stream_format; | 946 AudioStreamBasicDescription stream_format; |
| 849 UInt32 size = sizeof(stream_format); | 947 UInt32 size = sizeof(stream_format); |
| 850 OSStatus result = AudioObjectGetPropertyData( | 948 OSStatus result = AudioObjectGetPropertyData( |
| 851 input_device_id_, &property_address, 0, nullptr, &size, &stream_format); | 949 input_device_id_, &property_address, 0, nullptr, &size, &stream_format); |
| 852 if (result != noErr) { | 950 if (result != noErr) { |
| 853 DLOG(WARNING) << "Could not get stream format"; | 951 DLOG(WARNING) << "Could not get stream format"; |
| 854 return 0; | 952 return 0; |
| 855 } | 953 } |
| 856 | 954 |
| 857 return static_cast<int>(stream_format.mChannelsPerFrame); | 955 return static_cast<int>(stream_format.mChannelsPerFrame); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 870 << "AudioUnitGetProperty(kAudioOutputUnitProperty_IsRunning) failed"; | 968 << "AudioUnitGetProperty(kAudioOutputUnitProperty_IsRunning) failed"; |
| 871 return (error == noErr && is_running); | 969 return (error == noErr && is_running); |
| 872 } | 970 } |
| 873 | 971 |
| 874 void AUAudioInputStream::HandleError(OSStatus err) { | 972 void AUAudioInputStream::HandleError(OSStatus err) { |
| 875 // Log the latest OSStatus error message and also change the sign of the | 973 // Log the latest OSStatus error message and also change the sign of the |
| 876 // error if no callbacks are active. I.e., the sign of the error message | 974 // error if no callbacks are active. I.e., the sign of the error message |
| 877 // carries one extra level of information. | 975 // carries one extra level of information. |
| 878 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.InputErrorMac", | 976 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.InputErrorMac", |
| 879 GetInputCallbackIsActive() ? err : (err * -1)); | 977 GetInputCallbackIsActive() ? err : (err * -1)); |
| 880 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) | 978 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) << " (" << err |
| 881 << " (" << err << ")"; | 979 << ")"; |
| 882 if (sink_) | 980 if (sink_) |
| 883 sink_->OnError(this); | 981 sink_->OnError(this); |
| 884 } | 982 } |
| 885 | 983 |
| 886 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { | 984 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { |
| 887 Boolean is_settable = false; | 985 Boolean is_settable = false; |
| 888 AudioObjectPropertyAddress property_address = { | 986 AudioObjectPropertyAddress property_address = { |
| 889 kAudioDevicePropertyVolumeScalar, | 987 kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, |
| 890 kAudioDevicePropertyScopeInput, | 988 static_cast<UInt32>(channel)}; |
| 891 static_cast<UInt32>(channel) | 989 OSStatus result = AudioObjectIsPropertySettable( |
| 892 }; | 990 input_device_id_, &property_address, &is_settable); |
| 893 OSStatus result = AudioObjectIsPropertySettable(input_device_id_, | |
| 894 &property_address, | |
| 895 &is_settable); | |
| 896 return (result == noErr) ? is_settable : false; | 991 return (result == noErr) ? is_settable : false; |
| 897 } | 992 } |
| 898 | 993 |
| 899 void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) { | 994 void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) { |
| 900 base::subtle::Release_Store(&input_callback_is_active_, enabled); | 995 base::subtle::Release_Store(&input_callback_is_active_, enabled); |
| 901 } | 996 } |
| 902 | 997 |
| 903 bool AUAudioInputStream::GetInputCallbackIsActive() { | 998 bool AUAudioInputStream::GetInputCallbackIsActive() { |
| 904 return (base::subtle::Acquire_Load(&input_callback_is_active_) != false); | 999 return (base::subtle::Acquire_Load(&input_callback_is_active_) != false); |
| 905 } | 1000 } |
| 906 | 1001 |
| 1002 void AUAudioInputStream::SetDeviceListenerIsActive(bool enabled) { | |
| 1003 base::subtle::Release_Store(&device_listener_is_active_, enabled); | |
| 1004 } | |
| 1005 | |
| 1006 bool AUAudioInputStream::GetDeviceListenerIsActive() { | |
| 1007 return (base::subtle::Acquire_Load(&device_listener_is_active_) != false); | |
| 1008 } | |
| 1009 | |
| 907 void AUAudioInputStream::CheckInputStartupSuccess() { | 1010 void AUAudioInputStream::CheckInputStartupSuccess() { |
| 908 DCHECK(thread_checker_.CalledOnValidThread()); | 1011 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1012 // Remove the observer for changes in device properties. The notifier is | |
| 1013 // currently only used to track down events that might lead to failure in | |
| 1014 // starting input audio. Don't keep the observer alive longer than needed. | |
| 1015 DeRegisterDeviceChangeListener(); | |
| 909 // Only add UMA stat related to failing input audio for streams where | 1016 // Only add UMA stat related to failing input audio for streams where |
| 910 // the AGC has been enabled, e.g. WebRTC audio input streams. | 1017 // the AGC has been enabled, e.g. WebRTC audio input streams. |
| 911 if (IsRunning() && GetAutomaticGainControl()) { | 1018 if (IsRunning() && GetAutomaticGainControl()) { |
| 912 // Check if we have called Start() and input callbacks have actually | 1019 // Check if we have called Start() and input callbacks have actually |
| 913 // started in time as they should. If that is not the case, we have a | 1020 // started in time as they should. If that is not the case, we have a |
| 914 // problem and the stream is considered dead. | 1021 // problem and the stream is considered dead. |
| 915 const bool input_callback_is_active = GetInputCallbackIsActive(); | 1022 const bool input_callback_is_active = GetInputCallbackIsActive(); |
| 916 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartupSuccessMac", | 1023 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartupSuccessMac", |
| 917 input_callback_is_active); | 1024 input_callback_is_active); |
| 918 DVLOG(1) << "input_callback_is_active: " << input_callback_is_active; | 1025 DVLOG(1) << "input_callback_is_active: " << input_callback_is_active; |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 943 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredMac", | 1050 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredMac", |
| 944 start_was_deferred_); | 1051 start_was_deferred_); |
| 945 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac", | 1052 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac", |
| 946 buffer_size_was_changed_); | 1053 buffer_size_was_changed_); |
| 947 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfOutputStreamsMac", | 1054 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfOutputStreamsMac", |
| 948 manager_->output_streams()); | 1055 manager_->output_streams()); |
| 949 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfLowLatencyInputStreamsMac", | 1056 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfLowLatencyInputStreamsMac", |
| 950 manager_->low_latency_input_streams()); | 1057 manager_->low_latency_input_streams()); |
| 951 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfBasicInputStreamsMac", | 1058 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfBasicInputStreamsMac", |
| 952 manager_->basic_input_streams()); | 1059 manager_->basic_input_streams()); |
| 953 // |number_of_frames_| is set at construction and corresponds to the requested | 1060 // |number_of_frames_| is set at construction and corresponds to the |
| 954 // (by the client) number of audio frames per I/O buffer connected to the | 1061 // requested (by the client) number of audio frames per I/O buffer connected |
| 955 // selected input device. Ideally, this size will be the same as the native | 1062 // to the selected input device. Ideally, this size will be the same as the |
| 956 // I/O buffer size given by |io_buffer_frame_size_|. | 1063 // native I/O buffer size given by |io_buffer_frame_size_|. |
| 957 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.RequestedInputBufferFrameSizeMac", | 1064 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.RequestedInputBufferFrameSizeMac", |
| 958 number_of_frames_); | 1065 number_of_frames_); |
| 959 DVLOG(1) << "number_of_frames_: " << number_of_frames_; | 1066 DVLOG(1) << "number_of_frames_: " << number_of_frames_; |
| 960 // This value indicates the number of frames in the IO buffers connected to | 1067 // This value indicates the number of frames in the IO buffers connected |
| 961 // the selected input device. It has been set by the audio manger in Open() | 1068 // to the selected input device. It has been set by the audio manger in |
| 962 // and can be the same as |number_of_frames_|, which is the desired buffer | 1069 // Open() and can be the same as |number_of_frames_|, which is the desired |
| 963 // size. These two values might differ if other streams are using the same | 1070 // buffer size. These two values might differ if other streams are using the |
| 964 // device and any of these streams have asked for a smaller buffer size. | 1071 // same device and any of these streams have asked for a smaller buffer size. |
| 965 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.ActualInputBufferFrameSizeMac", | 1072 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.ActualInputBufferFrameSizeMac", |
| 966 io_buffer_frame_size_); | 1073 io_buffer_frame_size_); |
| 967 DVLOG(1) << "io_buffer_frame_size_: " << io_buffer_frame_size_; | 1074 DVLOG(1) << "io_buffer_frame_size_: " << io_buffer_frame_size_; |
| 968 // TODO(henrika): this value will currently always report true. It should be | 1075 // TODO(henrika): this value will currently always report true. It should |
| 1076 // be | |
| 969 // fixed when we understand the problem better. | 1077 // fixed when we understand the problem better. |
| 970 UMA_HISTOGRAM_BOOLEAN("Media.Audio.AutomaticGainControlMac", | 1078 UMA_HISTOGRAM_BOOLEAN("Media.Audio.AutomaticGainControlMac", |
| 971 GetAutomaticGainControl()); | 1079 GetAutomaticGainControl()); |
| 1080 // TODO(henrika): add comments... | |
| 1081 AddDevicePropertyChangesToUMA(); | |
| 1082 } | |
| 1083 | |
| 1084 void AUAudioInputStream::AddDevicePropertyChangesToUMA() { | |
| 1085 DVLOG(1) << "AddDevicePropertyChangesToUMA"; | |
| 1086 while (!device_property_changes_map_.empty()) { | |
|
o1ka
2016/02/18 13:46:22
You could just iterate through the map from the be
henrika (OOO until Aug 14)
2016/02/18 15:58:03
Thanks. Will change.
| |
| 1087 UInt32 device_property = device_property_changes_map_.begin()->first; | |
| 1088 int change_count = device_property_changes_map_.begin()->second; | |
| 1089 DVLOG(1) << "property: " << device_property << " changed: " << change_count; | |
| 1090 AudioDevicePropertyResult uma_result = PROPERTY_MAX; | |
| 1091 switch (device_property) { | |
| 1092 case kAudioDevicePropertyDeviceHasChanged: | |
| 1093 uma_result = PROPERTY_DEVICE_HAS_CHANGED; | |
| 1094 DVLOG(1) << "kAudioDevicePropertyDeviceHasChanged"; | |
| 1095 break; | |
| 1096 case kAudioDevicePropertyIOStoppedAbnormally: | |
| 1097 uma_result = PROPERTY_IO_STOPPED_ABNORMALLY; | |
| 1098 DVLOG(1) << "kAudioDevicePropertyIOStoppedAbnormally"; | |
| 1099 break; | |
| 1100 case kAudioDevicePropertyHogMode: | |
| 1101 uma_result = PROPERTY_HOG_MODE; | |
| 1102 DVLOG(1) << "kAudioDevicePropertyHogMode"; | |
| 1103 break; | |
| 1104 case kAudioDevicePropertyBufferFrameSize: | |
| 1105 uma_result = PROPERTY_BUFFER_FRAME_SIZE; | |
| 1106 DVLOG(1) << "kAudioDevicePropertyBufferFrameSize"; | |
| 1107 break; | |
| 1108 case kAudioDevicePropertyBufferFrameSizeRange: | |
| 1109 uma_result = PROPERTY_BUFFER_FRAME_SIZE_RANGE; | |
| 1110 DVLOG(1) << "kAudioDevicePropertyBufferFrameSizeRange"; | |
| 1111 break; | |
| 1112 case kAudioDevicePropertyStreamConfiguration: | |
| 1113 uma_result = PROPERTY_STREAM_CONFIGURATION; | |
| 1114 DVLOG(1) << "kAudioDevicePropertyStreamConfiguration"; | |
| 1115 break; | |
| 1116 case kAudioDevicePropertyActualSampleRate: | |
| 1117 uma_result = PROPERTY_ACTUAL_SAMPLE_RATE; | |
| 1118 DVLOG(1) << "kAudioDevicePropertyActualSampleRate"; | |
| 1119 break; | |
| 1120 case kAudioDevicePropertyNominalSampleRate: | |
| 1121 uma_result = PROPERTY_NOMINAL_SAMPLE_RATE; | |
| 1122 DVLOG(1) << "kAudioDevicePropertyNominalSampleRate"; | |
| 1123 break; | |
| 1124 case kAudioDevicePropertyDeviceIsRunningSomewhere: | |
| 1125 uma_result = PROPERTY_DEVICE_IS_RUNNING_SOMEWHERE; | |
| 1126 DVLOG(1) << "kAudioDevicePropertyDeviceIsRunningSomewhere"; | |
| 1127 break; | |
| 1128 case kAudioDevicePropertyDeviceIsRunning: | |
| 1129 uma_result = PROPERTY_DEVICE_IS_RUNNING; | |
| 1130 DVLOG(1) << "kAudioDevicePropertyDeviceIsRunning"; | |
| 1131 break; | |
| 1132 case kAudioDevicePropertyDeviceIsAlive: | |
| 1133 uma_result = PROPERTY_DEVICE_IS_ALIVE; | |
| 1134 DVLOG(1) << "kAudioDevicePropertyDeviceIsAlive"; | |
| 1135 break; | |
| 1136 default: | |
| 1137 LOG(ERROR) << "Invalid device property change"; | |
| 1138 break; | |
| 1139 } | |
| 1140 LogDevicePropertyChange(uma_result); | |
| 1141 device_property_changes_map_.erase(device_property_changes_map_.begin()); | |
| 1142 } | |
| 972 } | 1143 } |
| 973 | 1144 |
| 974 } // namespace media | 1145 } // namespace media |
| OLD | NEW |