Index: content/browser/device_monitor_mac.mm |
diff --git a/content/browser/device_monitor_mac.mm b/content/browser/device_monitor_mac.mm |
index eb429afe6402bb07bfc3f100ec31e9c05e2665e9..deeb3986f47d78344146636ffe3bbd3e49e13705 100644 |
--- a/content/browser/device_monitor_mac.mm |
+++ b/content/browser/device_monitor_mac.mm |
@@ -10,9 +10,11 @@ |
#include "base/bind_helpers.h" |
#include "base/logging.h" |
+#include "base/mac/bind_objc_block.h" |
#include "base/mac/scoped_nsobject.h" |
#include "base/threading/thread_checker.h" |
#include "content/public/browser/browser_thread.h" |
+#include "media/base/bind_to_current_loop.h" |
#import "media/video/capture/mac/avfoundation_glue.h" |
namespace { |
@@ -216,15 +218,17 @@ class SuspendObserverDelegate; |
// This class is a Key-Value Observer (KVO) shim. It is needed because C++ |
// classes cannot observe Key-Values directly. Created, manipulated, and |
-// destroyed on the Device Thread by SuspendedObserverDelegate. |
+// destroyed on the UI Thread by SuspendObserverDelegate. |
@interface CrAVFoundationDeviceObserver : NSObject { |
@private |
- SuspendObserverDelegate* receiver_; // weak |
+ base::Closure onDeviceChangedCallback_; |
+ SuspendObserverDelegate* receiver_; // Weak. |
tommi (sloooow) - chröme
2014/07/07 07:41:07
you don't need this variable anymore
mcasas
2014/07/08 15:11:45
Done.
|
// Member to keep track of the devices we are already monitoring. |
std::set<CrAVCaptureDevice*> monitoredDevices_; |
} |
-- (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver; |
+- (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver |
tommi (sloooow) - chröme
2014/07/07 07:41:07
Since this method doesn't need the receiver pointe
mcasas
2014/07/08 15:11:45
Changed, but still kept the initWithX as is part o
|
+ onDeviceChangedCallback:(const base::Closure&)callback; |
- (void)startObserving:(CrAVCaptureDevice*)device; |
- (void)stopObserving:(CrAVCaptureDevice*)device; |
@@ -233,14 +237,15 @@ class SuspendObserverDelegate; |
namespace { |
// This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. |
-// Provides a callback for this device observer to indicate that there has been |
-// a device change of some kind. Created by AVFoundationMonitorImpl in UI thread |
-// but living in Device Thread. |
+// AVFoundationMonitorImpl creates and destroys it in UI thread. Runs the |
+// expensive device enumerations in OnDeviceChanged() and StartObserver() on |
+// Device Thread. |
class SuspendObserverDelegate : |
public base::RefCountedThreadSafe<SuspendObserverDelegate> { |
public: |
explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) |
: avfoundation_monitor_impl_(monitor) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
device_thread_checker_.DetachFromThread(); |
} |
@@ -251,12 +256,15 @@ class SuspendObserverDelegate : |
private: |
friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; |
- virtual ~SuspendObserverDelegate() {} |
+ virtual ~SuspendObserverDelegate() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ } |
void OnDeviceChangedOnUIThread( |
const std::vector<DeviceInfo>& snapshot_devices); |
base::ThreadChecker device_thread_checker_; |
+ // Created, used and released in UI thread. |
base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; |
DeviceMonitorMacImpl* avfoundation_monitor_impl_; |
}; |
@@ -266,7 +274,8 @@ void SuspendObserverDelegate::OnDeviceChanged() { |
NSArray* devices = [AVCaptureDeviceGlue devices]; |
std::vector<DeviceInfo> snapshot_devices; |
for (CrAVCaptureDevice* device in devices) { |
- [suspend_observer_ startObserving:device]; |
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
+ base::BindBlock(^{ [suspend_observer_ startObserving:device]; })); |
BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && |
[device isSuspended]; |
DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; |
@@ -290,10 +299,19 @@ void SuspendObserverDelegate::OnDeviceChanged() { |
void SuspendObserverDelegate::StartObserver() { |
DCHECK(device_thread_checker_.CalledOnValidThread()); |
- suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] |
- initWithChangeReceiver:this]); |
- for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) |
- [suspend_observer_ startObserving:device]; |
+ |
+ base::Closure on_device_changed_callback = media::BindToCurrentLoop( |
tommi (sloooow) - chröme
2014/07/07 07:41:07
is media::BindToCurrentLoop used elsewhere in cont
mcasas
2014/07/08 15:11:45
Yes, f.i. in browser/renderer_host/media/video_cap
|
+ base::Bind(&SuspendObserverDelegate::OnDeviceChanged, this)); |
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
+ base::BindBlock(^{ suspend_observer_.reset( |
+ [[CrAVFoundationDeviceObserver alloc] |
+ initWithChangeReceiver:this |
+ onDeviceChangedCallback:on_device_changed_callback]); |
+ })); |
+ for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) { |
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
+ base::BindBlock(^{ [suspend_observer_ startObserving:device]; })); |
+ } |
} |
void SuspendObserverDelegate::ResetDeviceMonitorOnUIThread() { |
@@ -384,22 +402,29 @@ void AVFoundationMonitorImpl::OnDeviceChanged() { |
@implementation CrAVFoundationDeviceObserver |
-- (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver { |
+- (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver |
+ onDeviceChangedCallback:(const base::Closure&)callback { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
if ((self = [super init])) { |
DCHECK(receiver != NULL); |
receiver_ = receiver; |
+ onDeviceChangedCallback_ = callback; |
} |
return self; |
} |
- (void)dealloc { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); |
- while (it != monitoredDevices_.end()) |
- [self stopObserving:*it++]; |
+ while (it != monitoredDevices_.end()) { |
+ [self removeObservers:*it]; |
+ monitoredDevices_.erase(it++); |
+ } |
[super dealloc]; |
} |
- (void)startObserving:(CrAVCaptureDevice*)device { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
DCHECK(device != nil); |
// Skip this device if there are already observers connected to it. |
if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != |
@@ -418,28 +443,32 @@ void AVFoundationMonitorImpl::OnDeviceChanged() { |
} |
- (void)stopObserving:(CrAVCaptureDevice*)device { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
DCHECK(device != nil); |
std::set<CrAVCaptureDevice*>::iterator found = |
std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); |
DCHECK(found != monitoredDevices_.end()); |
- // Every so seldom, |device| might be gone when getting here, in that case |
- // removing the observer causes a crash. Try to avoid it by checking sanity of |
- // the |device| via its -observationInfo. http://crbug.com/371271. |
+ [self removeObservers:*found]; |
+ monitoredDevices_.erase(found); |
+} |
+ |
+- (void)removeObservers:(CrAVCaptureDevice*)device { |
+ // Check sanity of |device| via its -observationInfo. http://crbug.com/371271. |
if ([device observationInfo]) { |
[device removeObserver:self |
forKeyPath:@"suspended"]; |
[device removeObserver:self |
forKeyPath:@"connected"]; |
} |
- monitoredDevices_.erase(found); |
} |
- (void)observeValueForKeyPath:(NSString*)keyPath |
ofObject:(id)object |
change:(NSDictionary*)change |
context:(void*)context { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
if ([keyPath isEqual:@"suspended"]) |
- receiver_->OnDeviceChanged(); |
+ onDeviceChangedCallback_.Run(); |
if ([keyPath isEqual:@"connected"]) |
[self stopObserving:static_cast<CrAVCaptureDevice*>(context)]; |
} |