OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
Robert Sesek
2014/07/07 22:47:50
Unless QTKit is going away soon, I think it may ma
mcasas
2014/07/08 15:11:45
Total +1 !!
http://crbug.com/392076
| |
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" |
17 #include "media/base/bind_to_current_loop.h" | |
16 #import "media/video/capture/mac/avfoundation_glue.h" | 18 #import "media/video/capture/mac/avfoundation_glue.h" |
17 | 19 |
18 namespace { | 20 namespace { |
19 | 21 |
20 // This class is used to keep track of system devices names and their types. | 22 // This class is used to keep track of system devices names and their types. |
21 class DeviceInfo { | 23 class DeviceInfo { |
22 public: | 24 public: |
23 enum DeviceType { | 25 enum DeviceType { |
24 kAudio, | 26 kAudio, |
25 kVideo, | 27 kVideo, |
(...skipping 14 matching lines...) Expand all Loading... | |
40 | 42 |
41 const std::string& unique_id() const { return unique_id_; } | 43 const std::string& unique_id() const { return unique_id_; } |
42 DeviceType type() const { return type_; } | 44 DeviceType type() const { return type_; } |
43 | 45 |
44 private: | 46 private: |
45 std::string unique_id_; | 47 std::string unique_id_; |
46 DeviceType type_; | 48 DeviceType type_; |
47 // Allow generated copy constructor and assignment. | 49 // Allow generated copy constructor and assignment. |
48 }; | 50 }; |
49 | 51 |
52 class CrAVCaptureDeviceRefCounted : | |
Robert Sesek
2014/07/07 22:47:50
I don't understand the point of this class. CrAVCa
mcasas
2014/07/08 15:11:46
Gone; you're right it wasn't needed.
| |
53 public base::RefCountedThreadSafe<CrAVCaptureDeviceRefCounted> { | |
54 public: | |
55 explicit CrAVCaptureDeviceRefCounted(CrAVCaptureDevice* device) | |
56 : device_(device) {} | |
57 private: | |
58 friend class base::RefCountedThreadSafe<CrAVCaptureDeviceRefCounted>; | |
59 ~CrAVCaptureDeviceRefCounted() {}; | |
60 | |
61 CrAVCaptureDevice* device_; | |
62 }; | |
63 | |
50 // Base abstract class used by DeviceMonitorMac to interact with either a QTKit | 64 // Base abstract class used by DeviceMonitorMac to interact with either a QTKit |
51 // or an AVFoundation implementation of events and notifications. | 65 // or an AVFoundation implementation of events and notifications. |
52 class DeviceMonitorMacImpl { | 66 class DeviceMonitorMacImpl { |
53 public: | 67 public: |
54 explicit DeviceMonitorMacImpl(content::DeviceMonitorMac* monitor) | 68 explicit DeviceMonitorMacImpl(content::DeviceMonitorMac* monitor) |
55 : monitor_(monitor), | 69 : monitor_(monitor), |
56 cached_devices_(), | 70 cached_devices_(), |
57 device_arrival_(nil), | 71 device_arrival_(nil), |
58 device_removal_(nil) { | 72 device_removal_(nil) { |
59 DCHECK(monitor); | 73 DCHECK(monitor); |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 ConsolidateDevicesListAndNotify(snapshot_devices); | 223 ConsolidateDevicesListAndNotify(snapshot_devices); |
210 } | 224 } |
211 | 225 |
212 // Forward declaration for use by CrAVFoundationDeviceObserver. | 226 // Forward declaration for use by CrAVFoundationDeviceObserver. |
213 class SuspendObserverDelegate; | 227 class SuspendObserverDelegate; |
214 | 228 |
215 } // namespace | 229 } // namespace |
216 | 230 |
217 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ | 231 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ |
218 // classes cannot observe Key-Values directly. Created, manipulated, and | 232 // classes cannot observe Key-Values directly. Created, manipulated, and |
219 // destroyed on the Device Thread by SuspendedObserverDelegate. | 233 // destroyed on the UI Thread by SuspendObserverDelegate. |
220 @interface CrAVFoundationDeviceObserver : NSObject { | 234 @interface CrAVFoundationDeviceObserver : NSObject { |
221 @private | 235 @private |
222 SuspendObserverDelegate* receiver_; // weak | 236 base::Closure onDeviceChangedCallback_; |
Robert Sesek
2014/07/07 22:47:49
nit: blank line after, and a comment (e.g. on what
mcasas
2014/07/08 15:11:45
Done.
| |
223 // Member to keep track of the devices we are already monitoring. | 237 // Member to keep track of the devices we are already monitoring. |
224 std::set<CrAVCaptureDevice*> monitoredDevices_; | 238 std::set<CrAVCaptureDevice*> monitoredDevices_; |
225 } | 239 } |
226 | 240 |
227 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver; | 241 - (id)initWithOnChangedCallback:(const base::Closure&)callback; |
228 - (void)startObserving:(CrAVCaptureDevice*)device; | 242 - (void)startObserving:(CrAVCaptureDevice*)device; |
229 - (void)stopObserving:(CrAVCaptureDevice*)device; | 243 - (void)stopObserving:(CrAVCaptureDevice*)device; |
230 | 244 |
231 @end | 245 @end |
232 | 246 |
233 namespace { | 247 namespace { |
234 | 248 |
235 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. | 249 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. |
236 // Provides a callback for this device observer to indicate that there has been | 250 // AVFoundationMonitorImpl creates and destroys it in UI thread. Runs the |
237 // a device change of some kind. Created by AVFoundationMonitorImpl in UI thread | 251 // expensive device enumerations in OnDeviceChanged() and StartObserver() on |
238 // but living in Device Thread. | 252 // Device Thread. |
239 class SuspendObserverDelegate : | 253 class SuspendObserverDelegate : |
240 public base::RefCountedThreadSafe<SuspendObserverDelegate> { | 254 public base::RefCountedThreadSafe<SuspendObserverDelegate> { |
241 public: | 255 public: |
242 explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) | 256 explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) |
243 : avfoundation_monitor_impl_(monitor) { | 257 : avfoundation_monitor_impl_(monitor) { |
Robert Sesek
2014/07/07 22:47:49
Please move the ctor and dtor out of line, like th
mcasas
2014/07/08 15:11:46
Done.
| |
258 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
244 device_thread_checker_.DetachFromThread(); | 259 device_thread_checker_.DetachFromThread(); |
245 } | 260 } |
246 | 261 |
247 void OnDeviceChanged(); | 262 void OnDeviceChanged(); |
248 void StartObserver(); | 263 void StartObserver(); |
249 void ResetDeviceMonitorOnUIThread(); | 264 void ResetDeviceMonitorOnUIThread(); |
250 | 265 |
251 private: | 266 private: |
252 friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; | 267 friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; |
253 | 268 |
254 virtual ~SuspendObserverDelegate() {} | 269 virtual ~SuspendObserverDelegate() { |
270 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
Robert Sesek
2014/07/07 22:47:49
You could add a |using content::BrowserThread| if
mcasas
2014/07/08 15:11:46
Great, less verbosity, done.
| |
271 } | |
255 | 272 |
256 void OnDeviceChangedOnUIThread( | 273 void OnDeviceChangedOnUIThread( |
257 const std::vector<DeviceInfo>& snapshot_devices); | 274 const std::vector<DeviceInfo>& snapshot_devices); |
258 | 275 |
259 base::ThreadChecker device_thread_checker_; | 276 base::ThreadChecker device_thread_checker_; |
277 // Created, used and released in UI thread. | |
260 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; | 278 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; |
261 DeviceMonitorMacImpl* avfoundation_monitor_impl_; | 279 DeviceMonitorMacImpl* avfoundation_monitor_impl_; |
262 }; | 280 }; |
263 | 281 |
264 void SuspendObserverDelegate::OnDeviceChanged() { | 282 void SuspendObserverDelegate::OnDeviceChanged() { |
265 DCHECK(device_thread_checker_.CalledOnValidThread()); | 283 DCHECK(device_thread_checker_.CalledOnValidThread()); |
266 NSArray* devices = [AVCaptureDeviceGlue devices]; | |
267 std::vector<DeviceInfo> snapshot_devices; | 284 std::vector<DeviceInfo> snapshot_devices; |
268 for (CrAVCaptureDevice* device in devices) { | 285 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) { |
269 [suspend_observer_ startObserving:device]; | 286 scoped_refptr<CrAVCaptureDeviceRefCounted> device_refptr = |
287 new CrAVCaptureDeviceRefCounted(device); | |
288 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
289 base::BindBlock(^{ [suspend_observer_ startObserving:device]; })); | |
270 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && | 290 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && |
271 [device isSuspended]; | 291 [device isSuspended]; |
272 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; | 292 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; |
273 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { | 293 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { |
274 if (suspended) | 294 if (suspended) |
275 continue; | 295 continue; |
276 device_type = DeviceInfo::kVideo; | 296 device_type = DeviceInfo::kVideo; |
277 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { | 297 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { |
278 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; | 298 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; |
279 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { | 299 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { |
280 device_type = DeviceInfo::kAudio; | 300 device_type = DeviceInfo::kAudio; |
281 } | 301 } |
282 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], | 302 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], |
283 device_type)); | 303 device_type)); |
284 } | 304 } |
285 // Post the consolidation of enumerated devices to be done on UI thread. | 305 // Post the consolidation of enumerated devices to be done on UI thread. |
286 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 306 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
287 base::Bind(&SuspendObserverDelegate::OnDeviceChangedOnUIThread, | 307 base::Bind(&SuspendObserverDelegate::OnDeviceChangedOnUIThread, |
288 this, snapshot_devices)); | 308 this, snapshot_devices)); |
289 } | 309 } |
290 | 310 |
291 void SuspendObserverDelegate::StartObserver() { | 311 void SuspendObserverDelegate::StartObserver() { |
292 DCHECK(device_thread_checker_.CalledOnValidThread()); | 312 DCHECK(device_thread_checker_.CalledOnValidThread()); |
293 suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] | 313 |
294 initWithChangeReceiver:this]); | 314 base::Closure on_device_changed_callback = media::BindToCurrentLoop( |
295 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) | 315 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, this)); |
296 [suspend_observer_ startObserving:device]; | 316 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
317 base::BindBlock(^{ suspend_observer_.reset( | |
Robert Sesek
2014/07/07 22:47:50
nit: keep ^{ on this line but start the content of
mcasas
2014/07/08 15:11:45
Done.
| |
318 [[CrAVFoundationDeviceObserver alloc] | |
319 initWithOnChangedCallback:on_device_changed_callback]); | |
320 })); | |
321 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) { | |
322 scoped_refptr<CrAVCaptureDeviceRefCounted> device_refptr = | |
323 new CrAVCaptureDeviceRefCounted(device); | |
324 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
325 base::BindBlock(^{ [suspend_observer_ startObserving:device]; })); | |
326 } | |
297 } | 327 } |
298 | 328 |
299 void SuspendObserverDelegate::ResetDeviceMonitorOnUIThread() { | 329 void SuspendObserverDelegate::ResetDeviceMonitorOnUIThread() { |
300 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 330 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
301 avfoundation_monitor_impl_ = NULL; | 331 avfoundation_monitor_impl_ = NULL; |
302 } | 332 } |
303 | 333 |
304 void SuspendObserverDelegate::OnDeviceChangedOnUIThread( | 334 void SuspendObserverDelegate::OnDeviceChangedOnUIThread( |
305 const std::vector<DeviceInfo>& snapshot_devices) { | 335 const std::vector<DeviceInfo>& snapshot_devices) { |
306 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 336 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()); | 407 DCHECK(thread_checker_.CalledOnValidThread()); |
378 device_task_runner_->PostTask(FROM_HERE, | 408 device_task_runner_->PostTask(FROM_HERE, |
379 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, | 409 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, |
380 suspend_observer_delegate_)); | 410 suspend_observer_delegate_)); |
381 } | 411 } |
382 | 412 |
383 } // namespace | 413 } // namespace |
384 | 414 |
385 @implementation CrAVFoundationDeviceObserver | 415 @implementation CrAVFoundationDeviceObserver |
386 | 416 |
387 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver { | 417 - (id)initWithOnChangedCallback:(const base::Closure&)callback { |
418 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
388 if ((self = [super init])) { | 419 if ((self = [super init])) { |
389 DCHECK(receiver != NULL); | 420 DCHECK(!callback.is_null()); |
390 receiver_ = receiver; | 421 onDeviceChangedCallback_ = callback; |
391 } | 422 } |
392 return self; | 423 return self; |
393 } | 424 } |
394 | 425 |
395 - (void)dealloc { | 426 - (void)dealloc { |
427 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
396 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); | 428 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); |
397 while (it != monitoredDevices_.end()) | 429 while (it != monitoredDevices_.end()) { |
398 [self stopObserving:*it++]; | 430 [self removeObservers:*it]; |
431 monitoredDevices_.erase(it++); | |
432 } | |
399 [super dealloc]; | 433 [super dealloc]; |
400 } | 434 } |
401 | 435 |
402 - (void)startObserving:(CrAVCaptureDevice*)device { | 436 - (void)startObserving:(CrAVCaptureDevice*)device { |
437 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
403 DCHECK(device != nil); | 438 DCHECK(device != nil); |
404 // Skip this device if there are already observers connected to it. | 439 // Skip this device if there are already observers connected to it. |
405 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != | 440 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != |
406 monitoredDevices_.end()) { | 441 monitoredDevices_.end()) { |
407 return; | 442 return; |
408 } | 443 } |
409 [device addObserver:self | 444 [device addObserver:self |
410 forKeyPath:@"suspended" | 445 forKeyPath:@"suspended" |
411 options:0 | 446 options:0 |
412 context:device]; | 447 context:device]; |
413 [device addObserver:self | 448 [device addObserver:self |
414 forKeyPath:@"connected" | 449 forKeyPath:@"connected" |
415 options:0 | 450 options:0 |
416 context:device]; | 451 context:device]; |
417 monitoredDevices_.insert(device); | 452 monitoredDevices_.insert(device); |
418 } | 453 } |
419 | 454 |
420 - (void)stopObserving:(CrAVCaptureDevice*)device { | 455 - (void)stopObserving:(CrAVCaptureDevice*)device { |
456 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
421 DCHECK(device != nil); | 457 DCHECK(device != nil); |
422 std::set<CrAVCaptureDevice*>::iterator found = | 458 std::set<CrAVCaptureDevice*>::iterator found = |
423 std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); | 459 std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); |
424 DCHECK(found != monitoredDevices_.end()); | 460 DCHECK(found != monitoredDevices_.end()); |
425 // Every so seldom, |device| might be gone when getting here, in that case | 461 [self removeObservers:*found]; |
426 // removing the observer causes a crash. Try to avoid it by checking sanity of | 462 monitoredDevices_.erase(found); |
427 // the |device| via its -observationInfo. http://crbug.com/371271. | 463 } |
464 | |
465 - (void)removeObservers:(CrAVCaptureDevice*)device { | |
466 // Check sanity of |device| via its -observationInfo. http://crbug.com/371271. | |
428 if ([device observationInfo]) { | 467 if ([device observationInfo]) { |
429 [device removeObserver:self | 468 [device removeObserver:self |
430 forKeyPath:@"suspended"]; | 469 forKeyPath:@"suspended"]; |
431 [device removeObserver:self | 470 [device removeObserver:self |
432 forKeyPath:@"connected"]; | 471 forKeyPath:@"connected"]; |
433 } | 472 } |
434 monitoredDevices_.erase(found); | |
435 } | 473 } |
436 | 474 |
437 - (void)observeValueForKeyPath:(NSString*)keyPath | 475 - (void)observeValueForKeyPath:(NSString*)keyPath |
438 ofObject:(id)object | 476 ofObject:(id)object |
439 change:(NSDictionary*)change | 477 change:(NSDictionary*)change |
440 context:(void*)context { | 478 context:(void*)context { |
479 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
441 if ([keyPath isEqual:@"suspended"]) | 480 if ([keyPath isEqual:@"suspended"]) |
442 receiver_->OnDeviceChanged(); | 481 onDeviceChangedCallback_.Run(); |
Robert Sesek
2014/07/07 22:47:50
Won't this run the callback on the UI thread, rath
mcasas
2014/07/08 15:11:46
l.314 media::BindToCurrentLoop does the magic of c
Robert Sesek
2014/07/08 17:05:39
Looks like this got dropped in the latest patch se
| |
443 if ([keyPath isEqual:@"connected"]) | 482 if ([keyPath isEqual:@"connected"]) |
444 [self stopObserving:static_cast<CrAVCaptureDevice*>(context)]; | 483 [self stopObserving:static_cast<CrAVCaptureDevice*>(context)]; |
445 } | 484 } |
446 | 485 |
447 @end // @implementation CrAVFoundationDeviceObserver | 486 @end // @implementation CrAVFoundationDeviceObserver |
448 | 487 |
449 namespace content { | 488 namespace content { |
450 | 489 |
451 DeviceMonitorMac::DeviceMonitorMac() { | 490 DeviceMonitorMac::DeviceMonitorMac() { |
452 // Both QTKit and AVFoundation do not need to be fired up until the user | 491 // Both QTKit and AVFoundation do not need to be fired up until the user |
(...skipping 18 matching lines...) Expand all Loading... | |
471 } | 510 } |
472 | 511 |
473 void DeviceMonitorMac::NotifyDeviceChanged( | 512 void DeviceMonitorMac::NotifyDeviceChanged( |
474 base::SystemMonitor::DeviceType type) { | 513 base::SystemMonitor::DeviceType type) { |
475 DCHECK(thread_checker_.CalledOnValidThread()); | 514 DCHECK(thread_checker_.CalledOnValidThread()); |
476 // TODO(xians): Remove the global variable for SystemMonitor. | 515 // TODO(xians): Remove the global variable for SystemMonitor. |
477 base::SystemMonitor::Get()->ProcessDevicesChanged(type); | 516 base::SystemMonitor::Get()->ProcessDevicesChanged(type); |
478 } | 517 } |
479 | 518 |
480 } // namespace content | 519 } // namespace content |
OLD | NEW |