| Index: media/audio/mac/audio_device_listener_mac.cc
|
| diff --git a/media/audio/mac/audio_device_listener_mac.cc b/media/audio/mac/audio_device_listener_mac.cc
|
| index 0c33d10c2fa330591490cff26e8b72e96170fe83..34c0a168327e3f1d8171148815facc45cfb78d7a 100644
|
| --- a/media/audio/mac/audio_device_listener_mac.cc
|
| +++ b/media/audio/mac/audio_device_listener_mac.cc
|
| @@ -7,7 +7,6 @@
|
| #include "base/bind.h"
|
| #include "base/files/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"
|
| @@ -16,74 +15,6 @@
|
|
|
| namespace media {
|
|
|
| -// 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 base::MessageLoop::TaskObserver {
|
| - public:
|
| - ExclusiveDispatchQueueTaskObserver()
|
| - : property_listener_queue_(new base::mac::LibDispatchTaskRunner(
|
| - "com.google.chrome.AudioPropertyListenerQueue")),
|
| - queue_(property_listener_queue_->GetDispatchQueue()),
|
| - message_loop_(base::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())
|
| - SuspendDispatchQueue();
|
| -
|
| - 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())
|
| - ResumeDispatchQueue();
|
| -
|
| - // This will hang if any listeners are still registered with the queue.
|
| - property_listener_queue_->Shutdown();
|
| - }
|
| -
|
| - virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
|
| - SuspendDispatchQueue();
|
| - }
|
| -
|
| - virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
|
| - ResumeDispatchQueue();
|
| - }
|
| -
|
| - dispatch_queue_t dispatch_queue() const {
|
| - return queue_;
|
| - }
|
| -
|
| - private:
|
| - // 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.
|
| - void SuspendDispatchQueue() {
|
| - dispatch_sync(queue_, ^{
|
| - dispatch_suspend(queue_);
|
| - });
|
| - }
|
| -
|
| - // Issue an asynchronous resume operation. Benchmarks on a retina 10.8.2
|
| - // machine show this takes < 10us on average.
|
| - void ResumeDispatchQueue() {
|
| - dispatch_resume(queue_);
|
| - }
|
| -
|
| - scoped_refptr<base::mac::LibDispatchTaskRunner> property_listener_queue_;
|
| - const dispatch_queue_t queue_;
|
| - base::MessageLoop* message_loop_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ExclusiveDispatchQueueTaskObserver);
|
| -};
|
| -
|
| // Property address to monitor for device changes.
|
| const AudioObjectPropertyAddress
|
| AudioDeviceListenerMac::kDeviceChangePropertyAddress = {
|
| @@ -106,7 +37,10 @@ OSStatus AudioDeviceListenerMac::OnDefaultDeviceChanged(
|
| addresses[i].mScope == kDeviceChangePropertyAddress.mScope &&
|
| addresses[i].mElement == kDeviceChangePropertyAddress.mElement &&
|
| context) {
|
| - static_cast<AudioDeviceListenerMac*>(context)->listener_cb_.Run();
|
| + AudioDeviceListenerMac* p_this =
|
| + static_cast<AudioDeviceListenerMac*>(context);
|
| + DCHECK(p_this->thread_checker_.CalledOnValidThread());
|
| + p_this->listener_cb_.Run();
|
| break;
|
| }
|
| }
|
| @@ -114,72 +48,16 @@ OSStatus AudioDeviceListenerMac::OnDefaultDeviceChanged(
|
| return noErr;
|
| }
|
|
|
| -AudioDeviceListenerMac::AudioDeviceListenerMac(const base::Closure& listener_cb)
|
| - : listener_block_(NULL),
|
| - add_listener_block_func_(NULL),
|
| - remove_listener_block_func_(NULL) {
|
| - // 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. 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
|
| - // GetDefaultOutputStreamParameters() 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.
|
| - if (base::mac::IsOSLionOrLater()) {
|
| - if (!LoadAudioObjectPropertyListenerBlockFunctions())
|
| - return;
|
| -
|
| - task_observer_.reset(new ExclusiveDispatchQueueTaskObserver());
|
| - listener_block_ = Block_copy(^(
|
| - UInt32 num_addresses, const AudioObjectPropertyAddress addresses[]) {
|
| - OnDefaultDeviceChanged(
|
| - kAudioObjectSystemObject, num_addresses, addresses, this);
|
| - });
|
| -
|
| - OSStatus result = add_listener_block_func_(
|
| - kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
|
| - task_observer_->dispatch_queue(), listener_block_);
|
| -
|
| - if (result != noErr) {
|
| - task_observer_.reset();
|
| - Block_release(listener_block_);
|
| - listener_block_ = NULL;
|
| - OSSTATUS_DLOG(ERROR, result)
|
| - << "AudioObjectAddPropertyListenerBlock() failed!";
|
| - return;
|
| - }
|
| - } else {
|
| - const AudioObjectPropertyAddress kRunLoopAddress = {
|
| - kAudioHardwarePropertyRunLoop,
|
| - kAudioObjectPropertyScopeGlobal,
|
| - kAudioObjectPropertyElementMaster
|
| - };
|
| -
|
| - CFRunLoopRef run_loop = CFRunLoopGetCurrent();
|
| - UInt32 size = sizeof(CFRunLoopRef);
|
| - OSStatus 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);
|
| +AudioDeviceListenerMac::AudioDeviceListenerMac(
|
| + const base::Closure& listener_cb) {
|
| + OSStatus result = AudioObjectAddPropertyListener(
|
| + kAudioObjectSystemObject, &kDeviceChangePropertyAddress,
|
| + &AudioDeviceListenerMac::OnDefaultDeviceChanged, this);
|
|
|
| - if (result != noErr) {
|
| - OSSTATUS_DLOG(ERROR, result)
|
| - << "AudioObjectAddPropertyListener() failed!";
|
| - return;
|
| - }
|
| + if (result != noErr) {
|
| + OSSTATUS_DLOG(ERROR, result)
|
| + << "AudioObjectAddPropertyListener() failed!";
|
| + return;
|
| }
|
|
|
| listener_cb_ = listener_cb;
|
| @@ -190,20 +68,6 @@ AudioDeviceListenerMac::~AudioDeviceListenerMac() {
|
| if (listener_cb_.is_null())
|
| return;
|
|
|
| - if (task_observer_) {
|
| - OSStatus result = 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(
|
| @@ -213,35 +77,4 @@ AudioDeviceListenerMac::~AudioDeviceListenerMac() {
|
| << "AudioObjectRemovePropertyListener() failed!";
|
| }
|
|
|
| -bool AudioDeviceListenerMac::LoadAudioObjectPropertyListenerBlockFunctions() {
|
| - // Dynamically load required block functions.
|
| - // TODO(dalecurtis): Remove once the deployment target is > 10.6.
|
| - std::string error;
|
| - base::NativeLibrary core_audio = base::LoadNativeLibrary(base::FilePath(
|
| - "/System/Library/Frameworks/CoreAudio.framework/Versions/Current/"
|
| - "CoreAudio"), &error);
|
| - if (!error.empty()) {
|
| - LOG(ERROR) << "Could not open CoreAudio library: " << error;
|
| - return false;
|
| - }
|
| -
|
| - core_audio_lib_.Reset(core_audio);
|
| - add_listener_block_func_ =
|
| - reinterpret_cast<AudioObjectPropertyListenerBlockT>(
|
| - core_audio_lib_.GetFunctionPointer(
|
| - "AudioObjectAddPropertyListenerBlock"));
|
| - remove_listener_block_func_ =
|
| - reinterpret_cast<AudioObjectPropertyListenerBlockT>(
|
| - core_audio_lib_.GetFunctionPointer(
|
| - "AudioObjectRemovePropertyListenerBlock"));
|
| -
|
| - // If either function failed to load, skip listener registration.
|
| - if (!add_listener_block_func_ || !remove_listener_block_func_) {
|
| - DLOG(ERROR) << "Failed to load audio property listener block functions!";
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| } // namespace media
|
|
|