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