diff options
author | mcasas@chromium.org <mcasas@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-22 15:02:56 +0000 |
---|---|---|
committer | mcasas@chromium.org <mcasas@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-22 15:02:56 +0000 |
commit | 74c3445f9f933495683b27a986ede3e8e4858e03 (patch) | |
tree | 5fa42046324b54428d15e61539bb620e631819a1 | |
parent | 80b0c8c1adf1991387a038ec157f09b6c81caa45 (diff) | |
download | chromium_src-74c3445f9f933495683b27a986ede3e8e4858e03.zip chromium_src-74c3445f9f933495683b27a986ede3e8e4858e03.tar.gz chromium_src-74c3445f9f933495683b27a986ede3e8e4858e03.tar.bz2 |
Mac AVFoundation: Enumerate devices in device/audio thread.
Bug http://crbug.com/359589 describes a Chrome hang
where two threads are calling [AVCaptureDeviceGlue devices]
at the same time to enumerate the devices. The first call
comes from DeviceMonitorMac::StartMonitoring() on UI
thread while the second call comes from the Audio Thread,
a.k.a. Device Thread on
+VideoCaptureDeviceAVFoundation::getDeviceNames:.
DMM == DeviceMonitorMac
The solution proposed here is to do as much as possible
on Device thread :
- when there is a device change via DMM::OnDeviceChanged(),
enumerate on Device Thread, then consolidate the device
list on UI thread.
- First |suspend_observer_| registration happens on Device
Thread. This suspend observer lives in device thread.
- Observer removal happens dutifully on Device Thread
destructor in -CrAVFoundationDeviceObserver::dealloc
BUG=288562, 359589
Review URL: https://codereview.chromium.org/234563004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@265254 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/device_monitor_mac.h | 8 | ||||
-rw-r--r-- | content/browser/device_monitor_mac.mm | 85 | ||||
-rw-r--r-- | content/browser/renderer_host/media/media_stream_manager.cc | 9 |
3 files changed, 80 insertions, 22 deletions
diff --git a/content/browser/device_monitor_mac.h b/content/browser/device_monitor_mac.h index 696b961..b3d3f9f 100644 --- a/content/browser/device_monitor_mac.h +++ b/content/browser/device_monitor_mac.h @@ -25,8 +25,10 @@ class DeviceMonitorMac { // Registers the observers for the audio/video device removal, connection and // suspension. The AVFoundation library is also loaded and initialised if the - // OS supports it. - void StartMonitoring(); + // OS supports it. The |device_task_runner| argument represents the thread on + // which device enumeration will occur. + void StartMonitoring( + const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); // Method called by the internal DeviceMonitorMacImpl object // |device_monitor_impl_| when a device of type |type| has been added to or @@ -38,7 +40,7 @@ class DeviceMonitorMac { scoped_ptr<DeviceMonitorMacImpl> device_monitor_impl_; // |thread_checker_| is used to check that constructor and StartMonitoring() - // are called in the correct thread, that also owns the object. + // are called in the correct thread, the UI thread, that also owns the object. base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(DeviceMonitorMac); diff --git a/content/browser/device_monitor_mac.mm b/content/browser/device_monitor_mac.mm index 10dd620..9433c59 100644 --- a/content/browser/device_monitor_mac.mm +++ b/content/browser/device_monitor_mac.mm @@ -8,6 +8,7 @@ #include <set> +#include "base/bind_helpers.h" #include "base/logging.h" #include "base/mac/scoped_nsobject.h" #import "media/video/capture/mac/avfoundation_glue.h" @@ -211,8 +212,10 @@ class AVFoundationMonitorImpl; } // namespace -// This class is a Key-Value Observer (KVO) shim. It is needed because C++ -// classes cannot observe Key-Values directly. +// This class is a Key-Value Observer (KVO) shim. It is needed because C++ +// classes cannot observe Key-Values directly. This class is used by +// AVfoundationMonitorImpl and executed in its |device_task_runner_|, a.k.a. +// "Device Thread". -stopObserving is called dutifully on -dealloc on UI thread. @interface CrAVFoundationDeviceObserver : NSObject { @private AVFoundationMonitorImpl* receiver_; @@ -228,21 +231,41 @@ class AVFoundationMonitorImpl; namespace { +// AVFoundation implementation of the Mac Device Monitor, registers as a global +// device connect/disconnect observer and plugs suspend/wake up device observers +// per device. This class is created and lives in UI thread; device enumeration +// and operations involving |suspend_observer_| happen on |device_task_runner_|. class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { public: - explicit AVFoundationMonitorImpl(content::DeviceMonitorMac* monitor); + AVFoundationMonitorImpl( + content::DeviceMonitorMac* monitor, + const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); virtual ~AVFoundationMonitorImpl(); virtual void OnDeviceChanged() OVERRIDE; private: + void OnDeviceChangedOnDeviceThread( + const scoped_refptr<base::MessageLoopProxy>& ui_thread); + void StartObserverOnDeviceThread(); + + base::ThreadChecker thread_checker_; + + // {Video,AudioInput}DeviceManager's "Device" thread task runner used for + // device enumeration, valid after MediaStreamManager calls StartMonitoring(). + const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_; + + // Created and executed in |device_task_runnner_|. base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; + DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); }; AVFoundationMonitorImpl::AVFoundationMonitorImpl( - content::DeviceMonitorMac* monitor) - : DeviceMonitorMacImpl(monitor) { + content::DeviceMonitorMac* monitor, + const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) + : DeviceMonitorMacImpl(monitor), + device_task_runner_(device_task_runner) { NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; device_arrival_ = [nc addObserverForName:AVFoundationGlue:: @@ -258,23 +281,31 @@ AVFoundationMonitorImpl::AVFoundationMonitorImpl( queue:nil usingBlock:^(NSNotification* notification) { OnDeviceChanged();}]; - suspend_observer_.reset( - [[CrAVFoundationDeviceObserver alloc] initWithChangeReceiver:this]); - for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) - [suspend_observer_ startObserving:device]; + device_task_runner_->PostTask(FROM_HERE, + base::Bind(&AVFoundationMonitorImpl::StartObserverOnDeviceThread, + base::Unretained(this))); } AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:device_arrival_]; [nc removeObserver:device_removal_]; - for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) - [suspend_observer_ stopObserving:device]; } void AVFoundationMonitorImpl::OnDeviceChanged() { + DCHECK(thread_checker_.CalledOnValidThread()); + device_task_runner_->PostTask(FROM_HERE, + base::Bind(&AVFoundationMonitorImpl::OnDeviceChangedOnDeviceThread, + base::Unretained(this), + base::MessageLoop::current()->message_loop_proxy())); +} + +void AVFoundationMonitorImpl::OnDeviceChangedOnDeviceThread( + const scoped_refptr<base::MessageLoopProxy>& ui_thread) { + DCHECK(device_task_runner_->BelongsToCurrentThread()); + NSArray* devices = [AVCaptureDeviceGlue devices]; std::vector<DeviceInfo> snapshot_devices; - for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) { + for (CrAVCaptureDevice* device in devices) { [suspend_observer_ startObserving:device]; BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && [device isSuspended]; @@ -291,7 +322,18 @@ void AVFoundationMonitorImpl::OnDeviceChanged() { snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], device_type)); } - ConsolidateDevicesListAndNotify(snapshot_devices); + // Post the consolidation of enumerated devices to be done on UI thread. + ui_thread->PostTask(FROM_HERE, + base::Bind(&DeviceMonitorMacImpl::ConsolidateDevicesListAndNotify, + base::Unretained(this), snapshot_devices)); +} + +void AVFoundationMonitorImpl::StartObserverOnDeviceThread() { + DCHECK(device_task_runner_->BelongsToCurrentThread()); + suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] + initWithChangeReceiver:this]); + for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) + [suspend_observer_ startObserving:device]; } } // namespace @@ -299,13 +341,21 @@ void AVFoundationMonitorImpl::OnDeviceChanged() { @implementation CrAVFoundationDeviceObserver - (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver { - if ((self = [super init])) { + if (self = [super init]) { DCHECK(receiver != NULL); receiver_ = receiver; } return self; } +- (void)dealloc { + for (std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); + it != monitoredDevices_.end(); ++it) { + [self stopObserving:*it]; + } + [super dealloc]; +} + - (void)startObserving:(CrAVCaptureDevice*)device { DCHECK(device != nil); // Skip this device if there are already observers connected to it. @@ -359,11 +409,13 @@ DeviceMonitorMac::DeviceMonitorMac() { DeviceMonitorMac::~DeviceMonitorMac() {} -void DeviceMonitorMac::StartMonitoring() { +void DeviceMonitorMac::StartMonitoring( + const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) { DCHECK(thread_checker_.CalledOnValidThread()); if (AVFoundationGlue::IsAVFoundationSupported()) { DVLOG(1) << "Monitoring via AVFoundation"; - device_monitor_impl_.reset(new AVFoundationMonitorImpl(this)); + device_monitor_impl_.reset(new AVFoundationMonitorImpl(this, + device_task_runner)); } else { DVLOG(1) << "Monitoring via QTKit"; device_monitor_impl_.reset(new QTKitMonitorImpl(this)); @@ -372,6 +424,7 @@ void DeviceMonitorMac::StartMonitoring() { void DeviceMonitorMac::NotifyDeviceChanged( base::SystemMonitor::DeviceType type) { + DCHECK(thread_checker_.CalledOnValidThread()); // TODO(xians): Remove the global variable for SystemMonitor. base::SystemMonitor::Get()->ProcessDevicesChanged(type); } diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc index 56bc43a..944ece5 100644 --- a/content/browser/renderer_host/media/media_stream_manager.cc +++ b/content/browser/renderer_host/media/media_stream_manager.cc @@ -47,7 +47,8 @@ namespace content { // Forward declaration of DeviceMonitorMac and its only useable method. class DeviceMonitorMac { public: - void StartMonitoring(); + void StartMonitoring( + const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); }; namespace { @@ -834,8 +835,10 @@ void MediaStreamManager::StartMonitoring() { void MediaStreamManager::StartMonitoringOnUIThread() { DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserMainLoop* browser_main_loop = content::BrowserMainLoop::GetInstance(); - if (browser_main_loop) - browser_main_loop->device_monitor_mac()->StartMonitoring(); + if (browser_main_loop) { + browser_main_loop->device_monitor_mac() + ->StartMonitoring(audio_manager_->GetWorkerTaskRunner()); + } } #endif |