#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"
} // 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_;
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::
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];
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
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.
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));
void DeviceMonitorMac::NotifyDeviceChanged(
base::SystemMonitor::DeviceType type) {
+ DCHECK(thread_checker_.CalledOnValidThread());
// TODO(xians): Remove the global variable for SystemMonitor.
base::SystemMonitor::Get()->ProcessDevicesChanged(type);
}