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/logging.h" | 12 #include "base/logging.h" |
12 #include "base/mac/scoped_nsobject.h" | 13 #include "base/mac/scoped_nsobject.h" |
13 #import "media/video/capture/mac/avfoundation_glue.h" | 14 #import "media/video/capture/mac/avfoundation_glue.h" |
14 | 15 |
15 namespace { | 16 namespace { |
16 | 17 |
17 // This class is used to keep track of system devices names and their types. | 18 // This class is used to keep track of system devices names and their types. |
18 class DeviceInfo { | 19 class DeviceInfo { |
19 public: | 20 public: |
20 enum DeviceType { | 21 enum DeviceType { |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 DeviceInfo([[device uniqueID] UTF8String], device_type)); | 205 DeviceInfo([[device uniqueID] UTF8String], device_type)); |
205 } | 206 } |
206 ConsolidateDevicesListAndNotify(snapshot_devices); | 207 ConsolidateDevicesListAndNotify(snapshot_devices); |
207 } | 208 } |
208 | 209 |
209 // Forward declaration for use by CrAVFoundationDeviceObserver. | 210 // Forward declaration for use by CrAVFoundationDeviceObserver. |
210 class AVFoundationMonitorImpl; | 211 class AVFoundationMonitorImpl; |
211 | 212 |
212 } // namespace | 213 } // namespace |
213 | 214 |
214 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ | 215 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ |
215 // classes cannot observe Key-Values directly. | 216 // classes cannot observe Key-Values directly. This class is used by |
| 217 // AVfoundationMonitorImpl and executed in its |device_task_runner_|, a.k.a. |
| 218 // "Device Thread". -stopObserving is called dutifully on -dealloc on UI thread. |
216 @interface CrAVFoundationDeviceObserver : NSObject { | 219 @interface CrAVFoundationDeviceObserver : NSObject { |
217 @private | 220 @private |
218 AVFoundationMonitorImpl* receiver_; | 221 AVFoundationMonitorImpl* receiver_; |
219 // Member to keep track of the devices we are already monitoring. | 222 // Member to keep track of the devices we are already monitoring. |
220 std::set<CrAVCaptureDevice*> monitoredDevices_; | 223 std::set<CrAVCaptureDevice*> monitoredDevices_; |
221 } | 224 } |
222 | 225 |
223 - (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver; | 226 - (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver; |
224 - (void)startObserving:(CrAVCaptureDevice*)device; | 227 - (void)startObserving:(CrAVCaptureDevice*)device; |
225 - (void)stopObserving:(CrAVCaptureDevice*)device; | 228 - (void)stopObserving:(CrAVCaptureDevice*)device; |
226 | 229 |
227 @end | 230 @end |
228 | 231 |
229 namespace { | 232 namespace { |
230 | 233 |
| 234 // AVFoundation implementation of the Mac Device Monitor, registers as a global |
| 235 // device connect/disconnect observer and plugs suspend/wake up device observers |
| 236 // per device. This class is created and lives in UI thread; device enumeration |
| 237 // and operations involving |suspend_observer_| happen on |device_task_runner_|. |
231 class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { | 238 class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { |
232 public: | 239 public: |
233 explicit AVFoundationMonitorImpl(content::DeviceMonitorMac* monitor); | 240 AVFoundationMonitorImpl( |
| 241 content::DeviceMonitorMac* monitor, |
| 242 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); |
234 virtual ~AVFoundationMonitorImpl(); | 243 virtual ~AVFoundationMonitorImpl(); |
235 | 244 |
236 virtual void OnDeviceChanged() OVERRIDE; | 245 virtual void OnDeviceChanged() OVERRIDE; |
237 | 246 |
238 private: | 247 private: |
| 248 void OnDeviceChangedOnDeviceThread( |
| 249 const scoped_refptr<base::MessageLoopProxy>& ui_thread); |
| 250 void StartObserverOnDeviceThread(); |
| 251 |
| 252 base::ThreadChecker thread_checker_; |
| 253 |
| 254 // {Video,AudioInput}DeviceManager's "Device" thread task runner used for |
| 255 // device enumeration, valid after MediaStreamManager calls StartMonitoring(). |
| 256 const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_; |
| 257 |
| 258 // Created and executed in |device_task_runnner_|. |
239 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; | 259 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; |
| 260 |
240 DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); | 261 DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); |
241 }; | 262 }; |
242 | 263 |
243 AVFoundationMonitorImpl::AVFoundationMonitorImpl( | 264 AVFoundationMonitorImpl::AVFoundationMonitorImpl( |
244 content::DeviceMonitorMac* monitor) | 265 content::DeviceMonitorMac* monitor, |
245 : DeviceMonitorMacImpl(monitor) { | 266 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) |
| 267 : DeviceMonitorMacImpl(monitor), |
| 268 device_task_runner_(device_task_runner) { |
246 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; | 269 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
247 device_arrival_ = | 270 device_arrival_ = |
248 [nc addObserverForName:AVFoundationGlue:: | 271 [nc addObserverForName:AVFoundationGlue:: |
249 AVCaptureDeviceWasConnectedNotification() | 272 AVCaptureDeviceWasConnectedNotification() |
250 object:nil | 273 object:nil |
251 queue:nil | 274 queue:nil |
252 usingBlock:^(NSNotification* notification) { | 275 usingBlock:^(NSNotification* notification) { |
253 OnDeviceChanged();}]; | 276 OnDeviceChanged();}]; |
254 device_removal_ = | 277 device_removal_ = |
255 [nc addObserverForName:AVFoundationGlue:: | 278 [nc addObserverForName:AVFoundationGlue:: |
256 AVCaptureDeviceWasDisconnectedNotification() | 279 AVCaptureDeviceWasDisconnectedNotification() |
257 object:nil | 280 object:nil |
258 queue:nil | 281 queue:nil |
259 usingBlock:^(NSNotification* notification) { | 282 usingBlock:^(NSNotification* notification) { |
260 OnDeviceChanged();}]; | 283 OnDeviceChanged();}]; |
261 suspend_observer_.reset( | 284 device_task_runner_->PostTask(FROM_HERE, |
262 [[CrAVFoundationDeviceObserver alloc] initWithChangeReceiver:this]); | 285 base::Bind(&AVFoundationMonitorImpl::StartObserverOnDeviceThread, |
263 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) | 286 base::Unretained(this))); |
264 [suspend_observer_ startObserving:device]; | |
265 } | 287 } |
266 | 288 |
267 AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { | 289 AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { |
268 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; | 290 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
269 [nc removeObserver:device_arrival_]; | 291 [nc removeObserver:device_arrival_]; |
270 [nc removeObserver:device_removal_]; | 292 [nc removeObserver:device_removal_]; |
271 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) | |
272 [suspend_observer_ stopObserving:device]; | |
273 } | 293 } |
274 | 294 |
275 void AVFoundationMonitorImpl::OnDeviceChanged() { | 295 void AVFoundationMonitorImpl::OnDeviceChanged() { |
| 296 DCHECK(thread_checker_.CalledOnValidThread()); |
| 297 device_task_runner_->PostTask(FROM_HERE, |
| 298 base::Bind(&AVFoundationMonitorImpl::OnDeviceChangedOnDeviceThread, |
| 299 base::Unretained(this), |
| 300 base::MessageLoop::current()->message_loop_proxy())); |
| 301 } |
| 302 |
| 303 void AVFoundationMonitorImpl::OnDeviceChangedOnDeviceThread( |
| 304 const scoped_refptr<base::MessageLoopProxy>& ui_thread) { |
| 305 DCHECK(device_task_runner_->BelongsToCurrentThread()); |
| 306 NSArray* devices = [AVCaptureDeviceGlue devices]; |
276 std::vector<DeviceInfo> snapshot_devices; | 307 std::vector<DeviceInfo> snapshot_devices; |
277 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) { | 308 for (CrAVCaptureDevice* device in devices) { |
278 [suspend_observer_ startObserving:device]; | 309 [suspend_observer_ startObserving:device]; |
279 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && | 310 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && |
280 [device isSuspended]; | 311 [device isSuspended]; |
281 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; | 312 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; |
282 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { | 313 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { |
283 if (suspended) | 314 if (suspended) |
284 continue; | 315 continue; |
285 device_type = DeviceInfo::kVideo; | 316 device_type = DeviceInfo::kVideo; |
286 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { | 317 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { |
287 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; | 318 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; |
288 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { | 319 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { |
289 device_type = DeviceInfo::kAudio; | 320 device_type = DeviceInfo::kAudio; |
290 } | 321 } |
291 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], | 322 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], |
292 device_type)); | 323 device_type)); |
293 } | 324 } |
294 ConsolidateDevicesListAndNotify(snapshot_devices); | 325 // Post the consolidation of enumerated devices to be done on UI thread. |
| 326 ui_thread->PostTask(FROM_HERE, |
| 327 base::Bind(&DeviceMonitorMacImpl::ConsolidateDevicesListAndNotify, |
| 328 base::Unretained(this), snapshot_devices)); |
| 329 } |
| 330 |
| 331 void AVFoundationMonitorImpl::StartObserverOnDeviceThread() { |
| 332 DCHECK(device_task_runner_->BelongsToCurrentThread()); |
| 333 suspend_observer_.reset([[CrAVFoundationDeviceObserver alloc] |
| 334 initWithChangeReceiver:this]); |
| 335 for (CrAVCaptureDevice* device in [AVCaptureDeviceGlue devices]) |
| 336 [suspend_observer_ startObserving:device]; |
295 } | 337 } |
296 | 338 |
297 } // namespace | 339 } // namespace |
298 | 340 |
299 @implementation CrAVFoundationDeviceObserver | 341 @implementation CrAVFoundationDeviceObserver |
300 | 342 |
301 - (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver { | 343 - (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver { |
302 if ((self = [super init])) { | 344 if ((self = [super init])) { |
303 DCHECK(receiver != NULL); | 345 DCHECK(receiver != NULL); |
304 receiver_ = receiver; | 346 receiver_ = receiver; |
305 } | 347 } |
306 return self; | 348 return self; |
307 } | 349 } |
308 | 350 |
| 351 - (void)dealloc { |
| 352 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); |
| 353 while (it != monitoredDevices_.end()) |
| 354 [self stopObserving:*it++]; |
| 355 [super dealloc]; |
| 356 } |
| 357 |
309 - (void)startObserving:(CrAVCaptureDevice*)device { | 358 - (void)startObserving:(CrAVCaptureDevice*)device { |
310 DCHECK(device != nil); | 359 DCHECK(device != nil); |
311 // Skip this device if there are already observers connected to it. | 360 // Skip this device if there are already observers connected to it. |
312 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != | 361 if (std::find(monitoredDevices_.begin(), monitoredDevices_.end(), device) != |
313 monitoredDevices_.end()) { | 362 monitoredDevices_.end()) { |
314 return; | 363 return; |
315 } | 364 } |
316 [device addObserver:self | 365 [device addObserver:self |
317 forKeyPath:@"suspended" | 366 forKeyPath:@"suspended" |
318 options:0 | 367 options:0 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
352 | 401 |
353 DeviceMonitorMac::DeviceMonitorMac() { | 402 DeviceMonitorMac::DeviceMonitorMac() { |
354 // Both QTKit and AVFoundation do not need to be fired up until the user | 403 // Both QTKit and AVFoundation do not need to be fired up until the user |
355 // exercises a GetUserMedia. Bringing up either library and enumerating the | 404 // exercises a GetUserMedia. Bringing up either library and enumerating the |
356 // devices in the system is an operation taking in the range of hundred of ms, | 405 // devices in the system is an operation taking in the range of hundred of ms, |
357 // so it is triggered explicitly from MediaStreamManager::StartMonitoring(). | 406 // so it is triggered explicitly from MediaStreamManager::StartMonitoring(). |
358 } | 407 } |
359 | 408 |
360 DeviceMonitorMac::~DeviceMonitorMac() {} | 409 DeviceMonitorMac::~DeviceMonitorMac() {} |
361 | 410 |
362 void DeviceMonitorMac::StartMonitoring() { | 411 void DeviceMonitorMac::StartMonitoring( |
| 412 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) { |
363 DCHECK(thread_checker_.CalledOnValidThread()); | 413 DCHECK(thread_checker_.CalledOnValidThread()); |
364 if (AVFoundationGlue::IsAVFoundationSupported()) { | 414 if (AVFoundationGlue::IsAVFoundationSupported()) { |
365 DVLOG(1) << "Monitoring via AVFoundation"; | 415 DVLOG(1) << "Monitoring via AVFoundation"; |
366 device_monitor_impl_.reset(new AVFoundationMonitorImpl(this)); | 416 device_monitor_impl_.reset(new AVFoundationMonitorImpl(this, |
| 417 device_task_runner)); |
367 } else { | 418 } else { |
368 DVLOG(1) << "Monitoring via QTKit"; | 419 DVLOG(1) << "Monitoring via QTKit"; |
369 device_monitor_impl_.reset(new QTKitMonitorImpl(this)); | 420 device_monitor_impl_.reset(new QTKitMonitorImpl(this)); |
370 } | 421 } |
371 } | 422 } |
372 | 423 |
373 void DeviceMonitorMac::NotifyDeviceChanged( | 424 void DeviceMonitorMac::NotifyDeviceChanged( |
374 base::SystemMonitor::DeviceType type) { | 425 base::SystemMonitor::DeviceType type) { |
| 426 DCHECK(thread_checker_.CalledOnValidThread()); |
375 // TODO(xians): Remove the global variable for SystemMonitor. | 427 // TODO(xians): Remove the global variable for SystemMonitor. |
376 base::SystemMonitor::Get()->ProcessDevicesChanged(type); | 428 base::SystemMonitor::Get()->ProcessDevicesChanged(type); |
377 } | 429 } |
378 | 430 |
379 } // namespace content | 431 } // namespace content |
OLD | NEW |