Home | History | Annotate | Download | only in browser
      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
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/device_monitor_mac.h"
      6 
      7 #import <QTKit/QTKit.h>
      8 
      9 #include <set>
     10 
     11 #include "base/bind_helpers.h"
     12 #include "base/logging.h"
     13 #include "base/mac/bind_objc_block.h"
     14 #include "base/mac/scoped_nsobject.h"
     15 #include "base/threading/thread_checker.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #import "media/base/mac/avfoundation_glue.h"
     18 
     19 using content::BrowserThread;
     20 
     21 namespace {
     22 
     23 // This class is used to keep track of system devices names and their types.
     24 class DeviceInfo {
     25  public:
     26   enum DeviceType {
     27     kAudio,
     28     kVideo,
     29     kMuxed,
     30     kUnknown,
     31     kInvalid
     32   };
     33 
     34   DeviceInfo(std::string unique_id, DeviceType type)
     35       : unique_id_(unique_id), type_(type) {}
     36 
     37   // Operator== is needed here to use this class in a std::find. A given
     38   // |unique_id_| always has the same |type_| so for comparison purposes the
     39   // latter can be safely ignored.
     40   bool operator==(const DeviceInfo& device) const {
     41     return unique_id_ == device.unique_id_;
     42   }
     43 
     44   const std::string& unique_id() const { return unique_id_; }
     45   DeviceType type() const { return type_; }
     46 
     47  private:
     48   std::string unique_id_;
     49   DeviceType type_;
     50   // Allow generated copy constructor and assignment.
     51 };
     52 
     53 // Base abstract class used by DeviceMonitorMac to interact with either a QTKit
     54 // or an AVFoundation implementation of events and notifications.
     55 class DeviceMonitorMacImpl {
     56  public:
     57   explicit DeviceMonitorMacImpl(content::DeviceMonitorMac* monitor)
     58       : monitor_(monitor),
     59         cached_devices_(),
     60         device_arrival_(nil),
     61         device_removal_(nil) {
     62     DCHECK(monitor);
     63     // Initialise the devices_cache_ with a not-valid entry. For the case in
     64     // which there is one single device in the system and we get notified when
     65     // it gets removed, this will prevent the system from thinking that no
     66     // devices were added nor removed and not notifying the |monitor_|.
     67     cached_devices_.push_back(DeviceInfo("invalid", DeviceInfo::kInvalid));
     68   }
     69   virtual ~DeviceMonitorMacImpl() {}
     70 
     71   virtual void OnDeviceChanged() = 0;
     72 
     73   // Method called by the default notification center when a device is removed
     74   // or added to the system. It will compare the |cached_devices_| with the
     75   // current situation, update it, and, if there's an update, signal to
     76   // |monitor_| with the appropriate device type.
     77   void ConsolidateDevicesListAndNotify(
     78       const std::vector<DeviceInfo>& snapshot_devices);
     79 
     80  protected:
     81   content::DeviceMonitorMac* monitor_;
     82   std::vector<DeviceInfo> cached_devices_;
     83 
     84   // Handles to NSNotificationCenter block observers.
     85   id device_arrival_;
     86   id device_removal_;
     87 
     88  private:
     89   DISALLOW_COPY_AND_ASSIGN(DeviceMonitorMacImpl);
     90 };
     91 
     92 void DeviceMonitorMacImpl::ConsolidateDevicesListAndNotify(
     93     const std::vector<DeviceInfo>& snapshot_devices) {
     94   bool video_device_added = false;
     95   bool audio_device_added = false;
     96   bool video_device_removed = false;
     97   bool audio_device_removed = false;
     98 
     99   // Compare the current system devices snapshot with the ones cached to detect
    100   // additions, present in the former but not in the latter. If we find a device
    101   // in snapshot_devices entry also present in cached_devices, we remove it from
    102   // the latter vector.
    103   std::vector<DeviceInfo>::const_iterator it;
    104   for (it = snapshot_devices.begin(); it != snapshot_devices.end(); ++it) {
    105     std::vector<DeviceInfo>::iterator cached_devices_iterator =
    106         std::find(cached_devices_.begin(), cached_devices_.end(), *it);
    107     if (cached_devices_iterator == cached_devices_.end()) {
    108       video_device_added |= ((it->type() == DeviceInfo::kVideo) ||
    109                              (it->type() == DeviceInfo::kMuxed));
    110       audio_device_added |= ((it->type() == DeviceInfo::kAudio) ||
    111                              (it->type() == DeviceInfo::kMuxed));
    112       DVLOG(1) << "Device has been added, id: " << it->unique_id();
    113     } else {
    114       cached_devices_.erase(cached_devices_iterator);
    115     }
    116   }
    117   // All the remaining entries in cached_devices are removed devices.
    118   for (it = cached_devices_.begin(); it != cached_devices_.end(); ++it) {
    119     video_device_removed |= ((it->type() == DeviceInfo::kVideo) ||
    120                              (it->type() == DeviceInfo::kMuxed) ||
    121                              (it->type() == DeviceInfo::kInvalid));
    122     audio_device_removed |= ((it->type() == DeviceInfo::kAudio) ||
    123                              (it->type() == DeviceInfo::kMuxed) ||
    124                              (it->type() == DeviceInfo::kInvalid));
    125     DVLOG(1) << "Device has been removed, id: " << it->unique_id();
    126   }
    127   // Update the cached devices with the current system snapshot.
    128   cached_devices_ = snapshot_devices;
    129 
    130   if (video_device_added || video_device_removed)
    131     monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE);
    132   if (audio_device_added || audio_device_removed)
    133     monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE);
    134 }
    135 
    136 class QTKitMonitorImpl : public DeviceMonitorMacImpl {
    137  public:
    138   explicit QTKitMonitorImpl(content::DeviceMonitorMac* monitor);
    139   virtual ~QTKitMonitorImpl();
    140 
    141   virtual void OnDeviceChanged() OVERRIDE;
    142  private:
    143   void CountDevices();
    144   void OnAttributeChanged(NSNotification* notification);
    145 
    146   id device_change_;
    147 };
    148 
    149 QTKitMonitorImpl::QTKitMonitorImpl(content::DeviceMonitorMac* monitor)
    150     : DeviceMonitorMacImpl(monitor) {
    151   NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
    152   device_arrival_ =
    153       [nc addObserverForName:QTCaptureDeviceWasConnectedNotification
    154                       object:nil
    155                        queue:nil
    156                   usingBlock:^(NSNotification* notification) {
    157                       OnDeviceChanged();}];
    158   device_removal_ =
    159       [nc addObserverForName:QTCaptureDeviceWasDisconnectedNotification
    160                       object:nil
    161                        queue:nil
    162                   usingBlock:^(NSNotification* notification) {
    163                       OnDeviceChanged();}];
    164   device_change_ =
    165       [nc addObserverForName:QTCaptureDeviceAttributeDidChangeNotification
    166                       object:nil
    167                        queue:nil
    168                   usingBlock:^(NSNotification* notification) {
    169                       OnAttributeChanged(notification);}];
    170 }
    171 
    172 QTKitMonitorImpl::~QTKitMonitorImpl() {
    173   NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
    174   [nc removeObserver:device_arrival_];
    175   [nc removeObserver:device_removal_];
    176   [nc removeObserver:device_change_];
    177 }
    178 
    179 void QTKitMonitorImpl::OnAttributeChanged(
    180     NSNotification* notification) {
    181   if ([[[notification userInfo]
    182          objectForKey:QTCaptureDeviceChangedAttributeKey]
    183       isEqualToString:QTCaptureDeviceSuspendedAttribute]) {
    184     OnDeviceChanged();
    185   }
    186 }
    187 
    188 void QTKitMonitorImpl::OnDeviceChanged() {
    189   std::vector<DeviceInfo> snapshot_devices;
    190 
    191   NSArray* devices = [QTCaptureDevice inputDevices];
    192   for (QTCaptureDevice* device in devices) {
    193     DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown;
    194     // Act as if suspended video capture devices are not attached.  For
    195     // example, a laptop's internal webcam is suspended when the lid is closed.
    196     if ([device hasMediaType:QTMediaTypeVideo] &&
    197         ![[device attributeForKey:QTCaptureDeviceSuspendedAttribute]
    198         boolValue]) {
    199       device_type = DeviceInfo::kVideo;
    200     } else if ([device hasMediaType:QTMediaTypeMuxed] &&
    201         ![[device attributeForKey:QTCaptureDeviceSuspendedAttribute]
    202         boolValue]) {
    203       device_type = DeviceInfo::kMuxed;
    204     } else if ([device hasMediaType:QTMediaTypeSound] &&
    205         ![[device attributeForKey:QTCaptureDeviceSuspendedAttribute]
    206         boolValue]) {
    207       device_type = DeviceInfo::kAudio;
    208     }
    209     snapshot_devices.push_back(
    210         DeviceInfo([[device uniqueID] UTF8String], device_type));
    211   }
    212   ConsolidateDevicesListAndNotify(snapshot_devices);
    213 }
    214 
    215 // Forward declaration for use by CrAVFoundationDeviceObserver.
    216 class SuspendObserverDelegate;
    217 
    218 }  // namespace
    219 
    220 // This class is a Key-Value Observer (KVO) shim. It is needed because C++
    221 // classes cannot observe Key-Values directly. Created, manipulated, and
    222 // destroyed on the UI Thread by SuspendObserverDelegate.
    223 @interface CrAVFoundationDeviceObserver : NSObject {
    224  @private
    225   // Callback for device changed, has to run on Device Thread.
    226   base::Closure onDeviceChangedCallback_;
    227 
    228   // Member to keep track of the devices we are already monitoring.
    229   std::set<base::scoped_nsobject<CrAVCaptureDevice> > monitoredDevices_;
    230 }
    231 
    232 - (id)initWithOnChangedCallback:(const base::Closure&)callback;
    233 - (void)startObserving:(base::scoped_nsobject<CrAVCaptureDevice>)device;
    234 - (void)stopObserving:(CrAVCaptureDevice*)device;
    235 - (void)clearOnDeviceChangedCallback;
    236 
    237 @end
    238 
    239 namespace {
    240 
    241 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver.
    242 // It is created and destroyed in UI thread by AVFoundationMonitorImpl, and it
    243 // operates on this thread except for the expensive device enumerations which
    244 // are run on Device Thread.
    245 class SuspendObserverDelegate :
    246     public base::RefCountedThreadSafe<SuspendObserverDelegate> {
    247  public:
    248   explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor);
    249 
    250   // Create |suspend_observer_| for all devices and register OnDeviceChanged()
    251   // as its change callback. Schedule bottom half in DoStartObserver().
    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();
    262 
    263  private:
    264   friend class base::RefCountedThreadSafe<SuspendObserverDelegate>;
    265 
    266   virtual ~SuspendObserverDelegate();
    267 
    268   // Bottom half of StartObserver(), starts |suspend_observer_| for all 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);
    277 
    278   base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_;
    279   DeviceMonitorMacImpl* avfoundation_monitor_impl_;
    280 };
    281 
    282 SuspendObserverDelegate::SuspendObserverDelegate(DeviceMonitorMacImpl* monitor)
    283     : avfoundation_monitor_impl_(monitor) {
    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(
    301       device_thread.get(),
    302       FROM_HERE,
    303       base::BindBlock(^{ return [[AVCaptureDeviceGlue devices] retain]; }),
    304       base::Bind(&SuspendObserverDelegate::DoStartObserver, this));
    305 }
    306 
    307 void SuspendObserverDelegate::OnDeviceChanged(
    308       const scoped_refptr<base::SingleThreadTaskRunner>& device_thread) {
    309   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    310   // Enumerate the devices in Device thread and post the consolidation of the
    311   // new devices and the old ones to be done on UI thread. The devices array
    312   // is retained in |device_thread| and released in DoOnDeviceChanged().
    313   PostTaskAndReplyWithResult(
    314       device_thread.get(),
    315       FROM_HERE,
    316       base::BindBlock(^{ return [[AVCaptureDeviceGlue devices] retain]; }),
    317       base::Bind(&SuspendObserverDelegate::DoOnDeviceChanged, this));
    318 }
    319 
    320 void SuspendObserverDelegate::ResetDeviceMonitor() {
    321   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    322   avfoundation_monitor_impl_ = NULL;
    323   [suspend_observer_ clearOnDeviceChangedCallback];
    324 }
    325 
    326 SuspendObserverDelegate::~SuspendObserverDelegate() {
    327   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    328 }
    329 
    330 void SuspendObserverDelegate::DoStartObserver(NSArray* devices) {
    331   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    332   base::scoped_nsobject<NSArray> auto_release(devices);
    333   for (CrAVCaptureDevice* device in devices) {
    334     base::scoped_nsobject<CrAVCaptureDevice> device_ptr([device retain]);
    335     [suspend_observer_ startObserving:device_ptr];
    336   }
    337 }
    338 
    339 void SuspendObserverDelegate::DoOnDeviceChanged(NSArray* devices) {
    340   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    341   base::scoped_nsobject<NSArray> auto_release(devices);
    342   std::vector<DeviceInfo> snapshot_devices;
    343   for (CrAVCaptureDevice* device in devices) {
    344     base::scoped_nsobject<CrAVCaptureDevice> device_ptr([device retain]);
    345     [suspend_observer_ startObserving:device_ptr];
    346 
    347     BOOL suspended = [device respondsToSelector:@selector(isSuspended)] &&
    348         [device isSuspended];
    349     DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown;
    350     if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) {
    351       if (suspended)
    352         continue;
    353       device_type = DeviceInfo::kVideo;
    354     } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) {
    355       device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed;
    356     } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) {
    357       device_type = DeviceInfo::kAudio;
    358     }
    359     snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String],
    360                                           device_type));
    361   }
    362 
    363   // |avfoundation_monitor_impl_| might have been NULLed asynchronously before
    364   // arriving at this line.
    365   if (avfoundation_monitor_impl_) {
    366     avfoundation_monitor_impl_->ConsolidateDevicesListAndNotify(
    367         snapshot_devices);
    368   }
    369 }
    370 
    371 // AVFoundation implementation of the Mac Device Monitor, registers as a global
    372 // device connect/disconnect observer and plugs suspend/wake up device observers
    373 // per device. This class is created and lives in UI thread. Owns a
    374 // SuspendObserverDelegate that notifies when a device is suspended/resumed.
    375 class AVFoundationMonitorImpl : public DeviceMonitorMacImpl {
    376  public:
    377   AVFoundationMonitorImpl(
    378       content::DeviceMonitorMac* monitor,
    379       const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner);
    380   virtual ~AVFoundationMonitorImpl();
    381 
    382   virtual void OnDeviceChanged() OVERRIDE;
    383 
    384  private:
    385   // {Video,AudioInput}DeviceManager's "Device" thread task runner used for
    386   // posting tasks to |suspend_observer_delegate_|; valid after
    387   // MediaStreamManager calls StartMonitoring().
    388   const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
    389 
    390   scoped_refptr<SuspendObserverDelegate> suspend_observer_delegate_;
    391 
    392   DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl);
    393 };
    394 
    395 AVFoundationMonitorImpl::AVFoundationMonitorImpl(
    396     content::DeviceMonitorMac* monitor,
    397     const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner)
    398     : DeviceMonitorMacImpl(monitor),
    399       device_task_runner_(device_task_runner),
    400       suspend_observer_delegate_(new SuspendObserverDelegate(this)) {
    401   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    402   NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
    403   device_arrival_ =
    404       [nc addObserverForName:AVFoundationGlue::
    405           AVCaptureDeviceWasConnectedNotification()
    406                       object:nil
    407                        queue:nil
    408                   usingBlock:^(NSNotification* notification) {
    409                       OnDeviceChanged();}];
    410   device_removal_ =
    411       [nc addObserverForName:AVFoundationGlue::
    412           AVCaptureDeviceWasDisconnectedNotification()
    413                       object:nil
    414                        queue:nil
    415                   usingBlock:^(NSNotification* notification) {
    416                       OnDeviceChanged();}];
    417   suspend_observer_delegate_->StartObserver(device_task_runner_);
    418 }
    419 
    420 AVFoundationMonitorImpl::~AVFoundationMonitorImpl() {
    421   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    422   suspend_observer_delegate_->ResetDeviceMonitor();
    423   NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
    424   [nc removeObserver:device_arrival_];
    425   [nc removeObserver:device_removal_];
    426 }
    427 
    428 void AVFoundationMonitorImpl::OnDeviceChanged() {
    429   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    430   suspend_observer_delegate_->OnDeviceChanged(device_task_runner_);
    431 }
    432 
    433 }  // namespace
    434 
    435 @implementation CrAVFoundationDeviceObserver
    436 
    437 - (id)initWithOnChangedCallback:(const base::Closure&)callback {
    438   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    439   if ((self = [super init])) {
    440     DCHECK(!callback.is_null());
    441     onDeviceChangedCallback_ = callback;
    442   }
    443   return self;
    444 }
    445 
    446 - (void)dealloc {
    447   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    448   std::set<base::scoped_nsobject<CrAVCaptureDevice> >::iterator it =
    449       monitoredDevices_.begin();
    450   while (it != monitoredDevices_.end())
    451     [self removeObservers:*(it++)];
    452   [super dealloc];
    453 }
    454 
    455 - (void)startObserving:(base::scoped_nsobject<CrAVCaptureDevice>)device {
    456   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    457   DCHECK(device != nil);
    458   // Skip this device if there are already observers connected to it.
    459   if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) !=
    460       monitoredDevices_.end()) {
    461     return;
    462   }
    463   [device addObserver:self
    464            forKeyPath:@"suspended"
    465               options:0
    466               context:device.get()];
    467   [device addObserver:self
    468            forKeyPath:@"connected"
    469               options:0
    470               context:device.get()];
    471   monitoredDevices_.insert(device);
    472 }
    473 
    474 - (void)stopObserving:(CrAVCaptureDevice*)device {
    475   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    476   DCHECK(device != nil);
    477 
    478   std::set<base::scoped_nsobject<CrAVCaptureDevice> >::iterator found =
    479       std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device);
    480   DCHECK(found != monitoredDevices_.end());
    481   [self removeObservers:*found];
    482   monitoredDevices_.erase(found);
    483 }
    484 
    485 - (void)clearOnDeviceChangedCallback {
    486   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    487   onDeviceChangedCallback_.Reset();
    488 }
    489 
    490 - (void)removeObservers:(CrAVCaptureDevice*)device {
    491   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    492   // Check sanity of |device| via its -observationInfo. http://crbug.com/371271.
    493   if ([device observationInfo]) {
    494     [device removeObserver:self
    495                 forKeyPath:@"suspended"];
    496     [device removeObserver:self
    497                 forKeyPath:@"connected"];
    498   }
    499 }
    500 
    501 - (void)observeValueForKeyPath:(NSString*)keyPath
    502                       ofObject:(id)object
    503                         change:(NSDictionary*)change
    504                        context:(void*)context {
    505   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    506   if ([keyPath isEqual:@"suspended"])
    507     onDeviceChangedCallback_.Run();
    508   if ([keyPath isEqual:@"connected"])
    509     [self stopObserving:static_cast<CrAVCaptureDevice*>(context)];
    510 }
    511 
    512 @end  // @implementation CrAVFoundationDeviceObserver
    513 
    514 namespace content {
    515 
    516 DeviceMonitorMac::DeviceMonitorMac() {
    517   // Both QTKit and AVFoundation do not need to be fired up until the user
    518   // exercises a GetUserMedia. Bringing up either library and enumerating the
    519   // devices in the system is an operation taking in the range of hundred of ms,
    520   // so it is triggered explicitly from MediaStreamManager::StartMonitoring().
    521 }
    522 
    523 DeviceMonitorMac::~DeviceMonitorMac() {}
    524 
    525 void DeviceMonitorMac::StartMonitoring(
    526     const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) {
    527   DCHECK(thread_checker_.CalledOnValidThread());
    528   if (AVFoundationGlue::IsAVFoundationSupported()) {
    529     DVLOG(1) << "Monitoring via AVFoundation";
    530     device_monitor_impl_.reset(new AVFoundationMonitorImpl(this,
    531                                                            device_task_runner));
    532   } else {
    533     DVLOG(1) << "Monitoring via QTKit";
    534     device_monitor_impl_.reset(new QTKitMonitorImpl(this));
    535   }
    536 }
    537 
    538 void DeviceMonitorMac::NotifyDeviceChanged(
    539     base::SystemMonitor::DeviceType type) {
    540   DCHECK(thread_checker_.CalledOnValidThread());
    541   // TODO(xians): Remove the global variable for SystemMonitor.
    542   base::SystemMonitor::Get()->ProcessDevicesChanged(type);
    543 }
    544 
    545 }  // namespace content
    546