Commit ed22b40d authored by Guido Urdaneta's avatar Guido Urdaneta Committed by Commit Bot

Use device sources as label in audio device enumerations on Mac

On Mac, audio devices may have an active source.
For example, the built-in audio device might be using the internal
speakers or headphones (same device, different sources).
This CL changes audio device enumerations so that the device label
uses the source instead of the device name if the source is available.

The reason is that the source (together with the transport, added in
crrev.com/c/980951) is a better description of the device.

Some examples:

Before: "Built-in Microphone"
Now: "External Microphone (Built-in)" or "Internal Microphone (Built-in)"

Before: "Built-in Output"
Now: "Internal Speakers (Built-in)" or "Headphones (Built-in)"

Before: "HDMI"
Now: "HP Z30i (HDMI)"

This change also requires firing the devicechange event if the source for
a device changes, since that results in a change to the enumeration results.
This CL includes the required changes to AudioDeviceListenerMac.

Bug: 811687
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: Ieb6801fe979cfed528036862471203f5d29e4603
Reviewed-on: https://chromium-review.googlesource.com/984312
Commit-Queue: Guido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#547662}
parent e6436901
...@@ -4,18 +4,20 @@ ...@@ -4,18 +4,20 @@
#include "media/audio/mac/audio_device_listener_mac.h" #include "media/audio/mac/audio_device_listener_mac.h"
#include <utility> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/mac_logging.h" #include "base/mac/mac_logging.h"
#include "base/message_loop/message_loop.h" #include "base/optional.h"
#include "base/pending_task.h" #include "base/single_thread_task_runner.h"
#include "media/audio/audio_manager.h"
#include "media/audio/mac/core_audio_util_mac.h"
#include "media/base/bind_to_current_loop.h"
namespace media { namespace media {
// Property property to monitor for device changes.
const AudioObjectPropertyAddress const AudioObjectPropertyAddress
AudioDeviceListenerMac::kDefaultOutputDeviceChangePropertyAddress = { AudioDeviceListenerMac::kDefaultOutputDeviceChangePropertyAddress = {
kAudioHardwarePropertyDefaultOutputDevice, kAudioHardwarePropertyDefaultOutputDevice,
...@@ -31,18 +33,31 @@ const AudioObjectPropertyAddress ...@@ -31,18 +33,31 @@ const AudioObjectPropertyAddress
kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster}; kAudioObjectPropertyElementMaster};
const AudioObjectPropertyAddress kPropertyOutputSourceChanged = {
kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster};
const AudioObjectPropertyAddress kPropertyInputSourceChanged = {
kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster};
class AudioDeviceListenerMac::PropertyListener { class AudioDeviceListenerMac::PropertyListener {
public: public:
PropertyListener(const AudioObjectPropertyAddress* property, PropertyListener(AudioObjectID monitored_object,
AudioDeviceListenerMac* device_listener) const AudioObjectPropertyAddress* property,
: address_(property), device_listener_(device_listener) {} base::RepeatingClosure callback)
: monitored_object_(monitored_object),
address_(property),
callback_(std::move(callback)) {}
base::RepeatingClosure& callback() { return device_listener_->listener_cb_; } AudioObjectID monitored_object() const { return monitored_object_; }
const AudioObjectPropertyAddress* property() { return address_; } const base::RepeatingClosure& callback() const { return callback_; }
const AudioObjectPropertyAddress* property() const { return address_; }
private: private:
AudioObjectID monitored_object_;
const AudioObjectPropertyAddress* address_; const AudioObjectPropertyAddress* address_;
AudioDeviceListenerMac* device_listener_; base::RepeatingClosure callback_;
}; };
// Callback from the system when an event occurs; this must be called on the // Callback from the system when an event occurs; this must be called on the
...@@ -53,10 +68,10 @@ OSStatus AudioDeviceListenerMac::OnEvent( ...@@ -53,10 +68,10 @@ OSStatus AudioDeviceListenerMac::OnEvent(
UInt32 num_addresses, UInt32 num_addresses,
const AudioObjectPropertyAddress addresses[], const AudioObjectPropertyAddress addresses[],
void* context) { void* context) {
if (object != kAudioObjectSystemObject) PropertyListener* listener = static_cast<PropertyListener*>(context);
if (object != listener->monitored_object())
return noErr; return noErr;
PropertyListener* listener = static_cast<PropertyListener*>(context);
for (UInt32 i = 0; i < num_addresses; ++i) { for (UInt32 i = 0; i < num_addresses; ++i) {
if (addresses[i].mSelector == listener->property()->mSelector && if (addresses[i].mSelector == listener->property()->mSelector &&
addresses[i].mScope == listener->property()->mScope && addresses[i].mScope == listener->property()->mScope &&
...@@ -72,27 +87,38 @@ OSStatus AudioDeviceListenerMac::OnEvent( ...@@ -72,27 +87,38 @@ OSStatus AudioDeviceListenerMac::OnEvent(
AudioDeviceListenerMac::AudioDeviceListenerMac( AudioDeviceListenerMac::AudioDeviceListenerMac(
const base::RepeatingClosure listener_cb, const base::RepeatingClosure listener_cb,
bool monitor_default_input, bool monitor_default_input,
bool monitor_addition_removal) { bool monitor_addition_removal,
bool monitor_sources)
: weak_factory_(this) {
listener_cb_ = std::move(listener_cb); listener_cb_ = std::move(listener_cb);
// Changes to the default output device are always monitored. // Changes to the default output device are always monitored.
default_output_listener_ = std::make_unique<PropertyListener>( default_output_listener_ = std::make_unique<PropertyListener>(
&kDefaultOutputDeviceChangePropertyAddress, this); kAudioObjectSystemObject, &kDefaultOutputDeviceChangePropertyAddress,
listener_cb_);
if (!AddPropertyListener(default_output_listener_.get())) if (!AddPropertyListener(default_output_listener_.get()))
default_output_listener_.reset(); default_output_listener_.reset();
if (monitor_default_input) { if (monitor_default_input) {
default_input_listener_ = std::make_unique<PropertyListener>( default_input_listener_ = std::make_unique<PropertyListener>(
&kDefaultInputDeviceChangePropertyAddress, this); kAudioObjectSystemObject, &kDefaultInputDeviceChangePropertyAddress,
listener_cb_);
if (!AddPropertyListener(default_input_listener_.get())) if (!AddPropertyListener(default_input_listener_.get()))
default_input_listener_.reset(); default_input_listener_.reset();
} }
if (monitor_addition_removal) { if (monitor_addition_removal) {
addition_removal_listener_ = addition_removal_listener_ = std::make_unique<PropertyListener>(
std::make_unique<PropertyListener>(&kDevicesPropertyAddress, this); kAudioObjectSystemObject, &kDevicesPropertyAddress,
monitor_sources ? media::BindToCurrentLoop(base::BindRepeating(
&AudioDeviceListenerMac::OnDevicesAddedOrRemoved,
weak_factory_.GetWeakPtr()))
: listener_cb_);
if (!AddPropertyListener(addition_removal_listener_.get())) if (!AddPropertyListener(addition_removal_listener_.get()))
addition_removal_listener_.reset(); addition_removal_listener_.reset();
// Sources can be monitored only if addition/removal is monitored.
if (monitor_sources)
UpdateSourceListeners();
} }
} }
...@@ -111,8 +137,9 @@ AudioDeviceListenerMac::~AudioDeviceListenerMac() { ...@@ -111,8 +137,9 @@ AudioDeviceListenerMac::~AudioDeviceListenerMac() {
bool AudioDeviceListenerMac::AddPropertyListener( bool AudioDeviceListenerMac::AddPropertyListener(
AudioDeviceListenerMac::PropertyListener* property_listener) { AudioDeviceListenerMac::PropertyListener* property_listener) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
OSStatus result = AudioObjectAddPropertyListener( OSStatus result = AudioObjectAddPropertyListener(
kAudioObjectSystemObject, property_listener->property(), property_listener->monitored_object(), property_listener->property(),
&AudioDeviceListenerMac::OnEvent, property_listener); &AudioDeviceListenerMac::OnEvent, property_listener);
bool success = result == noErr; bool success = result == noErr;
if (!success) if (!success)
...@@ -123,11 +150,53 @@ bool AudioDeviceListenerMac::AddPropertyListener( ...@@ -123,11 +150,53 @@ bool AudioDeviceListenerMac::AddPropertyListener(
void AudioDeviceListenerMac::RemovePropertyListener( void AudioDeviceListenerMac::RemovePropertyListener(
AudioDeviceListenerMac::PropertyListener* property_listener) { AudioDeviceListenerMac::PropertyListener* property_listener) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
OSStatus result = AudioObjectRemovePropertyListener( OSStatus result = AudioObjectRemovePropertyListener(
kAudioObjectSystemObject, property_listener->property(), property_listener->monitored_object(), property_listener->property(),
&AudioDeviceListenerMac::OnEvent, property_listener); &AudioDeviceListenerMac::OnEvent, property_listener);
OSSTATUS_DLOG_IF(ERROR, result != noErr, result) OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
<< "AudioObjectRemovePropertyListener() failed!"; << "AudioObjectRemovePropertyListener() failed!";
} }
void AudioDeviceListenerMac::OnDevicesAddedOrRemoved() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
UpdateSourceListeners();
listener_cb_.Run();
}
void AudioDeviceListenerMac::UpdateSourceListeners() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::vector<AudioObjectID> device_ids =
core_audio_mac::GetAllAudioDeviceIDs();
for (bool is_input : {true, false}) {
for (auto device_id : device_ids) {
const AudioObjectPropertyAddress* property_address =
is_input ? &kPropertyInputSourceChanged
: &kPropertyOutputSourceChanged;
SourceListenerKey key = {device_id, is_input};
auto it_key = source_listeners_.find(key);
bool is_monitored = it_key != source_listeners_.end();
if (core_audio_mac::GetDeviceSource(device_id, is_input)) {
if (!is_monitored) {
// Start monitoring if the device has source and is not currently
// being monitored.
std::unique_ptr<PropertyListener> source_listener =
std::make_unique<PropertyListener>(device_id, property_address,
listener_cb_);
if (AddPropertyListener(source_listener.get())) {
source_listeners_[key] = std::move(source_listener);
} else {
source_listener.reset();
}
}
} else if (is_monitored) {
// Stop monitoring if the device has no source but is currently being
// monitored.
RemovePropertyListener(it_key->second.get());
source_listeners_.erase(it_key);
}
}
}
}
} // namespace media } // namespace media
...@@ -7,10 +7,14 @@ ...@@ -7,10 +7,14 @@
#include <CoreAudio/AudioHardware.h> #include <CoreAudio/AudioHardware.h>
#include <map>
#include <memory> #include <memory>
#include <utility>
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/flat_map.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "media/base/media_export.h" #include "media/base/media_export.h"
...@@ -25,7 +29,8 @@ class MEDIA_EXPORT AudioDeviceListenerMac { ...@@ -25,7 +29,8 @@ class MEDIA_EXPORT AudioDeviceListenerMac {
// might not be executed on the same thread as construction. // might not be executed on the same thread as construction.
AudioDeviceListenerMac(base::RepeatingClosure listener_cb, AudioDeviceListenerMac(base::RepeatingClosure listener_cb,
bool monitor_default_input = false, bool monitor_default_input = false,
bool monitor_addition_removal = false); bool monitor_addition_removal = false,
bool monitor_sources = false);
~AudioDeviceListenerMac(); ~AudioDeviceListenerMac();
private: private:
...@@ -44,16 +49,25 @@ class MEDIA_EXPORT AudioDeviceListenerMac { ...@@ -44,16 +49,25 @@ class MEDIA_EXPORT AudioDeviceListenerMac {
bool AddPropertyListener(PropertyListener* property_listener); bool AddPropertyListener(PropertyListener* property_listener);
void RemovePropertyListener(PropertyListener* property_listener); void RemovePropertyListener(PropertyListener* property_listener);
void OnDevicesAddedOrRemoved();
void UpdateSourceListeners();
base::RepeatingClosure listener_cb_; base::RepeatingClosure listener_cb_;
std::unique_ptr<PropertyListener> default_output_listener_; std::unique_ptr<PropertyListener> default_output_listener_;
std::unique_ptr<PropertyListener> default_input_listener_; std::unique_ptr<PropertyListener> default_input_listener_;
std::unique_ptr<PropertyListener> addition_removal_listener_; std::unique_ptr<PropertyListener> addition_removal_listener_;
using SourceListenerKey = std::pair<AudioObjectID, bool>;
using SourceListenerMap =
base::flat_map<SourceListenerKey, std::unique_ptr<PropertyListener>>;
SourceListenerMap source_listeners_;
// AudioDeviceListenerMac must be constructed and destructed on the same // AudioDeviceListenerMac must be constructed and destructed on the same
// thread. // thread.
THREAD_CHECKER(thread_checker_); THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<AudioDeviceListenerMac> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AudioDeviceListenerMac); DISALLOW_COPY_AND_ASSIGN(AudioDeviceListenerMac);
}; };
......
...@@ -63,12 +63,9 @@ base::Optional<uint32_t> GetDeviceUint32Property( ...@@ -63,12 +63,9 @@ base::Optional<uint32_t> GetDeviceUint32Property(
OSStatus result = AudioObjectGetPropertyData( OSStatus result = AudioObjectGetPropertyData(
device_id, &property_address, 0 /* inQualifierDataSize */, device_id, &property_address, 0 /* inQualifierDataSize */,
nullptr /* inQualifierData */, &size, &property_value); nullptr /* inQualifierData */, &size, &property_value);
if (result != noErr) { if (result != noErr)
OSSTATUS_DLOG(WARNING, result)
<< "Failed to read UInt32 property " << property_selector
<< " for device " << device_id;
return base::nullopt; return base::nullopt;
}
return property_value; return property_value;
} }
...@@ -189,6 +186,33 @@ std::string TransportTypeToString(uint32_t transport_type) { ...@@ -189,6 +186,33 @@ std::string TransportTypeToString(uint32_t transport_type) {
} }
} }
base::Optional<std::string> TranslateDeviceSource(AudioObjectID device_id,
UInt32 source_id,
bool is_input) {
CFStringRef source_name = nullptr;
AudioValueTranslation translation;
translation.mInputData = &source_id;
translation.mInputDataSize = sizeof(source_id);
translation.mOutputData = &source_name;
translation.mOutputDataSize = sizeof(source_name);
UInt32 translation_size = sizeof(AudioValueTranslation);
AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyDataSourceNameForIDCFString,
InputOutputScope(is_input), kAudioObjectPropertyElementMaster};
OSStatus result = AudioObjectGetPropertyData(
device_id, &property_address, 0 /* inQualifierDataSize */,
nullptr /* inQualifierData */, &translation_size, &translation);
if (result)
return base::nullopt;
std::string ret = base::SysCFStringRefToUTF8(source_name);
CFRelease(source_name);
return ret;
}
} // namespace } // namespace
std::vector<AudioObjectID> GetAllAudioDeviceIDs() { std::vector<AudioObjectID> GetAllAudioDeviceIDs() {
...@@ -206,9 +230,17 @@ base::Optional<std::string> GetDeviceUniqueID(AudioObjectID device_id) { ...@@ -206,9 +230,17 @@ base::Optional<std::string> GetDeviceUniqueID(AudioObjectID device_id) {
base::Optional<std::string> GetDeviceLabel(AudioObjectID device_id, base::Optional<std::string> GetDeviceLabel(AudioObjectID device_id,
bool is_input) { bool is_input) {
base::Optional<std::string> device_label = GetDeviceName(device_id); base::Optional<std::string> device_label;
if (!device_label) base::Optional<uint32_t> source = GetDeviceSource(device_id, is_input);
return base::nullopt; if (source) {
device_label = TranslateDeviceSource(device_id, *source, is_input);
}
if (!device_label) {
device_label = GetDeviceName(device_id);
if (!device_label)
return base::nullopt;
}
std::string suffix; std::string suffix;
base::Optional<uint32_t> transport_type = GetDeviceTransportType(device_id); base::Optional<uint32_t> transport_type = GetDeviceTransportType(device_id);
...@@ -223,6 +255,7 @@ base::Optional<std::string> GetDeviceLabel(AudioObjectID device_id, ...@@ -223,6 +255,7 @@ base::Optional<std::string> GetDeviceLabel(AudioObjectID device_id,
} }
} }
DCHECK(device_label);
if (!suffix.empty()) if (!suffix.empty())
*device_label += " (" + suffix + ")"; *device_label += " (" + suffix + ")";
...@@ -234,5 +267,11 @@ uint32_t GetNumStreams(AudioObjectID device_id, bool is_input) { ...@@ -234,5 +267,11 @@ uint32_t GetNumStreams(AudioObjectID device_id, bool is_input) {
InputOutputScope(is_input)); InputOutputScope(is_input));
} }
base::Optional<uint32_t> GetDeviceSource(AudioObjectID device_id,
bool is_input) {
return GetDeviceUint32Property(device_id, kAudioDevicePropertyDataSource,
InputOutputScope(is_input));
}
} // namespace core_audio_mac } // namespace core_audio_mac
} // namespace media } // namespace media
...@@ -38,6 +38,11 @@ base::Optional<std::string> GetDeviceLabel(AudioObjectID device_id, ...@@ -38,6 +38,11 @@ base::Optional<std::string> GetDeviceLabel(AudioObjectID device_id,
// |device_id|. Returns zero if there are no streams or if there is an error. // |device_id|. Returns zero if there are no streams or if there is an error.
uint32_t GetNumStreams(AudioObjectID device_id, bool is_input); uint32_t GetNumStreams(AudioObjectID device_id, bool is_input);
// Returns the source associated with the given |device_id|, or no value if
// |device_id| has no source or if there is an error.
base::Optional<uint32_t> GetDeviceSource(AudioObjectID device_id,
bool is_input);
} // namespace core_audio_mac } // namespace core_audio_mac
} // namespace media } // namespace media
......
...@@ -451,7 +451,8 @@ void DeviceMonitorMac::StartMonitoring() { ...@@ -451,7 +451,8 @@ void DeviceMonitorMac::StartMonitoring() {
base::SystemMonitor::DEVTYPE_AUDIO); base::SystemMonitor::DEVTYPE_AUDIO);
} }
}), }),
true /* monitor_default_input */, true /* monitor_addition_removal */); true /* monitor_default_input */, true /* monitor_addition_removal */,
true /* monitor_sources */);
} }
void DeviceMonitorMac::NotifyDeviceChanged( void DeviceMonitorMac::NotifyDeviceChanged(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment