Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(875)

Unified Diff: media/audio/mac/audio_low_latency_input_mac.cc

Issue 1695303002: Adds Media.Audio.InputDevicePropertyChangedMac UMA stat on Mac (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Modified when device notification is started and stopped Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/audio/mac/audio_low_latency_input_mac.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/audio/mac/audio_low_latency_input_mac.cc
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc
index 4157c22396e222c7a35d54379f3e093365302012..8bec69da94700b1ddc783da27a1198ffecb56223 100644
--- a/media/audio/mac/audio_low_latency_input_mac.cc
+++ b/media/audio/mac/audio_low_latency_input_mac.cc
@@ -58,6 +58,36 @@ static std::ostream& operator<<(std::ostream& os,
return os;
}
+// Property address to monitor device changes. Use wildcards to match any and
+// all values for their associated type. Filtering for device-specific
+// notifications will take place in the callback.
+const AudioObjectPropertyAddress
+ AUAudioInputStream::kDeviceChangePropertyAddress = {
+ kAudioObjectPropertySelectorWildcard, kAudioObjectPropertyScopeWildcard,
+ kAudioObjectPropertyElementWildcard};
+
+// TODO(henrika): add comments...
+enum AudioDevicePropertyResult {
+ PROPERTY_DEVICE_HAS_CHANGED = 0,
+ 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.
+ PROPERTY_HOG_MODE = 2,
+ PROPERTY_BUFFER_FRAME_SIZE = 3,
+ PROPERTY_BUFFER_FRAME_SIZE_RANGE = 4,
+ PROPERTY_STREAM_CONFIGURATION = 5,
+ PROPERTY_ACTUAL_SAMPLE_RATE = 6,
+ PROPERTY_NOMINAL_SAMPLE_RATE = 7,
+ PROPERTY_DEVICE_IS_RUNNING_SOMEWHERE = 8,
+ PROPERTY_DEVICE_IS_RUNNING = 9,
+ PROPERTY_DEVICE_IS_ALIVE = 10,
+ PROPERTY_MAX = PROPERTY_DEVICE_IS_ALIVE
+};
+
+// TODO(henrika): add comments...
+static void LogDevicePropertyChange(AudioDevicePropertyResult result) {
+ UMA_HISTOGRAM_ENUMERATION("Media.Audio.InputDevicePropertyChangedMac", result,
+ PROPERTY_MAX + 1);
+}
+
static OSStatus GetInputDeviceStreamFormat(
AudioUnit audio_unit,
AudioStreamBasicDescription* format) {
@@ -93,7 +123,8 @@ AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager,
input_callback_is_active_(false),
start_was_deferred_(false),
buffer_size_was_changed_(false),
- audio_unit_render_has_worked_(false) {
+ audio_unit_render_has_worked_(false),
+ device_listener_is_active_(false) {
DCHECK(manager_);
// Set up the desired (output) format specified by the client.
@@ -135,6 +166,7 @@ AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager,
AUAudioInputStream::~AUAudioInputStream() {
DVLOG(1) << "~dtor";
+ DeRegisterDeviceChangeListener();
}
// Obtain and open the AUHAL AudioOutputUnit for recording.
@@ -151,6 +183,8 @@ bool AUAudioInputStream::Open() {
return false;
}
+ RegisterDeviceChangeListener();
+
// The requested sample-rate must match the hardware sample-rate.
int sample_rate =
AudioManagerMac::HardwareSampleRateForDevice(input_device_id_);
@@ -428,6 +462,7 @@ void AUAudioInputStream::Close() {
Stop();
}
CloseAudioUnit();
+ DeRegisterDeviceChangeListener();
// Inform the audio manager that we have been closed. This will cause our
// destruction.
manager_->ReleaseInputStream(this);
@@ -612,6 +647,10 @@ OSStatus AUAudioInputStream::OnDataIsAvailable(
// be safe to modify.
SetInputCallbackIsActive(true);
+ // We are only interested in device property changes in combination with
+ // failing input callbacks. Hence, might as well disable the listener now.
+ DeRegisterDeviceChangeListener();
+
// Update the |mDataByteSize| value in the audio_buffer_list() since
// |number_of_frames| can be changed on the fly.
// |mDataByteSize| needs to be exactly mapping to |number_of_frames|,
@@ -750,22 +789,94 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
return noErr;
}
+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)
+ AudioObjectID object_id,
+ UInt32 num_addresses,
+ const AudioObjectPropertyAddress addresses[],
+ void* context) {
+ AUAudioInputStream* self = static_cast<AUAudioInputStream*>(context);
+ return self->DevicePropertyChanged(object_id, num_addresses, addresses);
+}
+
+OSStatus AUAudioInputStream::DevicePropertyChanged(
+ AudioObjectID object_id,
+ UInt32 num_addresses,
+ const AudioObjectPropertyAddress addresses[]) {
+ if (object_id != input_device_id_)
+ return noErr;
+
+ // 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
+ // Consequently, the implementation of a listener must go through the array of
+ // addresses to see what exactly has changed.
+ for (UInt32 i = 0; i < num_addresses; ++i) {
+ const UInt32 property = addresses[i].mSelector;
+ switch (property) {
+ // Filter out and store certain property values provided by the
+ // AudioDevice class.
+ 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
+ case kAudioDevicePropertyIOStoppedAbnormally:
+ case kAudioDevicePropertyHogMode:
+ case kAudioDevicePropertyBufferFrameSize:
+ case kAudioDevicePropertyBufferFrameSizeRange:
+ case kAudioDevicePropertyStreamConfiguration:
+ case kAudioDevicePropertyActualSampleRate:
+ case kAudioDevicePropertyNominalSampleRate:
+ case kAudioDevicePropertyDeviceIsRunningSomewhere:
+ case kAudioDevicePropertyDeviceIsRunning:
+ case kAudioDevicePropertyDeviceIsAlive:
+ // Use selector as key to a map and increase its value.
+ device_property_changes_map_[property]++;
+ break;
+ default:
+ // TODO(henrika): figure out if more property changes should be tracked.
+ DVLOG(1) << "No action for kAudioDeviceProperty: " << property;
+ break;
+ }
+ }
+ return noErr;
+}
+
+void AUAudioInputStream::RegisterDeviceChangeListener() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DVLOG(1) << "RegisterDeviceChangeListener";
+ if (input_device_id_ == kAudioObjectUnknown)
+ return;
+
+ device_property_changes_map_.clear();
+
+ OSStatus result = AudioObjectAddPropertyListener(
+ input_device_id_, &kDeviceChangePropertyAddress,
+ &AUAudioInputStream::OnDevicePropertyChanged, this);
+ OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+ << "AudioObjectAddPropertyListener() failed!";
+ SetDeviceListenerIsActive(result == noErr);
+}
+
+void AUAudioInputStream::DeRegisterDeviceChangeListener() {
+ if (!GetDeviceListenerIsActive())
+ return;
+ DVLOG(1) << "DeRegisterDeviceChangeListener";
+ if (input_device_id_ == kAudioObjectUnknown)
+ return;
+ 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.
+ input_device_id_, &kDeviceChangePropertyAddress,
+ &AUAudioInputStream::OnDevicePropertyChanged, this);
+ OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+ << "AudioObjectRemovePropertyListener() failed!";
+ SetDeviceListenerIsActive(!(result == noErr));
+}
+
int AUAudioInputStream::HardwareSampleRate() {
// Determine the default input device's sample-rate.
AudioDeviceID device_id = kAudioObjectUnknown;
UInt32 info_size = sizeof(device_id);
AudioObjectPropertyAddress default_input_device_address = {
- kAudioHardwarePropertyDefaultInputDevice,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster
- };
+ kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster};
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &default_input_device_address,
- 0,
- 0,
- &info_size,
- &device_id);
+ &default_input_device_address, 0,
+ 0, &info_size, &device_id);
if (result != noErr)
return 0.0;
@@ -773,16 +884,10 @@ int AUAudioInputStream::HardwareSampleRate() {
info_size = sizeof(nominal_sample_rate);
AudioObjectPropertyAddress nominal_sample_rate_address = {
- kAudioDevicePropertyNominalSampleRate,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster
- };
- result = AudioObjectGetPropertyData(device_id,
- &nominal_sample_rate_address,
- 0,
- 0,
- &info_size,
- &nominal_sample_rate);
+ kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster};
+ result = AudioObjectGetPropertyData(device_id, &nominal_sample_rate_address,
+ 0, 0, &info_size, &nominal_sample_rate);
if (result != noErr)
return 0.0;
@@ -798,40 +903,35 @@ double AUAudioInputStream::GetHardwareLatency() {
// Get audio unit latency.
Float64 audio_unit_latency_sec = 0.0;
UInt32 size = sizeof(audio_unit_latency_sec);
- OSStatus result = AudioUnitGetProperty(audio_unit_,
- kAudioUnitProperty_Latency,
- kAudioUnitScope_Global,
- 0,
- &audio_unit_latency_sec,
- &size);
+ OSStatus result = AudioUnitGetProperty(
+ audio_unit_, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0,
+ &audio_unit_latency_sec, &size);
OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
<< "Could not get audio unit latency";
// Get input audio device latency.
AudioObjectPropertyAddress property_address = {
- kAudioDevicePropertyLatency,
- kAudioDevicePropertyScopeInput,
- kAudioObjectPropertyElementMaster
- };
+ kAudioDevicePropertyLatency, kAudioDevicePropertyScopeInput,
+ kAudioObjectPropertyElementMaster};
UInt32 device_latency_frames = 0;
size = sizeof(device_latency_frames);
result = AudioObjectGetPropertyData(input_device_id_, &property_address, 0,
nullptr, &size, &device_latency_frames);
DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
- return static_cast<double>((audio_unit_latency_sec *
- format_.mSampleRate) + device_latency_frames);
+ return static_cast<double>((audio_unit_latency_sec * format_.mSampleRate) +
+ device_latency_frames);
}
double AUAudioInputStream::GetCaptureLatency(
const AudioTimeStamp* input_time_stamp) {
// Get the delay between between the actual recording instant and the time
// when the data packet is provided as a callback.
- UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
- input_time_stamp->mHostTime);
+ UInt64 capture_time_ns =
+ AudioConvertHostTimeToNanos(input_time_stamp->mHostTime);
UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
- double delay_frames = static_cast<double>
- (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
+ double delay_frames = static_cast<double>(1e-9 * (now_ns - capture_time_ns) *
+ format_.mSampleRate);
// Total latency is composed by the dynamic latency and the fixed
// hardware latency.
@@ -841,10 +941,8 @@ double AUAudioInputStream::GetCaptureLatency(
int AUAudioInputStream::GetNumberOfChannelsFromStream() {
// Get the stream format, to be able to read the number of channels.
AudioObjectPropertyAddress property_address = {
- kAudioDevicePropertyStreamFormat,
- kAudioDevicePropertyScopeInput,
- kAudioObjectPropertyElementMaster
- };
+ kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput,
+ kAudioObjectPropertyElementMaster};
AudioStreamBasicDescription stream_format;
UInt32 size = sizeof(stream_format);
OSStatus result = AudioObjectGetPropertyData(
@@ -877,8 +975,8 @@ void AUAudioInputStream::HandleError(OSStatus err) {
// carries one extra level of information.
UMA_HISTOGRAM_SPARSE_SLOWLY("Media.InputErrorMac",
GetInputCallbackIsActive() ? err : (err * -1));
- NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
- << " (" << err << ")";
+ NOTREACHED() << "error " << GetMacOSStatusErrorString(err) << " (" << err
+ << ")";
if (sink_)
sink_->OnError(this);
}
@@ -886,13 +984,10 @@ void AUAudioInputStream::HandleError(OSStatus err) {
bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
Boolean is_settable = false;
AudioObjectPropertyAddress property_address = {
- kAudioDevicePropertyVolumeScalar,
- kAudioDevicePropertyScopeInput,
- static_cast<UInt32>(channel)
- };
- OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
- &property_address,
- &is_settable);
+ kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput,
+ static_cast<UInt32>(channel)};
+ OSStatus result = AudioObjectIsPropertySettable(
+ input_device_id_, &property_address, &is_settable);
return (result == noErr) ? is_settable : false;
}
@@ -904,8 +999,20 @@ bool AUAudioInputStream::GetInputCallbackIsActive() {
return (base::subtle::Acquire_Load(&input_callback_is_active_) != false);
}
+void AUAudioInputStream::SetDeviceListenerIsActive(bool enabled) {
+ base::subtle::Release_Store(&device_listener_is_active_, enabled);
+}
+
+bool AUAudioInputStream::GetDeviceListenerIsActive() {
+ return (base::subtle::Acquire_Load(&device_listener_is_active_) != false);
+}
+
void AUAudioInputStream::CheckInputStartupSuccess() {
DCHECK(thread_checker_.CalledOnValidThread());
+ // Remove the observer for changes in device properties. The notifier is
+ // currently only used to track down events that might lead to failure in
+ // starting input audio. Don't keep the observer alive longer than needed.
+ DeRegisterDeviceChangeListener();
// Only add UMA stat related to failing input audio for streams where
// the AGC has been enabled, e.g. WebRTC audio input streams.
if (IsRunning() && GetAutomaticGainControl()) {
@@ -950,25 +1057,89 @@ void AUAudioInputStream::AddHistogramsForFailedStartup() {
manager_->low_latency_input_streams());
UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfBasicInputStreamsMac",
manager_->basic_input_streams());
- // |number_of_frames_| is set at construction and corresponds to the requested
- // (by the client) number of audio frames per I/O buffer connected to the
- // selected input device. Ideally, this size will be the same as the native
- // I/O buffer size given by |io_buffer_frame_size_|.
+ // |number_of_frames_| is set at construction and corresponds to the
+ // requested (by the client) number of audio frames per I/O buffer connected
+ // to the selected input device. Ideally, this size will be the same as the
+ // native I/O buffer size given by |io_buffer_frame_size_|.
UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.RequestedInputBufferFrameSizeMac",
number_of_frames_);
DVLOG(1) << "number_of_frames_: " << number_of_frames_;
- // This value indicates the number of frames in the IO buffers connected to
- // the selected input device. It has been set by the audio manger in Open()
- // and can be the same as |number_of_frames_|, which is the desired buffer
- // size. These two values might differ if other streams are using the same
- // device and any of these streams have asked for a smaller buffer size.
+ // This value indicates the number of frames in the IO buffers connected
+ // to the selected input device. It has been set by the audio manger in
+ // Open() and can be the same as |number_of_frames_|, which is the desired
+ // buffer size. These two values might differ if other streams are using the
+ // same device and any of these streams have asked for a smaller buffer size.
UMA_HISTOGRAM_SPARSE_SLOWLY("Media.Audio.ActualInputBufferFrameSizeMac",
io_buffer_frame_size_);
DVLOG(1) << "io_buffer_frame_size_: " << io_buffer_frame_size_;
- // TODO(henrika): this value will currently always report true. It should be
+ // TODO(henrika): this value will currently always report true. It should
+ // be
// fixed when we understand the problem better.
UMA_HISTOGRAM_BOOLEAN("Media.Audio.AutomaticGainControlMac",
GetAutomaticGainControl());
+ // TODO(henrika): add comments...
+ AddDevicePropertyChangesToUMA();
+}
+
+void AUAudioInputStream::AddDevicePropertyChangesToUMA() {
+ DVLOG(1) << "AddDevicePropertyChangesToUMA";
+ 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.
+ UInt32 device_property = device_property_changes_map_.begin()->first;
+ int change_count = device_property_changes_map_.begin()->second;
+ DVLOG(1) << "property: " << device_property << " changed: " << change_count;
+ AudioDevicePropertyResult uma_result = PROPERTY_MAX;
+ switch (device_property) {
+ case kAudioDevicePropertyDeviceHasChanged:
+ uma_result = PROPERTY_DEVICE_HAS_CHANGED;
+ DVLOG(1) << "kAudioDevicePropertyDeviceHasChanged";
+ break;
+ case kAudioDevicePropertyIOStoppedAbnormally:
+ uma_result = PROPERTY_IO_STOPPED_ABNORMALLY;
+ DVLOG(1) << "kAudioDevicePropertyIOStoppedAbnormally";
+ break;
+ case kAudioDevicePropertyHogMode:
+ uma_result = PROPERTY_HOG_MODE;
+ DVLOG(1) << "kAudioDevicePropertyHogMode";
+ break;
+ case kAudioDevicePropertyBufferFrameSize:
+ uma_result = PROPERTY_BUFFER_FRAME_SIZE;
+ DVLOG(1) << "kAudioDevicePropertyBufferFrameSize";
+ break;
+ case kAudioDevicePropertyBufferFrameSizeRange:
+ uma_result = PROPERTY_BUFFER_FRAME_SIZE_RANGE;
+ DVLOG(1) << "kAudioDevicePropertyBufferFrameSizeRange";
+ break;
+ case kAudioDevicePropertyStreamConfiguration:
+ uma_result = PROPERTY_STREAM_CONFIGURATION;
+ DVLOG(1) << "kAudioDevicePropertyStreamConfiguration";
+ break;
+ case kAudioDevicePropertyActualSampleRate:
+ uma_result = PROPERTY_ACTUAL_SAMPLE_RATE;
+ DVLOG(1) << "kAudioDevicePropertyActualSampleRate";
+ break;
+ case kAudioDevicePropertyNominalSampleRate:
+ uma_result = PROPERTY_NOMINAL_SAMPLE_RATE;
+ DVLOG(1) << "kAudioDevicePropertyNominalSampleRate";
+ break;
+ case kAudioDevicePropertyDeviceIsRunningSomewhere:
+ uma_result = PROPERTY_DEVICE_IS_RUNNING_SOMEWHERE;
+ DVLOG(1) << "kAudioDevicePropertyDeviceIsRunningSomewhere";
+ break;
+ case kAudioDevicePropertyDeviceIsRunning:
+ uma_result = PROPERTY_DEVICE_IS_RUNNING;
+ DVLOG(1) << "kAudioDevicePropertyDeviceIsRunning";
+ break;
+ case kAudioDevicePropertyDeviceIsAlive:
+ uma_result = PROPERTY_DEVICE_IS_ALIVE;
+ DVLOG(1) << "kAudioDevicePropertyDeviceIsAlive";
+ break;
+ default:
+ LOG(ERROR) << "Invalid device property change";
+ break;
+ }
+ LogDevicePropertyChange(uma_result);
+ device_property_changes_map_.erase(device_property_changes_map_.begin());
+ }
}
} // namespace media
« no previous file with comments | « media/audio/mac/audio_low_latency_input_mac.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698