Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/device_monitor_mac.h" | 5 #include "content/browser/device_monitor_mac.h" |
| 6 | 6 |
| 7 #import <QTKit/QTKit.h> | 7 #import <QTKit/QTKit.h> |
| 8 | 8 |
| 9 #include <set> | 9 #include <set> |
| 10 | 10 |
| 11 #include "base/bind_helpers.h" | 11 #include "base/bind_helpers.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/mac/bind_objc_block.h" | |
| 13 #include "base/mac/scoped_nsobject.h" | 14 #include "base/mac/scoped_nsobject.h" |
| 14 #include "base/threading/thread_checker.h" | 15 #include "base/threading/thread_checker.h" |
| 15 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 16 #import "media/video/capture/mac/avfoundation_glue.h" | 17 #import "media/video/capture/mac/avfoundation_glue.h" |
| 17 | 18 |
| 18 namespace { | 19 namespace { |
| 19 | 20 |
| 20 // This class is used to keep track of system devices names and their types. | 21 // This class is used to keep track of system devices names and their types. |
| 21 class DeviceInfo { | 22 class DeviceInfo { |
| 22 public: | 23 public: |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 209 ConsolidateDevicesListAndNotify(snapshot_devices); | 210 ConsolidateDevicesListAndNotify(snapshot_devices); |
| 210 } | 211 } |
| 211 | 212 |
| 212 // Forward declaration for use by CrAVFoundationDeviceObserver. | 213 // Forward declaration for use by CrAVFoundationDeviceObserver. |
| 213 class SuspendObserverDelegate; | 214 class SuspendObserverDelegate; |
| 214 | 215 |
| 215 } // namespace | 216 } // namespace |
| 216 | 217 |
| 217 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ | 218 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ |
| 218 // classes cannot observe Key-Values directly. Created, manipulated, and | 219 // classes cannot observe Key-Values directly. Created, manipulated, and |
| 219 // destroyed on the Device Thread by SuspendedObserverDelegate. | 220 // destroyed on the UI Thread by SuspendObserverDelegate. |
| 220 @interface CrAVFoundationDeviceObserver : NSObject { | 221 @interface CrAVFoundationDeviceObserver : NSObject { |
| 221 @private | 222 @private |
| 222 SuspendObserverDelegate* receiver_; // weak | 223 scoped_refptr<base::MessageLoopProxy> device_thread_message_loop_; |
| 224 scoped_refptr<SuspendObserverDelegate> receiver_; | |
|
tommi (sloooow) - chröme
2014/07/03 13:30:17
instead of holding on to the loop + receiver, what
mcasas
2014/07/04 14:08:58
That makes sense. Will do.
| |
| 223 // Member to keep track of the devices we are already monitoring. | 225 // Member to keep track of the devices we are already monitoring. |
| 224 std::set<CrAVCaptureDevice*> monitoredDevices_; | 226 std::set<CrAVCaptureDevice*> monitoredDevices_; |
| 225 } | 227 } |
| 226 | 228 |
| 227 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver; | 229 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver |
| 230 deviceMessageLoop:(scoped_refptr<base::MessageLoopProxy>)loop; | |
| 228 - (void)startObserving:(CrAVCaptureDevice*)device; | 231 - (void)startObserving:(CrAVCaptureDevice*)device; |
| 229 - (void)stopObserving:(CrAVCaptureDevice*)device; | 232 - (void)stopObserving:(CrAVCaptureDevice*)device; |
| 230 | 233 |
| 231 @end | 234 @end |
| 232 | 235 |
| 233 namespace { | 236 namespace { |
| 234 | 237 |
| 235 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. | 238 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. |
| 236 // Provides a callback for this device observer to indicate that there has been | 239 // AVFoundationMonitorImpl creates and destroys it in UI thread. Runs the |
| 237 // a device change of some kind. Created by AVFoundationMonitorImpl in UI thread | 240 // expensive device enumerations in OnDeviceChanged() and StartObserver() on |
| 238 // but living in Device Thread. | 241 // Device Thread. |
| 239 class SuspendObserverDelegate : | 242 class SuspendObserverDelegate : |
| 240 public base::RefCountedThreadSafe<SuspendObserverDelegate> { | 243 public base::RefCountedThreadSafe<SuspendObserverDelegate> { |
| 241 public: | 244 public: |
| 242 explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) | 245 explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) |
| 243 : avfoundation_monitor_impl_(monitor) { | 246 : avfoundation_monitor_impl_(monitor) { |
| 247 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 244 device_thread_checker_.DetachFromThread(); | 248 device_thread_checker_.DetachFromThread(); |
| 245 } | 249 } |
| 246 | 250 |
| 247 void OnDeviceChanged(); | 251 void OnDeviceChanged(); |
| 248 void StartObserver(); | 252 void StartObserver(); |
| 249 void ResetDeviceMonitorOnUIThread(); | 253 void ResetDeviceMonitorOnUIThread(); |
| 250 | 254 |
| 251 private: | 255 private: |
| 252 friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; | 256 friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; |
| 253 | 257 |
| 254 virtual ~SuspendObserverDelegate() {} | 258 virtual ~SuspendObserverDelegate() { |
| 259 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 260 } | |
| 255 | 261 |
| 256 void OnDeviceChangedOnUIThread( | 262 void OnDeviceChangedOnUIThread( |
| 257 const std::vector<DeviceInfo>& snapshot_devices); | 263 const std::vector<DeviceInfo>& snapshot_devices); |
| 258 | 264 |
| 259 base::ThreadChecker device_thread_checker_; | 265 base::ThreadChecker device_thread_checker_; |
| 266 // Created, used and released in UI thread. | |
| 260 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; | 267 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; |
| 261 DeviceMonitorMacImpl* avfoundation_monitor_impl_; | 268 DeviceMonitorMacImpl* avfoundation_monitor_impl_; |
| 262 }; | 269 }; |
| 263 | 270 |
| 264 void SuspendObserverDelegate::OnDeviceChanged() { | 271 void SuspendObserverDelegate::OnDeviceChanged() { |
| 265 DCHECK(device_thread_checker_.CalledOnValidThread()); | 272 DCHECK(device_thread_checker_.CalledOnValidThread()); |
| 266 NSArray* devices = [AVCaptureDeviceGlue devices]; | 273 NSArray* devices = [AVCaptureDeviceGlue devices]; |
| 267 std::vector<DeviceInfo> snapshot_devices; | 274 std::vector<DeviceInfo> snapshot_devices; |
| 268 for (CrAVCaptureDevice* device in devices) { | 275 for (CrAVCaptureDevice* device in devices) { |
| 269 [suspend_observer_ startObserving:device]; | 276 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
| 277 base::BindBlock(^{ [suspend_observer_ startObserving:device]; })); | |
|
tommi (sloooow) - chröme
2014/07/03 13:30:17
does this also addref the device pointer?
If not,
mcasas
2014/07/04 14:08:58
|device| is a naked pointer, comes verbatim from t
tommi (sloooow) - chröme
2014/07/07 07:41:07
Yes. If you think about the case where refcount i
mcasas
2014/07/08 15:11:45
We tackled this topic offline and, basically, solv
| |
| 270 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && | 278 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && |
| 271 [device isSuspended]; | 279 [device isSuspended]; |
| 272 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; | 280 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; |
| 273 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { | 281 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { |
| 274 if (suspended) | 282 if (suspended) |
| 275 continue; | 283 continue; |
| 276 device_type = DeviceInfo::kVideo; | 284 device_type = DeviceInfo::kVideo; |
| 277 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { | 285 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { |
| 278 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; | 286 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; |
| 279 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { | 287 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { |
| 280 device_type = DeviceInfo::kAudio; | 288 device_type = DeviceInfo::kAudio; |
| 281 } | 289 } |
| 282 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], | 290 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], |
| 283 device_type)); | 291 device_type)); |
| 284 } | 292 } |
| 285 // Post the consolidation of enumerated devices to be done on UI thread. | 293 // Post the consolidation of enumerated devices to be done on UI thread. |
| 286 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 294 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
| 287 base::Bind(&SuspendObserverDelegate::OnDeviceChangedOnUIThread, | 295 base::Bind(&SuspendObserverDelegate::OnDeviceChangedOnUIThread, |
| 288 this, snapshot_devices)); | 296 this, snapshot_devices)); |
| 289 } | 297 } |
| 290 | 298 |
| 291 void SuspendObserverDelegate::StartObserver() { | 299 void SuspendObserverDelegate::StartObserver() { |
| 292 DCHECK(device_thread_checker_.CalledOnValidThread()); | 300 DCHECK(device_thread_checker_.CalledOnValidThread()); |
| 293 suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] | 301 scoped_refptr<base::MessageLoopProxy> device_message_loop = |
| 294 initWithChangeReceiver:this]); | 302 base::MessageLoopProxy::current(); |
| 295 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) | 303 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
| 296 [suspend_observer_ startObserving:device]; | 304 base::BindBlock(^{ suspend_observer_.reset( |
| 305 [[CrAVFoundationDeviceObserver alloc] | |
| 306 initWithChangeReceiver:this | |
| 307 deviceMessageLoop:device_message_loop]); | |
|
tommi (sloooow) - chröme
2014/07/03 13:30:17
Is passing the device_message_loop this way to the
mcasas
2014/07/04 14:08:58
|device_message_loop| is cached inside CrAVFoundat
| |
| 308 })); | |
| 309 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) { | |
| 310 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
| 311 base::BindBlock(^{ [suspend_observer_ startObserving:device]; })); | |
|
tommi (sloooow) - chröme
2014/07/03 13:30:17
seems like we're posting many tasks to the UI thre
mcasas
2014/07/04 14:08:58
l.309 ought to be executed in Device Thread, is th
| |
| 312 } | |
| 297 } | 313 } |
| 298 | 314 |
| 299 void SuspendObserverDelegate::ResetDeviceMonitorOnUIThread() { | 315 void SuspendObserverDelegate::ResetDeviceMonitorOnUIThread() { |
| 300 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 316 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 301 avfoundation_monitor_impl_ = NULL; | 317 avfoundation_monitor_impl_ = NULL; |
| 302 } | 318 } |
| 303 | 319 |
| 304 void SuspendObserverDelegate::OnDeviceChangedOnUIThread( | 320 void SuspendObserverDelegate::OnDeviceChangedOnUIThread( |
| 305 const std::vector<DeviceInfo>& snapshot_devices) { | 321 const std::vector<DeviceInfo>& snapshot_devices) { |
| 306 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 322 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 377 DCHECK(thread_checker_.CalledOnValidThread()); | 393 DCHECK(thread_checker_.CalledOnValidThread()); |
| 378 device_task_runner_->PostTask(FROM_HERE, | 394 device_task_runner_->PostTask(FROM_HERE, |
| 379 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, | 395 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, |
| 380 suspend_observer_delegate_)); | 396 suspend_observer_delegate_)); |
| 381 } | 397 } |
| 382 | 398 |
| 383 } // namespace | 399 } // namespace |
| 384 | 400 |
| 385 @implementation CrAVFoundationDeviceObserver | 401 @implementation CrAVFoundationDeviceObserver |
| 386 | 402 |
| 387 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver { | 403 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver |
| 404 deviceMessageLoop:(scoped_refptr<base::MessageLoopProxy>)loop { | |
| 405 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 388 if ((self = [super init])) { | 406 if ((self = [super init])) { |
| 389 DCHECK(receiver != NULL); | 407 DCHECK(receiver != NULL); |
| 390 receiver_ = receiver; | 408 receiver_ = receiver; |
| 409 device_thread_message_loop_ = loop; | |
| 391 } | 410 } |
| 392 return self; | 411 return self; |
| 393 } | 412 } |
| 394 | 413 |
| 395 - (void)dealloc { | 414 - (void)dealloc { |
| 415 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 396 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); | 416 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); |
| 397 while (it != monitoredDevices_.end()) | 417 while (it != monitoredDevices_.end()) { |
| 398 [self stopObserving:*it++]; | 418 [self removeObservers:*it]; |
| 419 monitoredDevices_.erase(it++); | |
| 420 } | |
| 399 [super dealloc]; | 421 [super dealloc]; |
| 400 } | 422 } |
| 401 | 423 |
| 402 - (void)startObserving:(CrAVCaptureDevice*)device { | 424 - (void)startObserving:(CrAVCaptureDevice*)device { |
| 425 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 403 DCHECK(device != nil); | 426 DCHECK(device != nil); |
| 404 // Skip this device if there are already observers connected to it. | 427 // Skip this device if there are already observers connected to it. |
| 405 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != | 428 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != |
| 406 monitoredDevices_.end()) { | 429 monitoredDevices_.end()) { |
| 407 return; | 430 return; |
| 408 } | 431 } |
| 409 [device addObserver:self | 432 [device addObserver:self |
| 410 forKeyPath:@"suspended" | 433 forKeyPath:@"suspended" |
| 411 options:0 | 434 options:0 |
| 412 context:device]; | 435 context:device]; |
| 413 [device addObserver:self | 436 [device addObserver:self |
| 414 forKeyPath:@"connected" | 437 forKeyPath:@"connected" |
| 415 options:0 | 438 options:0 |
| 416 context:device]; | 439 context:device]; |
| 417 monitoredDevices_.insert(device); | 440 monitoredDevices_.insert(device); |
| 418 } | 441 } |
| 419 | 442 |
| 420 - (void)stopObserving:(CrAVCaptureDevice*)device { | 443 - (void)stopObserving:(CrAVCaptureDevice*)device { |
| 444 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
| 421 DCHECK(device != nil); | 445 DCHECK(device != nil); |
| 422 std::set<CrAVCaptureDevice*>::iterator found = | 446 std::set<CrAVCaptureDevice*>::iterator found = |
| 423 std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); | 447 std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); |
| 424 DCHECK(found != monitoredDevices_.end()); | 448 DCHECK(found != monitoredDevices_.end()); |
| 425 // Every so seldom, |device| might be gone when getting here, in that case | 449 [self removeObservers:*found]; |
| 426 // removing the observer causes a crash. Try to avoid it by checking sanity of | 450 monitoredDevices_.erase(found); |
| 427 // the |device| via its -observationInfo. http://crbug.com/371271. | 451 } |
| 452 | |
| 453 - (void)removeObservers:(CrAVCaptureDevice*)device { | |
| 454 // Check sanity of |device| via its -observationInfo. http://crbug.com/371271. | |
| 428 if ([device observationInfo]) { | 455 if ([device observationInfo]) { |
| 429 [device removeObserver:self | 456 [device removeObserver:self |
| 430 forKeyPath:@"suspended"]; | 457 forKeyPath:@"suspended"]; |
| 431 [device removeObserver:self | 458 [device removeObserver:self |
| 432 forKeyPath:@"connected"]; | 459 forKeyPath:@"connected"]; |
| 433 } | 460 } |
| 434 monitoredDevices_.erase(found); | |
| 435 } | 461 } |
| 436 | 462 |
| 437 - (void)observeValueForKeyPath:(NSString*)keyPath | 463 - (void)observeValueForKeyPath:(NSString*)keyPath |
| 438 ofObject:(id)object | 464 ofObject:(id)object |
| 439 change:(NSDictionary*)change | 465 change:(NSDictionary*)change |
| 440 context:(void*)context { | 466 context:(void*)context { |
| 441 if ([keyPath isEqual:@"suspended"]) | 467 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 442 receiver_->OnDeviceChanged(); | 468 if ([keyPath isEqual:@"suspended"]) { |
| 469 device_thread_message_loop_->PostTask(FROM_HERE, | |
| 470 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, receiver_)); | |
| 471 } | |
| 443 if ([keyPath isEqual:@"connected"]) | 472 if ([keyPath isEqual:@"connected"]) |
| 444 [self stopObserving:static_cast<CrAVCaptureDevice*>(context)]; | 473 [self stopObserving:static_cast<CrAVCaptureDevice*>(context)]; |
| 445 } | 474 } |
| 446 | 475 |
| 447 @end // @implementation CrAVFoundationDeviceObserver | 476 @end // @implementation CrAVFoundationDeviceObserver |
| 448 | 477 |
| 449 namespace content { | 478 namespace content { |
| 450 | 479 |
| 451 DeviceMonitorMac::DeviceMonitorMac() { | 480 DeviceMonitorMac::DeviceMonitorMac() { |
| 452 // Both QTKit and AVFoundation do not need to be fired up until the user | 481 // Both QTKit and AVFoundation do not need to be fired up until the user |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 471 } | 500 } |
| 472 | 501 |
| 473 void DeviceMonitorMac::NotifyDeviceChanged( | 502 void DeviceMonitorMac::NotifyDeviceChanged( |
| 474 base::SystemMonitor::DeviceType type) { | 503 base::SystemMonitor::DeviceType type) { |
| 475 DCHECK(thread_checker_.CalledOnValidThread()); | 504 DCHECK(thread_checker_.CalledOnValidThread()); |
| 476 // TODO(xians): Remove the global variable for SystemMonitor. | 505 // TODO(xians): Remove the global variable for SystemMonitor. |
| 477 base::SystemMonitor::Get()->ProcessDevicesChanged(type); | 506 base::SystemMonitor::Get()->ProcessDevicesChanged(type); |
| 478 } | 507 } |
| 479 | 508 |
| 480 } // namespace content | 509 } // namespace content |
| OLD | NEW |