summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authordalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-11 01:59:37 +0000
committerdalecurtis@google.com <dalecurtis@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-11 01:59:37 +0000
commitca8ceaa180917272404e2de7752a2052bd2dbfbf (patch)
tree3e4b8c1f025125b5875a8228296fabfdc3f2a1bd /media
parent9bb9f99499ea9e60b3269c9e6053b5a3af1d51eb (diff)
downloadchromium_src-ca8ceaa180917272404e2de7752a2052bd2dbfbf.zip
chromium_src-ca8ceaa180917272404e2de7752a2052bd2dbfbf.tar.gz
chromium_src-ca8ceaa180917272404e2de7752a2052bd2dbfbf.tar.bz2
Revert 176228
> Improve thread safety for device changes on OSX. > > Device changes are hard, lets go shopping! Sadly OSX does not handle property > listener callbacks in a thread safe manner. On 10.6 we can set > kAudioHardwarePropertyRunLoop to account for this. On 10.7 this is broken > and we need to create a dispatch queue to drive callbacks. However code > running on the dispatch queue must be mutually exclusive with code running > on the audio thread. > > ExclusiveDispatchQueueTaskObserver works around this by pausing and resuming > the dispatch queue before and after each pumped task. Benchmarks on my retina > 10.8.2 macbook pro show on average this takes < 30us in total. No performance > issues were seen even using FakeAudioOutputStream which drives audio callbacks > on the message loop. > > Complicating matters it the fact that the AudioProperty(Add|Remove)ListenerBlock > functions are not available on 10.6... so we need to dynamically load them :-/ > > This is not ideal and long term we should replace the audio thread on OSX with a > dispatch queue to avoid having to suspend/resume the queue; this is non-trivial > and requires a significant refactoring of the AudioManager to support. > > BUG=158170 > TEST=media_unittests. manual device changes. > > Review URL: https://codereview.chromium.org/11824018 Fails on 10.6 :( TBR=dalecurtis@google.com Review URL: https://codereview.chromium.org/11840005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@176230 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/audio/audio_manager_base.cc7
-rw-r--r--media/audio/mac/audio_device_listener_mac.cc243
-rw-r--r--media/audio/mac/audio_device_listener_mac.h59
-rw-r--r--media/audio/mac/audio_device_listener_mac_unittest.cc82
-rw-r--r--media/audio/mac/audio_manager_mac.cc87
-rw-r--r--media/audio/mac/audio_manager_mac.h12
-rw-r--r--media/audio/win/audio_manager_win.h2
-rw-r--r--media/media.gyp3
8 files changed, 76 insertions, 419 deletions
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc
index 41c226d..458e53b 100644
--- a/media/audio/audio_manager_base.cc
+++ b/media/audio/audio_manager_base.cc
@@ -54,14 +54,7 @@ AudioManagerBase::AudioManagerBase()
#if defined(OS_WIN)
audio_thread_->init_com_with_mta(true);
#endif
-#if defined(OS_MACOSX)
- // On Mac, use a UI loop to get native message pump so that CoreAudio property
- // listener callbacks fire.
- CHECK(audio_thread_->StartWithOptions(
- base::Thread::Options(MessageLoop::TYPE_UI, 0)));
-#else
CHECK(audio_thread_->Start());
-#endif
message_loop_ = audio_thread_->message_loop_proxy();
}
diff --git a/media/audio/mac/audio_device_listener_mac.cc b/media/audio/mac/audio_device_listener_mac.cc
deleted file mode 100644
index a868e05..0000000
--- a/media/audio/mac/audio_device_listener_mac.cc
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/audio/mac/audio_device_listener_mac.h"
-
-#include "base/file_path.h"
-#include "base/logging.h"
-#include "base/mac/libdispatch_task_runner.h"
-#include "base/mac/mac_logging.h"
-#include "base/mac/mac_util.h"
-#include "base/message_loop.h"
-#include "base/native_library.h"
-
-namespace media {
-
-// The following three variables are used for dynamically loading the required
-// AudioObject(Add|Remove)PropertyListenerBlock functions on 10.7+ while keeping
-// a 10.6 deployment target. See http://crbug.com/158170 for details on why.
-// TODO(dalecurtis): Remove once the deployment target is > 10.6.
-static const char kCoreAudioFrameworkPath[] =
- "/System/Library/Frameworks/CoreAudio.framework/Versions/Current/CoreAudio";
-
-typedef OSStatus(*AudioObjectPropertyListenerBlockT)(
- AudioObjectID inObjectID,
- const AudioObjectPropertyAddress* inAddress,
- dispatch_queue_t inDispatchQueue,
- CrAudioObjectPropertyListenerBlock inListener);
-
-static AudioObjectPropertyListenerBlockT g_add_listener_block_func = NULL;
-static AudioObjectPropertyListenerBlockT g_remove_listener_block_func = NULL;
-
-// TaskObserver which guarantees that dispatch queue operations such as property
-// listener callbacks are mutually exclusive to operations on the audio thread.
-// TODO(dalecurtis): Instead we should replace the main thread with a dispatch
-// queue. See http://crbug.com/158170.
-class ExclusiveDispatchQueueTaskObserver : public MessageLoop::TaskObserver {
- public:
- ExclusiveDispatchQueueTaskObserver()
- : property_listener_queue_(new base::mac::LibDispatchTaskRunner(
- "com.google.chrome.AudioPropertyListenerQueue")),
- queue_(property_listener_queue_->GetDispatchQueue()),
- message_loop_(MessageLoop::current()) {
- // If we're currently on the thread, fire the suspend operation so we don't
- // end up with an unbalanced resume.
- if (message_loop_->message_loop_proxy()->BelongsToCurrentThread())
- WillProcessTask(base::TimeTicks());
-
- message_loop_->AddTaskObserver(this);
- }
-
- virtual ~ExclusiveDispatchQueueTaskObserver() {
- message_loop_->RemoveTaskObserver(this);
-
- // If we're currently on the thread, fire the resume operation so we don't
- // end up with an unbalanced suspend.
- if (message_loop_->message_loop_proxy()->BelongsToCurrentThread())
- DidProcessTask(base::TimeTicks());
-
- // This will hang if any listeners are still registered with the queue.
- property_listener_queue_->Shutdown();
- }
-
- virtual void WillProcessTask(base::TimeTicks time_posted) OVERRIDE {
- // Issue a synchronous suspend operation. Benchmarks on a retina 10.8.2
- // machine show this takes < 20us on average. dispatch_suspend() is an
- // asynchronous operation so we need to issue it inside of a synchronous
- // block to ensure it completes before WillProccesTask() completes.
- dispatch_sync(queue_, ^{
- dispatch_suspend(queue_);
- });
- }
-
- virtual void DidProcessTask(base::TimeTicks time_posted) OVERRIDE {
- // Issue an asynchronous resume operation. Benchmarks on a retina 10.8.2
- // machine show this takes < 10us on average.
- dispatch_resume(queue_);
- }
-
- dispatch_queue_t dispatch_queue() const {
- return queue_;
- }
-
- private:
- scoped_refptr<base::mac::LibDispatchTaskRunner> property_listener_queue_;
- const dispatch_queue_t queue_;
- MessageLoop* message_loop_;
-
- DISALLOW_COPY_AND_ASSIGN(ExclusiveDispatchQueueTaskObserver);
-};
-
-static bool LoadAudioObjectPropertyListenerBlockFunctions() {
- if (g_add_listener_block_func && g_remove_listener_block_func)
- return true;
-
- // Dynamically load required block functions.
- // TODO(dalecurtis): Remove once the deployment target is > 10.6.
- std::string error;
- base::NativeLibrary core_audio = base::LoadNativeLibrary(
- FilePath(kCoreAudioFrameworkPath), &error);
- if (!error.empty()) {
- LOG(ERROR) << "Could not open CoreAudio library: " << error;
- return false;
- }
-
- g_add_listener_block_func =
- reinterpret_cast<AudioObjectPropertyListenerBlockT>(
- base::GetFunctionPointerFromNativeLibrary(
- core_audio, "AudioObjectAddPropertyListenerBlock"));
- g_remove_listener_block_func =
- reinterpret_cast<AudioObjectPropertyListenerBlockT>(
- base::GetFunctionPointerFromNativeLibrary(
- core_audio, "AudioObjectRemovePropertyListenerBlock"));
-
- // If either function failed to load, skip listener registration.
- if (!g_add_listener_block_func || !g_remove_listener_block_func) {
- g_add_listener_block_func = NULL;
- g_remove_listener_block_func = NULL;
- return false;
- }
-
- return true;
-}
-
-// Property address to monitor for device changes.
-const AudioObjectPropertyAddress
-AudioDeviceListenerMac::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 AudioDeviceListenerMac::OnDefaultDeviceChanged(
- 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<AudioDeviceListenerMac*>(context)->listener_cb_.Run();
- break;
- }
- }
-
- return noErr;
-}
-
-AudioDeviceListenerMac::AudioDeviceListenerMac(
- const base::Closure& listener_cb) {
- if (!LoadAudioObjectPropertyListenerBlockFunctions())
- return;
-
- // Device changes are hard, lets go shopping! Sadly OSX does not handle
- // property listener callbacks in a thread safe manner. On 10.6 we can set
- // kAudioHardwarePropertyRunLoop to account for this. On 10.7 this is broken
- // and we need to create our dispatch queue to drive callbacks. However code
- // running on the dispatch queue must be mutually exclusive with code running
- // on the audio thread. ExclusiveDispatchQueueTaskObserver works around this
- // by pausing and resuming the dispatch queue before and after each pumped
- // task. This is not ideal and long term we should replace the audio thread
- // on OSX with a dispatch queue. See http://crbug.com/158170 for discussion.
- // TODO(dalecurtis): Does not fix the cases where GetAudioHardwareSampleRate()
- // and GetAudioInputHardwareSampleRate() are called by the browser process.
- // These are one time events due to renderer side cache and thus unlikely to
- // occur at the same time as a device callback. Should be fixed along with
- // http://crbug.com/137326 using a forced PostTask.
- OSStatus result;
- if (base::mac::IsOSLionOrLater()) {
- task_observer_.reset(new ExclusiveDispatchQueueTaskObserver());
- listener_block_ = Block_copy(^(
- UInt32 num_addresses, const AudioObjectPropertyAddress addresses[]) {
- OnDefaultDeviceChanged(
- kAudioObjectSystemObject, num_addresses, addresses, this);
- });
- result = g_add_listener_block_func(
- kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
- task_observer_->dispatch_queue(), listener_block_);
- } else {
- const AudioObjectPropertyAddress kRunLoopAddress = {
- kAudioHardwarePropertyRunLoop,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster
- };
-
- CFRunLoopRef run_loop = CFRunLoopGetCurrent();
- UInt32 size = sizeof(CFRunLoopRef);
- result = AudioObjectSetPropertyData(
- kAudioObjectSystemObject, &kRunLoopAddress, 0, 0, size, &run_loop);
- if (result != noErr) {
- OSSTATUS_DLOG(ERROR, result) << "Failed to set property listener thread.";
- return;
- }
-
- result = AudioObjectAddPropertyListener(
- kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
- &AudioDeviceListenerMac::OnDefaultDeviceChanged, this);
- }
-
- if (result != noErr) {
- task_observer_.reset();
- OSSTATUS_DLOG(ERROR, result) << "AudioObjectAddPropertyListener() failed!";
- return;
- }
-
- listener_cb_ = listener_cb;
-}
-
-AudioDeviceListenerMac::~AudioDeviceListenerMac() {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (listener_cb_.is_null())
- return;
-
- if (task_observer_) {
- OSStatus result = g_remove_listener_block_func(
- kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
- task_observer_->dispatch_queue(), listener_block_);
-
- OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
- << "AudioObjectRemovePropertyListenerBlock() failed!";
-
- // Task observer will wait for all outstanding callbacks to complete.
- task_observer_.reset();
- Block_release(listener_block_);
- return;
- }
-
- // Since we're running on the same CFRunLoop, there can be no outstanding
- // callbacks in flight.
- OSStatus result = AudioObjectRemovePropertyListener(
- kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
- &AudioDeviceListenerMac::OnDefaultDeviceChanged, this);
- OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
- << "AudioObjectRemovePropertyListener() failed!";
-}
-
-} // namespace media
diff --git a/media/audio/mac/audio_device_listener_mac.h b/media/audio/mac/audio_device_listener_mac.h
deleted file mode 100644
index b944817..0000000
--- a/media/audio/mac/audio_device_listener_mac.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_AUDIO_MAC_AUDIO_DEVICE_LISTENER_MAC_H_
-#define MEDIA_AUDIO_MAC_AUDIO_DEVICE_LISTENER_MAC_H_
-
-#include <CoreAudio/AudioHardware.h>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/threading/thread_checker.h"
-#include "media/base/media_export.h"
-
-namespace media {
-
-// TODO(dalecurtis): Use the standard AudioObjectPropertyListenerBlock when
-// the deployment target is > 10.6.
-typedef void (^CrAudioObjectPropertyListenerBlock)(
- UInt32 num_addresses, const AudioObjectPropertyAddress addresses[]);
-
-class ExclusiveDispatchQueueTaskObserver;
-
-// AudioDeviceListenerMac facilitates thread safe execution of device listener
-// callbacks issued via CoreAudio. On 10.6 this means correctly binding the
-// CFRunLoop driving callbacks to the CFRunLoop AudioDeviceListenerMac is
-// constructed on. On 10.7 this means creating a dispatch queue to drive
-// callbacks and modifying the message loop AudioDeviceListenerMac to ensure
-// message loop tasks and dispatch queue tasks are executed in a mutually
-// exclusive manner. See http://crbug.com/158170 for more discussion.
-class MEDIA_EXPORT AudioDeviceListenerMac {
- public:
- // |listener_cb| will be called when a device change occurs; it's a permanent
- // callback and must outlive AudioDeviceListenerMac.
- explicit AudioDeviceListenerMac(const base::Closure& listener_cb);
- ~AudioDeviceListenerMac();
-
- private:
- friend class AudioDeviceListenerMacTest;
- static const AudioObjectPropertyAddress kDeviceChangePropertyAddress;
-
- static OSStatus OnDefaultDeviceChanged(
- AudioObjectID object, UInt32 num_addresses,
- const AudioObjectPropertyAddress addresses[], void* context);
-
- base::Closure listener_cb_;
- CrAudioObjectPropertyListenerBlock listener_block_;
- scoped_ptr<ExclusiveDispatchQueueTaskObserver> task_observer_;
-
- // AudioDeviceListenerMac must be constructed and destructed on the same
- // thread.
- base::ThreadChecker thread_checker_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioDeviceListenerMac);
-};
-
-} // namespace media
-
-#endif // MEDIA_AUDIO_MAC_AUDIO_DEVICE_LISTENER_MAC_H_
diff --git a/media/audio/mac/audio_device_listener_mac_unittest.cc b/media/audio/mac/audio_device_listener_mac_unittest.cc
deleted file mode 100644
index cff3b05..0000000
--- a/media/audio/mac/audio_device_listener_mac_unittest.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <CoreAudio/AudioHardware.h>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop.h"
-#include "media/audio/mac/audio_device_listener_mac.h"
-#include "media/base/bind_to_loop.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace media {
-
-class AudioDeviceListenerMacTest : public testing::Test {
- public:
- AudioDeviceListenerMacTest() {
- // It's important to create the device listener from the message loop in
- // order to ensure we don't end up with unbalanced TaskObserver calls.
- message_loop_.PostTask(FROM_HERE, base::Bind(
- &AudioDeviceListenerMacTest::CreateDeviceListener,
- base::Unretained(this)));
- message_loop_.RunUntilIdle();
- }
-
- virtual ~AudioDeviceListenerMacTest() {
- // It's important to destroy the device listener from the message loop in
- // order to ensure we don't end up with unbalanced TaskObserver calls.
- message_loop_.PostTask(FROM_HERE, base::Bind(
- &AudioDeviceListenerMacTest::DestroyDeviceListener,
- base::Unretained(this)));
- message_loop_.RunUntilIdle();
- }
-
- void CreateDeviceListener() {
- // Force a post task using BindToLoop to ensure device listener internals
- // are working correctly.
- output_device_listener_.reset(new AudioDeviceListenerMac(BindToLoop(
- message_loop_.message_loop_proxy(), base::Bind(
- &AudioDeviceListenerMacTest::OnDeviceChange,
- base::Unretained(this)))));
- }
-
- void DestroyDeviceListener() {
- output_device_listener_.reset();
- }
-
- // Simulate a device change where no output devices are available.
- bool SimulateDefaultOutputDeviceChange() {
- // Include multiple addresses to ensure only a single device change event
- // occurs.
- const AudioObjectPropertyAddress addresses[] = {
- AudioDeviceListenerMac::kDeviceChangePropertyAddress,
- { kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster }
- };
-
- return noErr == output_device_listener_->OnDefaultDeviceChanged(
- kAudioObjectSystemObject, 1, addresses, output_device_listener_.get());
- }
-
- MOCK_METHOD0(OnDeviceChange, void());
-
- protected:
- MessageLoop message_loop_;
- scoped_ptr<AudioDeviceListenerMac> output_device_listener_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioDeviceListenerMacTest);
-};
-
-// Simulate a device change events and ensure we get the right callbacks.
-TEST_F(AudioDeviceListenerMacTest, OutputDeviceChange) {
- EXPECT_CALL(*this, OnDeviceChange()).Times(1);
- ASSERT_TRUE(SimulateDefaultOutputDeviceChange());
- message_loop_.RunUntilIdle();
-}
-
-} // namespace media
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() {
diff --git a/media/audio/mac/audio_manager_mac.h b/media/audio/mac/audio_manager_mac.h
index 40ed54e..d8b6b2d 100644
--- a/media/audio/mac/audio_manager_mac.h
+++ b/media/audio/mac/audio_manager_mac.h
@@ -9,7 +9,6 @@
#include "base/compiler_specific.h"
#include "base/message_loop_proxy.h"
#include "media/audio/audio_manager_base.h"
-#include "media/audio/mac/audio_device_listener_mac.h"
namespace media {
@@ -38,15 +37,16 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase {
virtual AudioParameters GetPreferredLowLatencyOutputStreamParameters(
const AudioParameters& input_params) OVERRIDE;
+ // Called by an internal device change listener. Must be called on
+ // |creating_message_loop_|.
+ void OnDeviceChange();
+
protected:
virtual ~AudioManagerMac();
private:
- // Helper methods for constructing AudioDeviceListenerMac on the audio thread.
- void CreateDeviceListener();
- void DestroyDeviceListener();
-
- scoped_ptr<AudioDeviceListenerMac> output_device_listener_;
+ bool listener_registered_;
+ scoped_refptr<base::MessageLoopProxy> creating_message_loop_;
DISALLOW_COPY_AND_ASSIGN(AudioManagerMac);
};
diff --git a/media/audio/win/audio_manager_win.h b/media/audio/win/audio_manager_win.h
index 61bd8a6..4ce2fbc 100644
--- a/media/audio/win/audio_manager_win.h
+++ b/media/audio/win/audio_manager_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/media/media.gyp b/media/media.gyp
index 963369d..05b35cc 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -111,8 +111,6 @@
'audio/linux/cras_input.h',
'audio/linux/cras_output.cc',
'audio/linux/cras_output.h',
- 'audio/mac/audio_device_listener_mac.cc',
- 'audio/mac/audio_device_listener_mac.h',
'audio/mac/audio_input_mac.cc',
'audio/mac/audio_input_mac.h',
'audio/mac/audio_low_latency_input_mac.cc',
@@ -669,7 +667,6 @@
'audio/fake_audio_output_stream_unittest.cc',
'audio/ios/audio_manager_ios_unittest.cc',
'audio/linux/alsa_output_unittest.cc',
- 'audio/mac/audio_device_listener_mac_unittest.cc',
'audio/mac/audio_low_latency_input_mac_unittest.cc',
'audio/mac/audio_output_mac_unittest.cc',
'audio/simple_sources_unittest.cc',