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 |
| 19 using content::BrowserThread; |
| 20 |
18 namespace { | 21 namespace { |
19 | 22 |
20 // This class is used to keep track of system devices names and their types. | 23 // This class is used to keep track of system devices names and their types. |
21 class DeviceInfo { | 24 class DeviceInfo { |
22 public: | 25 public: |
23 enum DeviceType { | 26 enum DeviceType { |
24 kAudio, | 27 kAudio, |
25 kVideo, | 28 kVideo, |
26 kMuxed, | 29 kMuxed, |
27 kUnknown, | 30 kUnknown, |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 ConsolidateDevicesListAndNotify(snapshot_devices); | 212 ConsolidateDevicesListAndNotify(snapshot_devices); |
210 } | 213 } |
211 | 214 |
212 // Forward declaration for use by CrAVFoundationDeviceObserver. | 215 // Forward declaration for use by CrAVFoundationDeviceObserver. |
213 class SuspendObserverDelegate; | 216 class SuspendObserverDelegate; |
214 | 217 |
215 } // namespace | 218 } // namespace |
216 | 219 |
217 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ | 220 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ |
218 // classes cannot observe Key-Values directly. Created, manipulated, and | 221 // classes cannot observe Key-Values directly. Created, manipulated, and |
219 // destroyed on the Device Thread by SuspendedObserverDelegate. | 222 // destroyed on the UI Thread by SuspendObserverDelegate. |
220 @interface CrAVFoundationDeviceObserver : NSObject { | 223 @interface CrAVFoundationDeviceObserver : NSObject { |
221 @private | 224 @private |
222 SuspendObserverDelegate* receiver_; // weak | 225 // Callback for device changed, has to run on Device Thread. |
| 226 base::Closure onDeviceChangedCallback_; |
| 227 |
223 // Member to keep track of the devices we are already monitoring. | 228 // Member to keep track of the devices we are already monitoring. |
224 std::set<CrAVCaptureDevice*> monitoredDevices_; | 229 std::set<base::scoped_nsobject<CrAVCaptureDevice> > monitoredDevices_; |
225 } | 230 } |
226 | 231 |
227 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver; | 232 - (id)initWithOnChangedCallback:(const base::Closure&)callback; |
228 - (void)startObserving:(CrAVCaptureDevice*)device; | 233 - (void)startObserving:(base::scoped_nsobject<CrAVCaptureDevice>)device; |
229 - (void)stopObserving:(CrAVCaptureDevice*)device; | 234 - (void)stopObserving:(CrAVCaptureDevice*)device; |
| 235 - (void)clearOnDeviceChangedCallback; |
230 | 236 |
231 @end | 237 @end |
232 | 238 |
233 namespace { | 239 namespace { |
234 | 240 |
235 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. | 241 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. |
236 // Provides a callback for this device observer to indicate that there has been | 242 // It is created and destroyed in UI thread by AVFoundationMonitorImpl, and it |
237 // a device change of some kind. Created by AVFoundationMonitorImpl in UI thread | 243 // operates on this thread except for the expensive device enumerations which |
238 // but living in Device Thread. | 244 // are run on Device Thread. |
239 class SuspendObserverDelegate : | 245 class SuspendObserverDelegate : |
240 public base::RefCountedThreadSafe<SuspendObserverDelegate> { | 246 public base::RefCountedThreadSafe<SuspendObserverDelegate> { |
241 public: | 247 public: |
242 explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) | 248 explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor); |
243 : avfoundation_monitor_impl_(monitor) { | |
244 device_thread_checker_.DetachFromThread(); | |
245 } | |
246 | 249 |
247 void OnDeviceChanged(); | 250 // Create |suspend_observer_| for all devices and register OnDeviceChanged() |
248 void StartObserver(); | 251 // as its change callback. Schedule bottom half in DoStartObserver(). |
249 void ResetDeviceMonitorOnUIThread(); | 252 void StartObserver( |
| 253 const scoped_refptr<base::SingleThreadTaskRunner>& device_thread); |
| 254 // Enumerate devices in |device_thread| and run the bottom half in |
| 255 // DoOnDeviceChange(). |suspend_observer_| calls back here on suspend event, |
| 256 // and our parent AVFoundationMonitorImpl calls on connect/disconnect device. |
| 257 void OnDeviceChanged( |
| 258 const scoped_refptr<base::SingleThreadTaskRunner>& device_thread); |
| 259 // Remove the device monitor's weak reference. Remove ourselves as suspend |
| 260 // notification observer from |suspend_observer_|. |
| 261 void ResetDeviceMonitor(); |
250 | 262 |
251 private: | 263 private: |
252 friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; | 264 friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; |
253 | 265 |
254 virtual ~SuspendObserverDelegate() {} | 266 virtual ~SuspendObserverDelegate(); |
255 | 267 |
256 void OnDeviceChangedOnUIThread( | 268 // Bottom half of StartObserver(), starts |suspend_observer_| for all devices. |
257 const std::vector<DeviceInfo>& snapshot_devices); | 269 // Assumes that |devices| has been retained prior to being called, and |
| 270 // releases it internally. |
| 271 void DoStartObserver(NSArray* devices); |
| 272 // Bottom half of OnDeviceChanged(), starts |suspend_observer_| for current |
| 273 // devices and composes a snapshot of them to send it to |
| 274 // |avfoundation_monitor_impl_|. Assumes that |devices| has been retained |
| 275 // prior to being called, and releases it internally. |
| 276 void DoOnDeviceChanged(NSArray* devices); |
258 | 277 |
259 base::ThreadChecker device_thread_checker_; | |
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 SuspendObserverDelegate::SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) |
265 DCHECK(device_thread_checker_.CalledOnValidThread()); | 283 : avfoundation_monitor_impl_(monitor) { |
266 NSArray* devices = [AVCaptureDeviceGlue devices]; | 284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 285 } |
| 286 |
| 287 void SuspendObserverDelegate::StartObserver( |
| 288 const scoped_refptr<base::SingleThreadTaskRunner>& device_thread) { |
| 289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 290 |
| 291 base::Closure on_device_changed_callback = |
| 292 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, |
| 293 this, device_thread); |
| 294 suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] |
| 295 initWithOnChangedCallback:on_device_changed_callback]); |
| 296 |
| 297 // Enumerate the devices in Device thread and post the observers start to be |
| 298 // done on UI thread. The devices array is retained in |device_thread| and |
| 299 // released in DoStartObserver(). |
| 300 base::PostTaskAndReplyWithResult(device_thread, FROM_HERE, |
| 301 base::BindBlock(^{ return [[AVCaptureDeviceGlue devices] retain]; }), |
| 302 base::Bind(&SuspendObserverDelegate::DoStartObserver, this)); |
| 303 } |
| 304 |
| 305 void SuspendObserverDelegate::OnDeviceChanged( |
| 306 const scoped_refptr<base::SingleThreadTaskRunner>& device_thread) { |
| 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 308 // Enumerate the devices in Device thread and post the consolidation of the |
| 309 // new devices and the old ones to be done on UI thread. The devices array |
| 310 // is retained in |device_thread| and released in DoOnDeviceChanged(). |
| 311 PostTaskAndReplyWithResult(device_thread, FROM_HERE, |
| 312 base::BindBlock(^{ return [[AVCaptureDeviceGlue devices] retain]; }), |
| 313 base::Bind(&SuspendObserverDelegate::DoOnDeviceChanged, this)); |
| 314 } |
| 315 |
| 316 void SuspendObserverDelegate::ResetDeviceMonitor() { |
| 317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 318 avfoundation_monitor_impl_ = NULL; |
| 319 [suspend_observer_ clearOnDeviceChangedCallback]; |
| 320 } |
| 321 |
| 322 SuspendObserverDelegate::~SuspendObserverDelegate() { |
| 323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 324 } |
| 325 |
| 326 void SuspendObserverDelegate::DoStartObserver(NSArray* devices) { |
| 327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 328 base::scoped_nsobject<NSArray> auto_release(devices); |
| 329 for (CrAVCaptureDevice* device in devices) { |
| 330 base::scoped_nsobject<CrAVCaptureDevice> device_ptr([device retain]); |
| 331 [suspend_observer_ startObserving:device_ptr]; |
| 332 } |
| 333 } |
| 334 |
| 335 void SuspendObserverDelegate::DoOnDeviceChanged(NSArray* devices) { |
| 336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 337 base::scoped_nsobject<NSArray> auto_release(devices); |
267 std::vector<DeviceInfo> snapshot_devices; | 338 std::vector<DeviceInfo> snapshot_devices; |
268 for (CrAVCaptureDevice* device in devices) { | 339 for (CrAVCaptureDevice* device in devices) { |
269 [suspend_observer_ startObserving:device]; | 340 base::scoped_nsobject<CrAVCaptureDevice> device_ptr([device retain]); |
| 341 [suspend_observer_ startObserving:device_ptr]; |
| 342 |
270 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && | 343 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && |
271 [device isSuspended]; | 344 [device isSuspended]; |
272 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; | 345 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; |
273 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { | 346 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { |
274 if (suspended) | 347 if (suspended) |
275 continue; | 348 continue; |
276 device_type = DeviceInfo::kVideo; | 349 device_type = DeviceInfo::kVideo; |
277 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { | 350 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { |
278 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; | 351 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; |
279 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { | 352 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { |
280 device_type = DeviceInfo::kAudio; | 353 device_type = DeviceInfo::kAudio; |
281 } | 354 } |
282 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], | 355 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], |
283 device_type)); | 356 device_type)); |
284 } | 357 } |
285 // Post the consolidation of enumerated devices to be done on UI thread. | |
286 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
287 base::Bind(&SuspendObserverDelegate::OnDeviceChangedOnUIThread, | |
288 this, snapshot_devices)); | |
289 } | |
290 | 358 |
291 void SuspendObserverDelegate::StartObserver() { | |
292 DCHECK(device_thread_checker_.CalledOnValidThread()); | |
293 suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] | |
294 initWithChangeReceiver:this]); | |
295 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) | |
296 [suspend_observer_ startObserving:device]; | |
297 } | |
298 | |
299 void SuspendObserverDelegate::ResetDeviceMonitorOnUIThread() { | |
300 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
301 avfoundation_monitor_impl_ = NULL; | |
302 } | |
303 | |
304 void SuspendObserverDelegate::OnDeviceChangedOnUIThread( | |
305 const std::vector<DeviceInfo>& snapshot_devices) { | |
306 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
307 // |avfoundation_monitor_impl_| might have been NULLed asynchronously before | 359 // |avfoundation_monitor_impl_| might have been NULLed asynchronously before |
308 // arriving at this line. | 360 // arriving at this line. |
309 if (avfoundation_monitor_impl_) { | 361 if (avfoundation_monitor_impl_) { |
310 avfoundation_monitor_impl_->ConsolidateDevicesListAndNotify( | 362 avfoundation_monitor_impl_->ConsolidateDevicesListAndNotify( |
311 snapshot_devices); | 363 snapshot_devices); |
312 } | 364 } |
313 } | 365 } |
314 | 366 |
315 // AVFoundation implementation of the Mac Device Monitor, registers as a global | 367 // AVFoundation implementation of the Mac Device Monitor, registers as a global |
316 // device connect/disconnect observer and plugs suspend/wake up device observers | 368 // device connect/disconnect observer and plugs suspend/wake up device observers |
317 // per device. Owns a SuspendObserverDelegate living in |device_task_runner_| | 369 // per device. This class is created and lives in UI thread. Owns a |
318 // and gets notified when a device is suspended/resumed. This class is created | 370 // SuspendObserverDelegate that notifies when a device is suspended/resumed. |
319 // and lives in UI thread; | |
320 class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { | 371 class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { |
321 public: | 372 public: |
322 AVFoundationMonitorImpl( | 373 AVFoundationMonitorImpl( |
323 content::DeviceMonitorMac* monitor, | 374 content::DeviceMonitorMac* monitor, |
324 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); | 375 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); |
325 virtual ~AVFoundationMonitorImpl(); | 376 virtual ~AVFoundationMonitorImpl(); |
326 | 377 |
327 virtual void OnDeviceChanged() OVERRIDE; | 378 virtual void OnDeviceChanged() OVERRIDE; |
328 | 379 |
329 private: | 380 private: |
330 base::ThreadChecker thread_checker_; | |
331 | |
332 // {Video,AudioInput}DeviceManager's "Device" thread task runner used for | 381 // {Video,AudioInput}DeviceManager's "Device" thread task runner used for |
333 // posting tasks to |suspend_observer_delegate_|; valid after | 382 // posting tasks to |suspend_observer_delegate_|; valid after |
334 // MediaStreamManager calls StartMonitoring(). | 383 // MediaStreamManager calls StartMonitoring(). |
335 const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_; | 384 const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_; |
336 | 385 |
337 scoped_refptr<SuspendObserverDelegate> suspend_observer_delegate_; | 386 scoped_refptr<SuspendObserverDelegate> suspend_observer_delegate_; |
338 | 387 |
339 DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); | 388 DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); |
340 }; | 389 }; |
341 | 390 |
342 AVFoundationMonitorImpl::AVFoundationMonitorImpl( | 391 AVFoundationMonitorImpl::AVFoundationMonitorImpl( |
343 content::DeviceMonitorMac* monitor, | 392 content::DeviceMonitorMac* monitor, |
344 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) | 393 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) |
345 : DeviceMonitorMacImpl(monitor), | 394 : DeviceMonitorMacImpl(monitor), |
346 device_task_runner_(device_task_runner), | 395 device_task_runner_(device_task_runner), |
347 suspend_observer_delegate_(new SuspendObserverDelegate(this)) { | 396 suspend_observer_delegate_(new SuspendObserverDelegate(this)) { |
| 397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
348 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; | 398 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
349 device_arrival_ = | 399 device_arrival_ = |
350 [nc addObserverForName:AVFoundationGlue:: | 400 [nc addObserverForName:AVFoundationGlue:: |
351 AVCaptureDeviceWasConnectedNotification() | 401 AVCaptureDeviceWasConnectedNotification() |
352 object:nil | 402 object:nil |
353 queue:nil | 403 queue:nil |
354 usingBlock:^(NSNotification* notification) { | 404 usingBlock:^(NSNotification* notification) { |
355 OnDeviceChanged();}]; | 405 OnDeviceChanged();}]; |
356 device_removal_ = | 406 device_removal_ = |
357 [nc addObserverForName:AVFoundationGlue:: | 407 [nc addObserverForName:AVFoundationGlue:: |
358 AVCaptureDeviceWasDisconnectedNotification() | 408 AVCaptureDeviceWasDisconnectedNotification() |
359 object:nil | 409 object:nil |
360 queue:nil | 410 queue:nil |
361 usingBlock:^(NSNotification* notification) { | 411 usingBlock:^(NSNotification* notification) { |
362 OnDeviceChanged();}]; | 412 OnDeviceChanged();}]; |
363 device_task_runner_->PostTask(FROM_HERE, | 413 suspend_observer_delegate_->StartObserver(device_task_runner_); |
364 base::Bind(&SuspendObserverDelegate::StartObserver, | |
365 suspend_observer_delegate_)); | |
366 } | 414 } |
367 | 415 |
368 AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { | 416 AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { |
369 DCHECK(thread_checker_.CalledOnValidThread()); | 417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
370 suspend_observer_delegate_->ResetDeviceMonitorOnUIThread(); | 418 suspend_observer_delegate_->ResetDeviceMonitor(); |
371 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; | 419 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
372 [nc removeObserver:device_arrival_]; | 420 [nc removeObserver:device_arrival_]; |
373 [nc removeObserver:device_removal_]; | 421 [nc removeObserver:device_removal_]; |
374 } | 422 } |
375 | 423 |
376 void AVFoundationMonitorImpl::OnDeviceChanged() { | 424 void AVFoundationMonitorImpl::OnDeviceChanged() { |
377 DCHECK(thread_checker_.CalledOnValidThread()); | 425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
378 device_task_runner_->PostTask(FROM_HERE, | 426 suspend_observer_delegate_->OnDeviceChanged(device_task_runner_); |
379 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, | |
380 suspend_observer_delegate_)); | |
381 } | 427 } |
382 | 428 |
383 } // namespace | 429 } // namespace |
384 | 430 |
385 @implementation CrAVFoundationDeviceObserver | 431 @implementation CrAVFoundationDeviceObserver |
386 | 432 |
387 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver { | 433 - (id)initWithOnChangedCallback:(const base::Closure&)callback { |
| 434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
388 if ((self = [super init])) { | 435 if ((self = [super init])) { |
389 DCHECK(receiver != NULL); | 436 DCHECK(!callback.is_null()); |
390 receiver_ = receiver; | 437 onDeviceChangedCallback_ = callback; |
391 } | 438 } |
392 return self; | 439 return self; |
393 } | 440 } |
394 | 441 |
395 - (void)dealloc { | 442 - (void)dealloc { |
396 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); | 443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 444 std::set<base::scoped_nsobject<CrAVCaptureDevice> >::iterator it = |
| 445 monitoredDevices_.begin(); |
397 while (it != monitoredDevices_.end()) | 446 while (it != monitoredDevices_.end()) |
398 [self stopObserving:*it++]; | 447 [self removeObservers:*(it++)]; |
399 [super dealloc]; | 448 [super dealloc]; |
400 } | 449 } |
401 | 450 |
402 - (void)startObserving:(CrAVCaptureDevice*)device { | 451 - (void)startObserving:(base::scoped_nsobject<CrAVCaptureDevice>)device { |
| 452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
403 DCHECK(device != nil); | 453 DCHECK(device != nil); |
404 // Skip this device if there are already observers connected to it. | 454 // Skip this device if there are already observers connected to it. |
405 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != | 455 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != |
406 monitoredDevices_.end()) { | 456 monitoredDevices_.end()) { |
407 return; | 457 return; |
408 } | 458 } |
409 [device addObserver:self | 459 [device addObserver:self |
410 forKeyPath:@"suspended" | 460 forKeyPath:@"suspended" |
411 options:0 | 461 options:0 |
412 context:device]; | 462 context:device.get()]; |
413 [device addObserver:self | 463 [device addObserver:self |
414 forKeyPath:@"connected" | 464 forKeyPath:@"connected" |
415 options:0 | 465 options:0 |
416 context:device]; | 466 context:device.get()]; |
417 monitoredDevices_.insert(device); | 467 monitoredDevices_.insert(device); |
418 } | 468 } |
419 | 469 |
420 - (void)stopObserving:(CrAVCaptureDevice*)device { | 470 - (void)stopObserving:(CrAVCaptureDevice*)device { |
| 471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
421 DCHECK(device != nil); | 472 DCHECK(device != nil); |
422 std::set<CrAVCaptureDevice*>::iterator found = | 473 |
| 474 std::set<base::scoped_nsobject<CrAVCaptureDevice> >::iterator found = |
423 std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); | 475 std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device); |
424 DCHECK(found != monitoredDevices_.end()); | 476 DCHECK(found != monitoredDevices_.end()); |
425 // Every so seldom, |device| might be gone when getting here, in that case | 477 [self removeObservers:*found]; |
426 // removing the observer causes a crash. Try to avoid it by checking sanity of | 478 monitoredDevices_.erase(found); |
427 // the |device| via its -observationInfo. http://crbug.com/371271. | 479 } |
| 480 |
| 481 - (void)clearOnDeviceChangedCallback { |
| 482 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 483 onDeviceChangedCallback_.Reset(); |
| 484 } |
| 485 |
| 486 - (void)removeObservers:(CrAVCaptureDevice*)device { |
| 487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 488 // Check sanity of |device| via its -observationInfo. http://crbug.com/371271. |
428 if ([device observationInfo]) { | 489 if ([device observationInfo]) { |
429 [device removeObserver:self | 490 [device removeObserver:self |
430 forKeyPath:@"suspended"]; | 491 forKeyPath:@"suspended"]; |
431 [device removeObserver:self | 492 [device removeObserver:self |
432 forKeyPath:@"connected"]; | 493 forKeyPath:@"connected"]; |
433 } | 494 } |
434 monitoredDevices_.erase(found); | |
435 } | 495 } |
436 | 496 |
437 - (void)observeValueForKeyPath:(NSString*)keyPath | 497 - (void)observeValueForKeyPath:(NSString*)keyPath |
438 ofObject:(id)object | 498 ofObject:(id)object |
439 change:(NSDictionary*)change | 499 change:(NSDictionary*)change |
440 context:(void*)context { | 500 context:(void*)context { |
| 501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
441 if ([keyPath isEqual:@"suspended"]) | 502 if ([keyPath isEqual:@"suspended"]) |
442 receiver_->OnDeviceChanged(); | 503 onDeviceChangedCallback_.Run(); |
443 if ([keyPath isEqual:@"connected"]) | 504 if ([keyPath isEqual:@"connected"]) |
444 [self stopObserving:static_cast<CrAVCaptureDevice*>(context)]; | 505 [self stopObserving:static_cast<CrAVCaptureDevice*>(context)]; |
445 } | 506 } |
446 | 507 |
447 @end // @implementation CrAVFoundationDeviceObserver | 508 @end // @implementation CrAVFoundationDeviceObserver |
448 | 509 |
449 namespace content { | 510 namespace content { |
450 | 511 |
451 DeviceMonitorMac::DeviceMonitorMac() { | 512 DeviceMonitorMac::DeviceMonitorMac() { |
452 // Both QTKit and AVFoundation do not need to be fired up until the user | 513 // Both QTKit and AVFoundation do not need to be fired up until the user |
(...skipping 18 matching lines...) Expand all Loading... |
471 } | 532 } |
472 | 533 |
473 void DeviceMonitorMac::NotifyDeviceChanged( | 534 void DeviceMonitorMac::NotifyDeviceChanged( |
474 base::SystemMonitor::DeviceType type) { | 535 base::SystemMonitor::DeviceType type) { |
475 DCHECK(thread_checker_.CalledOnValidThread()); | 536 DCHECK(thread_checker_.CalledOnValidThread()); |
476 // TODO(xians): Remove the global variable for SystemMonitor. | 537 // TODO(xians): Remove the global variable for SystemMonitor. |
477 base::SystemMonitor::Get()->ProcessDevicesChanged(type); | 538 base::SystemMonitor::Get()->ProcessDevicesChanged(type); |
478 } | 539 } |
479 | 540 |
480 } // namespace content | 541 } // namespace content |
OLD | NEW |