summaryrefslogtreecommitdiffstats
path: root/media/audio/mac/audio_manager_mac.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/audio/mac/audio_manager_mac.cc')
-rw-r--r--media/audio/mac/audio_manager_mac.cc87
1 files changed, 69 insertions, 18 deletions
diff --git a/media/audio/mac/audio_manager_mac.cc b/media/audio/mac/audio_manager_mac.cc
index 288ac14..e0300ed 100644
--- a/media/audio/mac/audio_manager_mac.cc
+++ b/media/audio/mac/audio_manager_mac.cc
@@ -233,20 +233,74 @@ static AudioDeviceID GetAudioDeviceIdByUId(bool is_input,
return audio_device_id;
}
-AudioManagerMac::AudioManagerMac() {
+// Property address to monitor for device changes.
+static const AudioObjectPropertyAddress kDeviceChangePropertyAddress = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+};
+
+// Callback from the system when the default device changes; this must be called
+// on the MessageLoop that created the AudioManager.
+static OSStatus OnDefaultDeviceChangedCallback(
+ AudioObjectID object,
+ UInt32 num_addresses,
+ const AudioObjectPropertyAddress addresses[],
+ void* context) {
+ if (object != kAudioObjectSystemObject)
+ return noErr;
+
+ for (UInt32 i = 0; i < num_addresses; ++i) {
+ if (addresses[i].mSelector == kDeviceChangePropertyAddress.mSelector &&
+ addresses[i].mScope == kDeviceChangePropertyAddress.mScope &&
+ addresses[i].mElement == kDeviceChangePropertyAddress.mElement &&
+ context) {
+ static_cast<AudioManagerMac*>(context)->OnDeviceChange();
+ break;
+ }
+ }
+
+ return noErr;
+}
+
+AudioManagerMac::AudioManagerMac()
+ : listener_registered_(false),
+ creating_message_loop_(base::MessageLoopProxy::current()) {
SetMaxOutputStreamsAllowed(kMaxOutputStreams);
- // Task must be posted last to avoid races from handing out "this" to the
- // audio thread.
- GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
- &AudioManagerMac::CreateDeviceListener, base::Unretained(this)));
+ // AudioManagerMac is expected to be created by the root platform thread, this
+ // is generally BrowserMainLoop, it's MessageLoop will drive the NSApplication
+ // pump which in turn fires the property listener callbacks.
+ if (!creating_message_loop_)
+ return;
+
+ OSStatus result = AudioObjectAddPropertyListener(
+ kAudioObjectSystemObject,
+ &kDeviceChangePropertyAddress,
+ &OnDefaultDeviceChangedCallback,
+ this);
+
+ if (result != noErr) {
+ OSSTATUS_DLOG(ERROR, result) << "AudioObjectAddPropertyListener() failed!";
+ return;
+ }
+
+ listener_registered_ = true;
}
AudioManagerMac::~AudioManagerMac() {
- // It's safe to post a task here since Shutdown() will wait for all tasks to
- // complete before returning.
- GetMessageLoop()->PostTask(FROM_HERE, base::Bind(
- &AudioManagerMac::DestroyDeviceListener, base::Unretained(this)));
+ if (listener_registered_) {
+ // TODO(dalecurtis): CHECK destruction happens on |creating_message_loop_|,
+ // should be true, but currently several unit tests perform destruction in
+ // odd places so we can't CHECK here currently.
+ OSStatus result = AudioObjectRemovePropertyListener(
+ kAudioObjectSystemObject,
+ &kDeviceChangePropertyAddress,
+ &OnDefaultDeviceChangedCallback,
+ this);
+ OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
+ << "AudioObjectRemovePropertyListener() failed!";
+ }
Shutdown();
}
@@ -335,17 +389,14 @@ AudioParameters AudioManagerMac::GetPreferredLowLatencyOutputStreamParameters(
input_params);
}
-void AudioManagerMac::CreateDeviceListener() {
- DCHECK(GetMessageLoop()->BelongsToCurrentThread());
- output_device_listener_.reset(new AudioDeviceListenerMac(BindToLoop(
+void AudioManagerMac::OnDeviceChange() {
+ // Post the task to the |creating_message_loop_| to execute our listener
+ // callback. The callback is created using BindToLoop() so will hop over
+ // to the audio thread upon execution.
+ creating_message_loop_->PostTask(FROM_HERE, BindToLoop(
GetMessageLoop(), base::Bind(
&AudioManagerMac::NotifyAllOutputDeviceChangeListeners,
- base::Unretained(this)))));
-}
-
-void AudioManagerMac::DestroyDeviceListener() {
- DCHECK(GetMessageLoop()->BelongsToCurrentThread());
- output_device_listener_.reset();
+ base::Unretained(this))));
}
AudioManager* CreateAudioManager() {