Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / device_monitor_mac.mm
index 10dd620..5491aa6 100644 (file)
@@ -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
@@ -306,6 +348,13 @@ void AVFoundationMonitorImpl::OnDeviceChanged() {
   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);
 }