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 | |
| 7 #include <CoreServices/CoreServices.h> | 6 #include <CoreServices/CoreServices.h> |
| 8 | 7 |
| 9 #include "base/logging.h" | 8 #include "base/logging.h" |
| 10 #include "base/mac/mac_logging.h" | 9 #include "base/mac/mac_logging.h" |
| 11 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
| 12 #include "base/metrics/sparse_histogram.h" | 11 #include "base/metrics/sparse_histogram.h" |
| 13 #include "base/time/time.h" | 12 #include "base/time/time.h" |
| 14 #include "media/audio/mac/audio_manager_mac.h" | 13 #include "media/audio/mac/audio_manager_mac.h" |
| 15 #include "media/base/audio_bus.h" | 14 #include "media/base/audio_bus.h" |
| 16 #include "media/base/data_buffer.h" | 15 #include "media/base/data_buffer.h" |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 29 // Media.Audio.InputStartupSuccessMac is then updated where true is added | 28 // Media.Audio.InputStartupSuccessMac is then updated where true is added |
| 30 // if input callbacks have started, and false otherwise. | 29 // if input callbacks have started, and false otherwise. |
| 31 const int kInputCallbackStartTimeoutInSeconds = 5; | 30 const int kInputCallbackStartTimeoutInSeconds = 5; |
| 32 | 31 |
| 33 // Returns true if the format flags in |format_flags| has the "non-interleaved" | 32 // Returns true if the format flags in |format_flags| has the "non-interleaved" |
| 34 // flag (kAudioFormatFlagIsNonInterleaved) cleared (set to 0). | 33 // flag (kAudioFormatFlagIsNonInterleaved) cleared (set to 0). |
| 35 static bool FormatIsInterleaved(UInt32 format_flags) { | 34 static bool FormatIsInterleaved(UInt32 format_flags) { |
| 36 return !(format_flags & kAudioFormatFlagIsNonInterleaved); | 35 return !(format_flags & kAudioFormatFlagIsNonInterleaved); |
| 37 } | 36 } |
| 38 | 37 |
| 38 // Converts the 32-bit non-terminated 4 byte string into an std::string. | |
| 39 // Example: code=1735354734 <=> 'goin' <=> kAudioDevicePropertyDeviceIsRunning. | |
| 40 static std::string FourCharFormatCodeToString(UInt32 code) { | |
| 41 char code_string[5]; | |
| 42 // Converts a 32-bit integer from the host’s native byte order to big-endian. | |
| 43 UInt32 code_id = CFSwapInt32HostToBig(code); | |
| 44 bcopy(&code_id, code_string, 4); | |
| 45 code_string[4] = '\0'; | |
| 46 return std::string(code_string); | |
| 47 } | |
| 48 | |
| 39 static std::ostream& operator<<(std::ostream& os, | 49 static std::ostream& operator<<(std::ostream& os, |
| 40 const AudioStreamBasicDescription& format) { | 50 const AudioStreamBasicDescription& format) { |
| 41 // The 32-bit integer format.mFormatID is actually a non-terminated 4 byte | 51 std::string format_string = FourCharFormatCodeToString(format.mFormatID); |
| 42 // string. Example: kAudioFormatLinearPCM = 'lpcm'. | |
| 43 char format_id_string[5]; | |
| 44 // Converts a 32-bit integer from the host’s native byte order to big-endian. | |
| 45 UInt32 format_id = CFSwapInt32HostToBig(format.mFormatID); | |
| 46 bcopy(&format_id, format_id_string, 4); | |
| 47 os << "sample rate : " << format.mSampleRate << std::endl | 52 os << "sample rate : " << format.mSampleRate << std::endl |
| 48 << "format ID : " << format_id_string << std::endl | 53 << "format ID : " << format_string << std::endl |
| 49 << "format flags : " << format.mFormatFlags << std::endl | 54 << "format flags : " << format.mFormatFlags << std::endl |
| 50 << "bytes per packet : " << format.mBytesPerPacket << std::endl | 55 << "bytes per packet : " << format.mBytesPerPacket << std::endl |
| 51 << "frames per packet : " << format.mFramesPerPacket << std::endl | 56 << "frames per packet : " << format.mFramesPerPacket << std::endl |
| 52 << "bytes per frame : " << format.mBytesPerFrame << std::endl | 57 << "bytes per frame : " << format.mBytesPerFrame << std::endl |
| 53 << "channels per frame: " << format.mChannelsPerFrame << std::endl | 58 << "channels per frame: " << format.mChannelsPerFrame << std::endl |
| 54 << "bits per channel : " << format.mBitsPerChannel << std::endl | 59 << "bits per channel : " << format.mBitsPerChannel << std::endl |
| 55 << "reserved : " << format.mReserved << std::endl | 60 << "reserved : " << format.mReserved << std::endl |
| 56 << "interleaved : " | 61 << "interleaved : " |
| 57 << (FormatIsInterleaved(format.mFormatFlags) ? "yes" : "no"); | 62 << (FormatIsInterleaved(format.mFormatFlags) ? "yes" : "no"); |
| 58 return os; | 63 return os; |
| 59 } | 64 } |
| 60 | 65 |
| 66 // Property address to monitor device changes. Use wildcards to match any and | |
| 67 // all values for their associated type. Filtering for device-specific | |
| 68 // notifications will take place in the callback. | |
| 69 const AudioObjectPropertyAddress | |
| 70 AUAudioInputStream::kDeviceChangePropertyAddress = { | |
| 71 kAudioObjectPropertySelectorWildcard, kAudioObjectPropertyScopeWildcard, | |
| 72 kAudioObjectPropertyElementWildcard}; | |
| 73 | |
| 74 // Maps internal enumerator values (e.g. kAudioDevicePropertyDeviceHasChanged) | |
| 75 // into local values that are suitable for UMA stats. | |
| 76 // See AudioObjectPropertySelector in CoreAudio/AudioHardware.h for details. | |
| 77 enum AudioDevicePropertyResult { | |
| 78 PROPERTY_OTHER = 0, // Use for all non-supported property changes | |
| 79 PROPERTY_DEVICE_HAS_CHANGED = 1, | |
| 80 PROPERTY_IO_STOPPED_ABNORMALLY = 2, | |
| 81 PROPERTY_HOG_MODE = 3, | |
| 82 PROPERTY_BUFFER_FRAME_SIZE = 4, | |
| 83 PROPERTY_BUFFER_FRAME_SIZE_RANGE = 5, | |
| 84 PROPERTY_STREAM_CONFIGURATION = 6, | |
| 85 PROPERTY_ACTUAL_SAMPLE_RATE = 7, | |
| 86 PROPERTY_NOMINAL_SAMPLE_RATE = 8, | |
| 87 PROPERTY_DEVICE_IS_RUNNING_SOMEWHERE = 9, | |
| 88 PROPERTY_DEVICE_IS_RUNNING = 10, | |
| 89 PROPERTY_DEVICE_IS_ALIVE = 11, | |
| 90 PROPERTY_STREAM_PHYSICAL_FORMAT = 12, | |
| 91 PROPERTY_MAX = PROPERTY_STREAM_PHYSICAL_FORMAT | |
| 92 }; | |
| 93 | |
| 94 // Add the provided value in |result| to a UMA histogram. | |
| 95 static void LogDevicePropertyChange(bool startup_failed, | |
| 96 AudioDevicePropertyResult result) { | |
| 97 if (startup_failed) { | |
| 98 UMA_HISTOGRAM_ENUMERATION( | |
| 99 "Media.Audio.InputDevicePropertyChangedStartupFailedMac", result, | |
| 100 PROPERTY_MAX + 1); | |
| 101 } else { | |
| 102 UMA_HISTOGRAM_ENUMERATION("Media.Audio.InputDevicePropertyChangedMac", | |
| 103 result, PROPERTY_MAX + 1); | |
| 104 } | |
| 105 } | |
| 106 | |
| 61 static OSStatus GetInputDeviceStreamFormat( | 107 static OSStatus GetInputDeviceStreamFormat( |
| 62 AudioUnit audio_unit, | 108 AudioUnit audio_unit, |
| 63 AudioStreamBasicDescription* format) { | 109 AudioStreamBasicDescription* format) { |
| 64 DCHECK(audio_unit); | 110 DCHECK(audio_unit); |
| 65 UInt32 property_size = sizeof(*format); | 111 UInt32 property_size = sizeof(*format); |
| 66 // Get the audio stream data format on the input scope of the input element | 112 // Get the audio stream data format on the input scope of the input element |
| 67 // since it is connected to the current input device. | 113 // since it is connected to the current input device. |
| 68 OSStatus result = | 114 OSStatus result = |
| 69 AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, | 115 AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, |
| 70 kAudioUnitScope_Input, 1, format, &property_size); | 116 kAudioUnitScope_Input, 1, format, &property_size); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 86 audio_unit_(0), | 132 audio_unit_(0), |
| 87 input_device_id_(audio_device_id), | 133 input_device_id_(audio_device_id), |
| 88 hardware_latency_frames_(0), | 134 hardware_latency_frames_(0), |
| 89 number_of_channels_in_frame_(0), | 135 number_of_channels_in_frame_(0), |
| 90 fifo_(input_params.channels(), | 136 fifo_(input_params.channels(), |
| 91 number_of_frames_, | 137 number_of_frames_, |
| 92 kNumberOfBlocksBufferInFifo), | 138 kNumberOfBlocksBufferInFifo), |
| 93 input_callback_is_active_(false), | 139 input_callback_is_active_(false), |
| 94 start_was_deferred_(false), | 140 start_was_deferred_(false), |
| 95 buffer_size_was_changed_(false), | 141 buffer_size_was_changed_(false), |
| 96 audio_unit_render_has_worked_(false) { | 142 audio_unit_render_has_worked_(false), |
| 143 device_listener_is_active_(false) { | |
| 97 DCHECK(manager_); | 144 DCHECK(manager_); |
| 98 | 145 |
| 99 // Set up the desired (output) format specified by the client. | 146 // Set up the desired (output) format specified by the client. |
| 100 format_.mSampleRate = input_params.sample_rate(); | 147 format_.mSampleRate = input_params.sample_rate(); |
| 101 format_.mFormatID = kAudioFormatLinearPCM; | 148 format_.mFormatID = kAudioFormatLinearPCM; |
| 102 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | | 149 format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | |
| 103 kLinearPCMFormatFlagIsSignedInteger; | 150 kLinearPCMFormatFlagIsSignedInteger; |
| 104 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); | 151 DCHECK(FormatIsInterleaved(format_.mFormatFlags)); |
| 105 format_.mBitsPerChannel = input_params.bits_per_sample(); | 152 format_.mBitsPerChannel = input_params.bits_per_sample(); |
| 106 format_.mChannelsPerFrame = input_params.channels(); | 153 format_.mChannelsPerFrame = input_params.channels(); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 128 audio_buffer_list_.mNumberBuffers = 1; | 175 audio_buffer_list_.mNumberBuffers = 1; |
| 129 | 176 |
| 130 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; | 177 AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; |
| 131 audio_buffer->mNumberChannels = input_params.channels(); | 178 audio_buffer->mNumberChannels = input_params.channels(); |
| 132 audio_buffer->mDataByteSize = data_byte_size; | 179 audio_buffer->mDataByteSize = data_byte_size; |
| 133 audio_buffer->mData = audio_data_buffer_.get(); | 180 audio_buffer->mData = audio_data_buffer_.get(); |
| 134 } | 181 } |
| 135 | 182 |
| 136 AUAudioInputStream::~AUAudioInputStream() { | 183 AUAudioInputStream::~AUAudioInputStream() { |
| 137 DVLOG(1) << "~dtor"; | 184 DVLOG(1) << "~dtor"; |
| 185 DCHECK(!device_listener_is_active_); | |
| 138 } | 186 } |
| 139 | 187 |
| 140 // Obtain and open the AUHAL AudioOutputUnit for recording. | 188 // Obtain and open the AUHAL AudioOutputUnit for recording. |
| 141 bool AUAudioInputStream::Open() { | 189 bool AUAudioInputStream::Open() { |
| 142 DCHECK(thread_checker_.CalledOnValidThread()); | 190 DCHECK(thread_checker_.CalledOnValidThread()); |
| 143 DVLOG(1) << "Open"; | 191 DVLOG(1) << "Open"; |
| 144 DCHECK(!audio_unit_); | 192 DCHECK(!audio_unit_); |
| 145 | 193 |
| 146 // Verify that we have a valid device. Send appropriate error code to | 194 // 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. | 195 // HandleError() to ensure that the error type is added to UMA stats. |
| 148 if (input_device_id_ == kAudioObjectUnknown) { | 196 if (input_device_id_ == kAudioObjectUnknown) { |
| 149 NOTREACHED() << "Device ID is unknown"; | 197 NOTREACHED() << "Device ID is unknown"; |
| 150 HandleError(kAudioUnitErr_InvalidElement); | 198 HandleError(kAudioUnitErr_InvalidElement); |
| 151 return false; | 199 return false; |
| 152 } | 200 } |
| 153 | 201 |
| 202 // Start listening for changes in device properties. | |
| 203 RegisterDeviceChangeListener(); | |
| 204 | |
| 154 // The requested sample-rate must match the hardware sample-rate. | 205 // The requested sample-rate must match the hardware sample-rate. |
| 155 int sample_rate = | 206 int sample_rate = |
| 156 AudioManagerMac::HardwareSampleRateForDevice(input_device_id_); | 207 AudioManagerMac::HardwareSampleRateForDevice(input_device_id_); |
| 157 DCHECK_EQ(sample_rate, format_.mSampleRate); | 208 DCHECK_EQ(sample_rate, format_.mSampleRate); |
| 158 | 209 |
| 159 // Start by obtaining an AudioOuputUnit using an AUHAL component description. | 210 // Start by obtaining an AudioOuputUnit using an AUHAL component description. |
| 160 | 211 |
| 161 // Description for the Audio Unit we want to use (AUHAL in this case). | 212 // Description for the Audio Unit we want to use (AUHAL in this case). |
| 162 // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device. | 213 // The kAudioUnitSubType_HALOutput audio unit interfaces to any audio device. |
| 163 // The user specifies which audio device to track. The audio unit can do | 214 // The user specifies which audio device to track. The audio unit can do |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 420 } | 471 } |
| 421 | 472 |
| 422 void AUAudioInputStream::Close() { | 473 void AUAudioInputStream::Close() { |
| 423 DCHECK(thread_checker_.CalledOnValidThread()); | 474 DCHECK(thread_checker_.CalledOnValidThread()); |
| 424 DVLOG(1) << "Close"; | 475 DVLOG(1) << "Close"; |
| 425 // It is valid to call Close() before calling open or Start(). | 476 // It is valid to call Close() before calling open or Start(). |
| 426 // It is also valid to call Close() after Start() has been called. | 477 // It is also valid to call Close() after Start() has been called. |
| 427 if (IsRunning()) { | 478 if (IsRunning()) { |
| 428 Stop(); | 479 Stop(); |
| 429 } | 480 } |
| 481 // Uninitialize and dispose the audio unit. | |
| 430 CloseAudioUnit(); | 482 CloseAudioUnit(); |
| 483 // Disable the listener for device property changes. | |
| 484 DeRegisterDeviceChangeListener(); | |
| 485 // Check if any device property changes are added by filtering out a selected | |
| 486 // set of the |device_property_changes_map_| map. Add UMA stats if valuable | |
| 487 // data is found. | |
| 488 AddDevicePropertyChangesToUMA(false); | |
| 431 // Inform the audio manager that we have been closed. This will cause our | 489 // Inform the audio manager that we have been closed. This will cause our |
| 432 // destruction. | 490 // destruction. |
| 433 manager_->ReleaseInputStream(this); | 491 manager_->ReleaseInputStream(this); |
| 434 } | 492 } |
| 435 | 493 |
| 436 double AUAudioInputStream::GetMaxVolume() { | 494 double AUAudioInputStream::GetMaxVolume() { |
| 437 // Verify that we have a valid device. | 495 // Verify that we have a valid device. |
| 438 if (input_device_id_ == kAudioObjectUnknown) { | 496 if (input_device_id_ == kAudioObjectUnknown) { |
| 439 NOTREACHED() << "Device ID is unknown"; | 497 NOTREACHED() << "Device ID is unknown"; |
| 440 return 0.0; | 498 return 0.0; |
| (...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 743 DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_)); | 801 DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_)); |
| 744 | 802 |
| 745 // Compensate the audio delay caused by the FIFO. | 803 // Compensate the audio delay caused by the FIFO. |
| 746 capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame; | 804 capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame; |
| 747 sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume); | 805 sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume); |
| 748 } | 806 } |
| 749 | 807 |
| 750 return noErr; | 808 return noErr; |
| 751 } | 809 } |
| 752 | 810 |
| 811 OSStatus AUAudioInputStream::OnDevicePropertyChanged( | |
| 812 AudioObjectID object_id, | |
| 813 UInt32 num_addresses, | |
| 814 const AudioObjectPropertyAddress addresses[], | |
| 815 void* context) { | |
| 816 AUAudioInputStream* self = static_cast<AUAudioInputStream*>(context); | |
| 817 return self->DevicePropertyChanged(object_id, num_addresses, addresses); | |
| 818 } | |
| 819 | |
| 820 OSStatus AUAudioInputStream::DevicePropertyChanged( | |
|
tommi (sloooow) - chröme
2016/02/23 16:24:59
can we add a thread checker that we detach before
henrika (OOO until Aug 14)
2016/02/24 09:49:45
Thanks but I can't since it will not happen on the
| |
| 821 AudioObjectID object_id, | |
| 822 UInt32 num_addresses, | |
| 823 const AudioObjectPropertyAddress addresses[]) { | |
| 824 if (object_id != input_device_id_) | |
| 825 return noErr; | |
| 826 | |
| 827 // Listeners will be called when possibly many properties have changed. | |
| 828 // Consequently, the implementation of a listener must go through the array of | |
| 829 // addresses to see what exactly has changed. | |
| 830 for (UInt32 i = 0; i < num_addresses; ++i) { | |
| 831 const UInt32 property = addresses[i].mSelector; | |
| 832 // Use selector as key to a map and increase its value. We are not | |
| 833 // interested in all property changes but store all here anyhow. | |
| 834 // Filtering will be done later in AddDevicePropertyChangesToUMA(); | |
| 835 ++device_property_changes_map_[property]; | |
| 836 } | |
| 837 return noErr; | |
| 838 } | |
| 839 | |
| 840 void AUAudioInputStream::RegisterDeviceChangeListener() { | |
| 841 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 842 DCHECK(!device_listener_is_active_); | |
| 843 DVLOG(1) << "RegisterDeviceChangeListener"; | |
| 844 if (input_device_id_ == kAudioObjectUnknown) | |
| 845 return; | |
| 846 device_property_changes_map_.clear(); | |
| 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 device_listener_is_active_ = true; | |
|
tommi (sloooow) - chröme
2016/02/23 16:24:59
should this be:
device_listener_is_active_ = (res
henrika (OOO until Aug 14)
2016/02/24 09:49:46
Done.
| |
| 853 } | |
| 854 | |
| 855 void AUAudioInputStream::DeRegisterDeviceChangeListener() { | |
| 856 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 857 if (!device_listener_is_active_) | |
| 858 return; | |
| 859 DVLOG(1) << "DeRegisterDeviceChangeListener"; | |
| 860 if (input_device_id_ == kAudioObjectUnknown) | |
| 861 return; | |
| 862 device_listener_is_active_ = false; | |
| 863 OSStatus result = AudioObjectRemovePropertyListener( | |
| 864 input_device_id_, &kDeviceChangePropertyAddress, | |
| 865 &AUAudioInputStream::OnDevicePropertyChanged, this); | |
| 866 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | |
| 867 << "AudioObjectRemovePropertyListener() failed!"; | |
| 868 } | |
| 869 | |
| 753 int AUAudioInputStream::HardwareSampleRate() { | 870 int AUAudioInputStream::HardwareSampleRate() { |
| 754 // Determine the default input device's sample-rate. | 871 // Determine the default input device's sample-rate. |
| 755 AudioDeviceID device_id = kAudioObjectUnknown; | 872 AudioDeviceID device_id = kAudioObjectUnknown; |
| 756 UInt32 info_size = sizeof(device_id); | 873 UInt32 info_size = sizeof(device_id); |
| 757 | 874 |
| 758 AudioObjectPropertyAddress default_input_device_address = { | 875 AudioObjectPropertyAddress default_input_device_address = { |
| 759 kAudioHardwarePropertyDefaultInputDevice, | 876 kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, |
| 760 kAudioObjectPropertyScopeGlobal, | 877 kAudioObjectPropertyElementMaster}; |
| 761 kAudioObjectPropertyElementMaster | |
| 762 }; | |
| 763 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, | 878 OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, |
| 764 &default_input_device_address, | 879 &default_input_device_address, 0, |
| 765 0, | 880 0, &info_size, &device_id); |
| 766 0, | |
| 767 &info_size, | |
| 768 &device_id); | |
| 769 if (result != noErr) | 881 if (result != noErr) |
| 770 return 0.0; | 882 return 0.0; |
| 771 | 883 |
| 772 Float64 nominal_sample_rate; | 884 Float64 nominal_sample_rate; |
| 773 info_size = sizeof(nominal_sample_rate); | 885 info_size = sizeof(nominal_sample_rate); |
| 774 | 886 |
| 775 AudioObjectPropertyAddress nominal_sample_rate_address = { | 887 AudioObjectPropertyAddress nominal_sample_rate_address = { |
| 776 kAudioDevicePropertyNominalSampleRate, | 888 kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, |
| 777 kAudioObjectPropertyScopeGlobal, | 889 kAudioObjectPropertyElementMaster}; |
| 778 kAudioObjectPropertyElementMaster | 890 result = AudioObjectGetPropertyData(device_id, &nominal_sample_rate_address, |
| 779 }; | 891 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) | 892 if (result != noErr) |
| 787 return 0.0; | 893 return 0.0; |
| 788 | 894 |
| 789 return static_cast<int>(nominal_sample_rate); | 895 return static_cast<int>(nominal_sample_rate); |
| 790 } | 896 } |
| 791 | 897 |
| 792 double AUAudioInputStream::GetHardwareLatency() { | 898 double AUAudioInputStream::GetHardwareLatency() { |
| 793 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) { | 899 if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) { |
| 794 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown"; | 900 DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown"; |
| 795 return 0.0; | 901 return 0.0; |
| 796 } | 902 } |
| 797 | 903 |
| 798 // Get audio unit latency. | 904 // Get audio unit latency. |
| 799 Float64 audio_unit_latency_sec = 0.0; | 905 Float64 audio_unit_latency_sec = 0.0; |
| 800 UInt32 size = sizeof(audio_unit_latency_sec); | 906 UInt32 size = sizeof(audio_unit_latency_sec); |
| 801 OSStatus result = AudioUnitGetProperty(audio_unit_, | 907 OSStatus result = AudioUnitGetProperty( |
| 802 kAudioUnitProperty_Latency, | 908 audio_unit_, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, |
| 803 kAudioUnitScope_Global, | 909 &audio_unit_latency_sec, &size); |
| 804 0, | |
| 805 &audio_unit_latency_sec, | |
| 806 &size); | |
| 807 OSSTATUS_DLOG_IF(WARNING, result != noErr, result) | 910 OSSTATUS_DLOG_IF(WARNING, result != noErr, result) |
| 808 << "Could not get audio unit latency"; | 911 << "Could not get audio unit latency"; |
| 809 | 912 |
| 810 // Get input audio device latency. | 913 // Get input audio device latency. |
| 811 AudioObjectPropertyAddress property_address = { | 914 AudioObjectPropertyAddress property_address = { |
| 812 kAudioDevicePropertyLatency, | 915 kAudioDevicePropertyLatency, kAudioDevicePropertyScopeInput, |
| 813 kAudioDevicePropertyScopeInput, | 916 kAudioObjectPropertyElementMaster}; |
| 814 kAudioObjectPropertyElementMaster | |
| 815 }; | |
| 816 UInt32 device_latency_frames = 0; | 917 UInt32 device_latency_frames = 0; |
| 817 size = sizeof(device_latency_frames); | 918 size = sizeof(device_latency_frames); |
| 818 result = AudioObjectGetPropertyData(input_device_id_, &property_address, 0, | 919 result = AudioObjectGetPropertyData(input_device_id_, &property_address, 0, |
| 819 nullptr, &size, &device_latency_frames); | 920 nullptr, &size, &device_latency_frames); |
| 820 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; | 921 DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; |
| 821 | 922 |
| 822 return static_cast<double>((audio_unit_latency_sec * | 923 return static_cast<double>((audio_unit_latency_sec * format_.mSampleRate) + |
| 823 format_.mSampleRate) + device_latency_frames); | 924 device_latency_frames); |
| 824 } | 925 } |
| 825 | 926 |
| 826 double AUAudioInputStream::GetCaptureLatency( | 927 double AUAudioInputStream::GetCaptureLatency( |
| 827 const AudioTimeStamp* input_time_stamp) { | 928 const AudioTimeStamp* input_time_stamp) { |
| 828 // Get the delay between between the actual recording instant and the time | 929 // Get the delay between between the actual recording instant and the time |
| 829 // when the data packet is provided as a callback. | 930 // when the data packet is provided as a callback. |
| 830 UInt64 capture_time_ns = AudioConvertHostTimeToNanos( | 931 UInt64 capture_time_ns = |
| 831 input_time_stamp->mHostTime); | 932 AudioConvertHostTimeToNanos(input_time_stamp->mHostTime); |
| 832 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); | 933 UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); |
| 833 double delay_frames = static_cast<double> | 934 double delay_frames = static_cast<double>(1e-9 * (now_ns - capture_time_ns) * |
| 834 (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); | 935 format_.mSampleRate); |
| 835 | 936 |
| 836 // Total latency is composed by the dynamic latency and the fixed | 937 // Total latency is composed by the dynamic latency and the fixed |
| 837 // hardware latency. | 938 // hardware latency. |
| 838 return (delay_frames + hardware_latency_frames_); | 939 return (delay_frames + hardware_latency_frames_); |
| 839 } | 940 } |
| 840 | 941 |
| 841 int AUAudioInputStream::GetNumberOfChannelsFromStream() { | 942 int AUAudioInputStream::GetNumberOfChannelsFromStream() { |
| 842 // Get the stream format, to be able to read the number of channels. | 943 // Get the stream format, to be able to read the number of channels. |
| 843 AudioObjectPropertyAddress property_address = { | 944 AudioObjectPropertyAddress property_address = { |
| 844 kAudioDevicePropertyStreamFormat, | 945 kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, |
| 845 kAudioDevicePropertyScopeInput, | 946 kAudioObjectPropertyElementMaster}; |
| 846 kAudioObjectPropertyElementMaster | |
| 847 }; | |
| 848 AudioStreamBasicDescription stream_format; | 947 AudioStreamBasicDescription stream_format; |
| 849 UInt32 size = sizeof(stream_format); | 948 UInt32 size = sizeof(stream_format); |
| 850 OSStatus result = AudioObjectGetPropertyData( | 949 OSStatus result = AudioObjectGetPropertyData( |
| 851 input_device_id_, &property_address, 0, nullptr, &size, &stream_format); | 950 input_device_id_, &property_address, 0, nullptr, &size, &stream_format); |
| 852 if (result != noErr) { | 951 if (result != noErr) { |
| 853 DLOG(WARNING) << "Could not get stream format"; | 952 DLOG(WARNING) << "Could not get stream format"; |
| 854 return 0; | 953 return 0; |
| 855 } | 954 } |
| 856 | 955 |
| 857 return static_cast<int>(stream_format.mChannelsPerFrame); | 956 return static_cast<int>(stream_format.mChannelsPerFrame); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 870 << "AudioUnitGetProperty(kAudioOutputUnitProperty_IsRunning) failed"; | 969 << "AudioUnitGetProperty(kAudioOutputUnitProperty_IsRunning) failed"; |
| 871 return (error == noErr && is_running); | 970 return (error == noErr && is_running); |
| 872 } | 971 } |
| 873 | 972 |
| 874 void AUAudioInputStream::HandleError(OSStatus err) { | 973 void AUAudioInputStream::HandleError(OSStatus err) { |
| 875 // Log the latest OSStatus error message and also change the sign of the | 974 // 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 | 975 // error if no callbacks are active. I.e., the sign of the error message |
| 877 // carries one extra level of information. | 976 // carries one extra level of information. |
| 878 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.InputErrorMac", | 977 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.InputErrorMac", |
| 879 GetInputCallbackIsActive() ? err : (err * -1)); | 978 GetInputCallbackIsActive() ? err : (err * -1)); |
| 880 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) | 979 NOTREACHED() << "error " << GetMacOSStatusErrorString(err) << " (" << err |
| 881 << " (" << err << ")"; | 980 << ")"; |
| 882 if (sink_) | 981 if (sink_) |
| 883 sink_->OnError(this); | 982 sink_->OnError(this); |
| 884 } | 983 } |
| 885 | 984 |
| 886 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { | 985 bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { |
| 887 Boolean is_settable = false; | 986 Boolean is_settable = false; |
| 888 AudioObjectPropertyAddress property_address = { | 987 AudioObjectPropertyAddress property_address = { |
| 889 kAudioDevicePropertyVolumeScalar, | 988 kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, |
| 890 kAudioDevicePropertyScopeInput, | 989 static_cast<UInt32>(channel)}; |
| 891 static_cast<UInt32>(channel) | 990 OSStatus result = AudioObjectIsPropertySettable( |
| 892 }; | 991 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; | 992 return (result == noErr) ? is_settable : false; |
| 897 } | 993 } |
| 898 | 994 |
| 899 void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) { | 995 void AUAudioInputStream::SetInputCallbackIsActive(bool enabled) { |
| 900 base::subtle::Release_Store(&input_callback_is_active_, enabled); | 996 base::subtle::Release_Store(&input_callback_is_active_, enabled); |
| 901 } | 997 } |
| 902 | 998 |
| 903 bool AUAudioInputStream::GetInputCallbackIsActive() { | 999 bool AUAudioInputStream::GetInputCallbackIsActive() { |
| 904 return (base::subtle::Acquire_Load(&input_callback_is_active_) != false); | 1000 return (base::subtle::Acquire_Load(&input_callback_is_active_) != false); |
| 905 } | 1001 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 943 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredMac", | 1039 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredMac", |
| 944 start_was_deferred_); | 1040 start_was_deferred_); |
| 945 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac", | 1041 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac", |
| 946 buffer_size_was_changed_); | 1042 buffer_size_was_changed_); |
| 947 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfOutputStreamsMac", | 1043 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfOutputStreamsMac", |
| 948 manager_->output_streams()); | 1044 manager_->output_streams()); |
| 949 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfLowLatencyInputStreamsMac", | 1045 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfLowLatencyInputStreamsMac", |
| 950 manager_->low_latency_input_streams()); | 1046 manager_->low_latency_input_streams()); |
| 951 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfBasicInputStreamsMac", | 1047 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfBasicInputStreamsMac", |
| 952 manager_->basic_input_streams()); | 1048 manager_->basic_input_streams()); |
| 953 // |number_of_frames_| is set at construction and corresponds to the requested | 1049 // |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 | 1050 // 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 | 1051 // 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_|. | 1052 // native I/O buffer size given by |io_buffer_frame_size_|. |
| 957 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.RequestedInputBufferFrameSizeMac", | 1053 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.RequestedInputBufferFrameSizeMac", |
| 958 number_of_frames_); | 1054 number_of_frames_); |
| 959 DVLOG(1) << "number_of_frames_: " << number_of_frames_; | 1055 DVLOG(1) << "number_of_frames_: " << number_of_frames_; |
| 960 // This value indicates the number of frames in the IO buffers connected to | 1056 // 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() | 1057 // 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 | 1058 // 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 | 1059 // 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. | 1060 // same device and any of these streams have asked for a smaller buffer size. |
| 965 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.ActualInputBufferFrameSizeMac", | 1061 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.ActualInputBufferFrameSizeMac", |
| 966 io_buffer_frame_size_); | 1062 io_buffer_frame_size_); |
| 967 DVLOG(1) << "io_buffer_frame_size_: " << io_buffer_frame_size_; | 1063 DVLOG(1) << "io_buffer_frame_size_: " << io_buffer_frame_size_; |
| 968 // TODO(henrika): this value will currently always report true. It should be | 1064 // TODO(henrika): this value will currently always report true. It should |
| 969 // fixed when we understand the problem better. | 1065 // be fixed when we understand the problem better. |
| 970 UMA_HISTOGRAM_BOOLEAN("Media.Audio.AutomaticGainControlMac", | 1066 UMA_HISTOGRAM_BOOLEAN("Media.Audio.AutomaticGainControlMac", |
| 971 GetAutomaticGainControl()); | 1067 GetAutomaticGainControl()); |
| 1068 // Disable the listener for device property changes. Ensures that we don't | |
| 1069 // need a lock when reading the map. | |
| 1070 DeRegisterDeviceChangeListener(); | |
| 1071 // Check if any device property changes are added by filtering out a selected | |
| 1072 // set of the |device_property_changes_map_| map. Add UMA stats if valuable | |
| 1073 // data is found. | |
| 1074 AddDevicePropertyChangesToUMA(true); | |
| 1075 } | |
| 1076 | |
| 1077 void AUAudioInputStream::AddDevicePropertyChangesToUMA(bool startup_failed) { | |
|
tommi (sloooow) - chröme
2016/02/23 16:24:59
dcheck that monitoring is not active?
henrika (OOO until Aug 14)
2016/02/24 09:49:46
Good point. Thanks.
| |
| 1078 DVLOG(1) << "AddDevicePropertyChangesToUMA"; | |
| 1079 // Scan the map of all available property changes (notification types) and | |
| 1080 // filter out some that make sense to add to UMA stats. | |
| 1081 // TODO(henrika): figure out if the set of stats is sufficient or not. | |
| 1082 for (auto it = device_property_changes_map_.begin(); | |
| 1083 it != device_property_changes_map_.end(); ++it) { | |
| 1084 UInt32 device_property = it->first; | |
| 1085 int change_count = it->second; | |
| 1086 AudioDevicePropertyResult uma_result = PROPERTY_OTHER; | |
| 1087 switch (device_property) { | |
| 1088 case kAudioDevicePropertyDeviceHasChanged: | |
| 1089 uma_result = PROPERTY_DEVICE_HAS_CHANGED; | |
| 1090 DVLOG(1) << "kAudioDevicePropertyDeviceHasChanged"; | |
| 1091 break; | |
| 1092 case kAudioDevicePropertyIOStoppedAbnormally: | |
| 1093 uma_result = PROPERTY_IO_STOPPED_ABNORMALLY; | |
| 1094 DVLOG(1) << "kAudioDevicePropertyIOStoppedAbnormally"; | |
| 1095 break; | |
| 1096 case kAudioDevicePropertyHogMode: | |
| 1097 uma_result = PROPERTY_HOG_MODE; | |
| 1098 DVLOG(1) << "kAudioDevicePropertyHogMode"; | |
| 1099 break; | |
| 1100 case kAudioDevicePropertyBufferFrameSize: | |
| 1101 uma_result = PROPERTY_BUFFER_FRAME_SIZE; | |
| 1102 DVLOG(1) << "kAudioDevicePropertyBufferFrameSize"; | |
| 1103 break; | |
| 1104 case kAudioDevicePropertyBufferFrameSizeRange: | |
| 1105 uma_result = PROPERTY_BUFFER_FRAME_SIZE_RANGE; | |
| 1106 DVLOG(1) << "kAudioDevicePropertyBufferFrameSizeRange"; | |
| 1107 break; | |
| 1108 case kAudioDevicePropertyStreamConfiguration: | |
| 1109 uma_result = PROPERTY_STREAM_CONFIGURATION; | |
| 1110 DVLOG(1) << "kAudioDevicePropertyStreamConfiguration"; | |
| 1111 break; | |
| 1112 case kAudioDevicePropertyActualSampleRate: | |
| 1113 uma_result = PROPERTY_ACTUAL_SAMPLE_RATE; | |
| 1114 DVLOG(1) << "kAudioDevicePropertyActualSampleRate"; | |
| 1115 break; | |
| 1116 case kAudioDevicePropertyNominalSampleRate: | |
| 1117 uma_result = PROPERTY_NOMINAL_SAMPLE_RATE; | |
| 1118 DVLOG(1) << "kAudioDevicePropertyNominalSampleRate"; | |
| 1119 break; | |
| 1120 case kAudioDevicePropertyDeviceIsRunningSomewhere: | |
| 1121 uma_result = PROPERTY_DEVICE_IS_RUNNING_SOMEWHERE; | |
| 1122 DVLOG(1) << "kAudioDevicePropertyDeviceIsRunningSomewhere"; | |
| 1123 break; | |
| 1124 case kAudioDevicePropertyDeviceIsRunning: | |
| 1125 uma_result = PROPERTY_DEVICE_IS_RUNNING; | |
| 1126 DVLOG(1) << "kAudioDevicePropertyDeviceIsRunning"; | |
| 1127 break; | |
| 1128 case kAudioDevicePropertyDeviceIsAlive: | |
| 1129 uma_result = PROPERTY_DEVICE_IS_ALIVE; | |
| 1130 DVLOG(1) << "kAudioDevicePropertyDeviceIsAlive"; | |
| 1131 break; | |
| 1132 case kAudioStreamPropertyPhysicalFormat: | |
| 1133 uma_result = PROPERTY_STREAM_PHYSICAL_FORMAT; | |
| 1134 DVLOG(1) << "kAudioStreamPropertyPhysicalFormat"; | |
| 1135 break; | |
| 1136 default: | |
| 1137 uma_result = PROPERTY_OTHER; | |
| 1138 DVLOG(1) << "Property change is ignored"; | |
| 1139 break; | |
| 1140 } | |
| 1141 DVLOG(1) << "property: " << device_property << " (" | |
| 1142 << FourCharFormatCodeToString(device_property) << ")" | |
| 1143 << " changed: " << change_count; | |
| 1144 LogDevicePropertyChange(startup_failed, uma_result); | |
| 1145 } | |
| 1146 device_property_changes_map_.clear(); | |
| 972 } | 1147 } |
| 973 | 1148 |
| 974 } // namespace media | 1149 } // namespace media |
| OLD | NEW |