Index: content/browser/device_monitor_mac.mm |
diff --git a/content/browser/device_monitor_mac.mm b/content/browser/device_monitor_mac.mm |
index 10dd620b7e5257de41980c154b3da0fef1c7c22a..bee58057e9c504ba34f1495ba36034f334896c78 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,20 @@ void AVFoundationMonitorImpl::OnDeviceChanged() { |
@implementation CrAVFoundationDeviceObserver |
- (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver { |
- if ((self = [super init])) { |
Robert Sesek
2014/04/29 15:19:53
nit: keep these
|
+ if (self = [super init]) { |
DCHECK(receiver != NULL); |
receiver_ = receiver; |
} |
return self; |
} |
+- (void)dealloc { |
+ std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); |
+ while (it != monitoredDevices_.end()) |
+ [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 +408,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 +423,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); |
} |