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