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/scoped_nsobject.h" | 13 #include "base/mac/scoped_nsobject.h" |
| 14 #include "base/threading/thread_checker.h" |
| 15 #include "content/public/browser/browser_thread.h" |
14 #import "media/video/capture/mac/avfoundation_glue.h" | 16 #import "media/video/capture/mac/avfoundation_glue.h" |
15 | 17 |
16 namespace { | 18 namespace { |
17 | 19 |
18 // This class is used to keep track of system devices names and their types. | 20 // This class is used to keep track of system devices names and their types. |
19 class DeviceInfo { | 21 class DeviceInfo { |
20 public: | 22 public: |
21 enum DeviceType { | 23 enum DeviceType { |
22 kAudio, | 24 kAudio, |
23 kVideo, | 25 kVideo, |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 boolValue]) { | 203 boolValue]) { |
202 device_type = DeviceInfo::kAudio; | 204 device_type = DeviceInfo::kAudio; |
203 } | 205 } |
204 snapshot_devices.push_back( | 206 snapshot_devices.push_back( |
205 DeviceInfo([[device uniqueID] UTF8String], device_type)); | 207 DeviceInfo([[device uniqueID] UTF8String], device_type)); |
206 } | 208 } |
207 ConsolidateDevicesListAndNotify(snapshot_devices); | 209 ConsolidateDevicesListAndNotify(snapshot_devices); |
208 } | 210 } |
209 | 211 |
210 // Forward declaration for use by CrAVFoundationDeviceObserver. | 212 // Forward declaration for use by CrAVFoundationDeviceObserver. |
211 class AVFoundationMonitorImpl; | 213 class SuspendObserverDelegate; |
212 | 214 |
213 } // namespace | 215 } // namespace |
214 | 216 |
215 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ | 217 // This class is a Key-Value Observer (KVO) shim. It is needed because C++ |
216 // classes cannot observe Key-Values directly. This class is used by | 218 // classes cannot observe Key-Values directly. Created, manipulated, and |
217 // AVfoundationMonitorImpl and executed in its |device_task_runner_|, a.k.a. | 219 // destroyed on the Device Thread by SuspendedObserverDelegate. |
218 // "Device Thread". -stopObserving is called dutifully on -dealloc on UI thread. | |
219 @interface CrAVFoundationDeviceObserver : NSObject { | 220 @interface CrAVFoundationDeviceObserver : NSObject { |
220 @private | 221 @private |
221 AVFoundationMonitorImpl* receiver_; | 222 SuspendObserverDelegate* receiver_; // weak |
222 // Member to keep track of the devices we are already monitoring. | 223 // Member to keep track of the devices we are already monitoring. |
223 std::set<CrAVCaptureDevice*> monitoredDevices_; | 224 std::set<CrAVCaptureDevice*> monitoredDevices_; |
224 } | 225 } |
225 | 226 |
226 - (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver; | 227 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver; |
227 - (void)startObserving:(CrAVCaptureDevice*)device; | 228 - (void)startObserving:(CrAVCaptureDevice*)device; |
228 - (void)stopObserving:(CrAVCaptureDevice*)device; | 229 - (void)stopObserving:(CrAVCaptureDevice*)device; |
229 | 230 |
230 @end | 231 @end |
231 | 232 |
232 namespace { | 233 namespace { |
233 | 234 |
| 235 // This class owns and manages the lifetime of a CrAVFoundationDeviceObserver. |
| 236 // Provides a callback for this device observer to indicate that there has been |
| 237 // a device change of some kind. Created by AVFoundationMonitorImpl in UI thread |
| 238 // but living in Device Thread. |
| 239 class SuspendObserverDelegate : |
| 240 public base::RefCountedThreadSafe<SuspendObserverDelegate> { |
| 241 public: |
| 242 explicit SuspendObserverDelegate(DeviceMonitorMacImpl* monitor) |
| 243 : avfoundation_monitor_impl_(monitor) { |
| 244 device_thread_checker_.DetachFromThread(); |
| 245 } |
| 246 |
| 247 void OnDeviceChanged(); |
| 248 void StartObserver(); |
| 249 void ResetDeviceMonitorOnUIThread(); |
| 250 |
| 251 private: |
| 252 friend class base::RefCountedThreadSafe<SuspendObserverDelegate>; |
| 253 |
| 254 virtual ~SuspendObserverDelegate() {} |
| 255 |
| 256 void OnDeviceChangedOnUIThread( |
| 257 const std::vector<DeviceInfo>& snapshot_devices); |
| 258 |
| 259 base::ThreadChecker device_thread_checker_; |
| 260 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; |
| 261 DeviceMonitorMacImpl* avfoundation_monitor_impl_; |
| 262 }; |
| 263 |
| 264 void SuspendObserverDelegate::OnDeviceChanged() { |
| 265 DCHECK(device_thread_checker_.CalledOnValidThread()); |
| 266 NSArray* devices = [AVCaptureDeviceGlue devices]; |
| 267 std::vector<DeviceInfo> snapshot_devices; |
| 268 for (CrAVCaptureDevice* device in devices) { |
| 269 [suspend_observer_ startObserving:device]; |
| 270 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && |
| 271 [device isSuspended]; |
| 272 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; |
| 273 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { |
| 274 if (suspended) |
| 275 continue; |
| 276 device_type = DeviceInfo::kVideo; |
| 277 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { |
| 278 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; |
| 279 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { |
| 280 device_type = DeviceInfo::kAudio; |
| 281 } |
| 282 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], |
| 283 device_type)); |
| 284 } |
| 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 |
| 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 |
| 308 // arriving at this line. |
| 309 if (avfoundation_monitor_impl_) { |
| 310 avfoundation_monitor_impl_->ConsolidateDevicesListAndNotify( |
| 311 snapshot_devices); |
| 312 } |
| 313 } |
| 314 |
234 // AVFoundation implementation of the Mac Device Monitor, registers as a global | 315 // AVFoundation implementation of the Mac Device Monitor, registers as a global |
235 // device connect/disconnect observer and plugs suspend/wake up device observers | 316 // 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 | 317 // per device. Owns a SuspendObserverDelegate living in |device_task_runner_| |
237 // and operations involving |suspend_observer_| happen on |device_task_runner_|. | 318 // and gets notified when a device is suspended/resumed. This class is created |
| 319 // and lives in UI thread; |
238 class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { | 320 class AVFoundationMonitorImpl : public DeviceMonitorMacImpl { |
239 public: | 321 public: |
240 AVFoundationMonitorImpl( | 322 AVFoundationMonitorImpl( |
241 content::DeviceMonitorMac* monitor, | 323 content::DeviceMonitorMac* monitor, |
242 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); | 324 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner); |
243 virtual ~AVFoundationMonitorImpl(); | 325 virtual ~AVFoundationMonitorImpl(); |
244 | 326 |
245 virtual void OnDeviceChanged() OVERRIDE; | 327 virtual void OnDeviceChanged() OVERRIDE; |
246 | 328 |
247 private: | 329 private: |
248 void OnDeviceChangedOnDeviceThread( | |
249 const scoped_refptr<base::MessageLoopProxy>& ui_thread); | |
250 void StartObserverOnDeviceThread(); | |
251 | |
252 base::ThreadChecker thread_checker_; | 330 base::ThreadChecker thread_checker_; |
253 | 331 |
254 // {Video,AudioInput}DeviceManager's "Device" thread task runner used for | 332 // {Video,AudioInput}DeviceManager's "Device" thread task runner used for |
255 // device enumeration, valid after MediaStreamManager calls StartMonitoring(). | 333 // posting tasks to |suspend_observer_delegate_|; valid after |
| 334 // MediaStreamManager calls StartMonitoring(). |
256 const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_; | 335 const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_; |
257 | 336 |
258 // Created and executed in |device_task_runnner_|. | 337 scoped_refptr<SuspendObserverDelegate> suspend_observer_delegate_; |
259 base::scoped_nsobject<CrAVFoundationDeviceObserver> suspend_observer_; | |
260 | 338 |
261 DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); | 339 DISALLOW_COPY_AND_ASSIGN(AVFoundationMonitorImpl); |
262 }; | 340 }; |
263 | 341 |
264 AVFoundationMonitorImpl::AVFoundationMonitorImpl( | 342 AVFoundationMonitorImpl::AVFoundationMonitorImpl( |
265 content::DeviceMonitorMac* monitor, | 343 content::DeviceMonitorMac* monitor, |
266 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) | 344 const scoped_refptr<base::SingleThreadTaskRunner>& device_task_runner) |
267 : DeviceMonitorMacImpl(monitor), | 345 : DeviceMonitorMacImpl(monitor), |
268 device_task_runner_(device_task_runner) { | 346 device_task_runner_(device_task_runner), |
| 347 suspend_observer_delegate_(new SuspendObserverDelegate(this)) { |
269 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; | 348 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
270 device_arrival_ = | 349 device_arrival_ = |
271 [nc addObserverForName:AVFoundationGlue:: | 350 [nc addObserverForName:AVFoundationGlue:: |
272 AVCaptureDeviceWasConnectedNotification() | 351 AVCaptureDeviceWasConnectedNotification() |
273 object:nil | 352 object:nil |
274 queue:nil | 353 queue:nil |
275 usingBlock:^(NSNotification* notification) { | 354 usingBlock:^(NSNotification* notification) { |
276 OnDeviceChanged();}]; | 355 OnDeviceChanged();}]; |
277 device_removal_ = | 356 device_removal_ = |
278 [nc addObserverForName:AVFoundationGlue:: | 357 [nc addObserverForName:AVFoundationGlue:: |
279 AVCaptureDeviceWasDisconnectedNotification() | 358 AVCaptureDeviceWasDisconnectedNotification() |
280 object:nil | 359 object:nil |
281 queue:nil | 360 queue:nil |
282 usingBlock:^(NSNotification* notification) { | 361 usingBlock:^(NSNotification* notification) { |
283 OnDeviceChanged();}]; | 362 OnDeviceChanged();}]; |
284 device_task_runner_->PostTask(FROM_HERE, | 363 device_task_runner_->PostTask(FROM_HERE, |
285 base::Bind(&AVFoundationMonitorImpl::StartObserverOnDeviceThread, | 364 base::Bind(&SuspendObserverDelegate::StartObserver, |
286 base::Unretained(this))); | 365 suspend_observer_delegate_)); |
287 } | 366 } |
288 | 367 |
289 AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { | 368 AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { |
| 369 DCHECK(thread_checker_.CalledOnValidThread()); |
| 370 suspend_observer_delegate_->ResetDeviceMonitorOnUIThread(); |
290 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; | 371 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
291 [nc removeObserver:device_arrival_]; | 372 [nc removeObserver:device_arrival_]; |
292 [nc removeObserver:device_removal_]; | 373 [nc removeObserver:device_removal_]; |
293 } | 374 } |
294 | 375 |
295 void AVFoundationMonitorImpl::OnDeviceChanged() { | 376 void AVFoundationMonitorImpl::OnDeviceChanged() { |
296 DCHECK(thread_checker_.CalledOnValidThread()); | 377 DCHECK(thread_checker_.CalledOnValidThread()); |
297 device_task_runner_->PostTask(FROM_HERE, | 378 device_task_runner_->PostTask(FROM_HERE, |
298 base::Bind(&AVFoundationMonitorImpl::OnDeviceChangedOnDeviceThread, | 379 base::Bind(&SuspendObserverDelegate::OnDeviceChanged, |
299 base::Unretained(this), | 380 suspend_observer_delegate_)); |
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]; | |
307 std::vector<DeviceInfo> snapshot_devices; | |
308 for (CrAVCaptureDevice* device in devices) { | |
309 [suspend_observer_ startObserving:device]; | |
310 BOOL suspended = [device respondsToSelector:@selector(isSuspended)] && | |
311 [device isSuspended]; | |
312 DeviceInfo::DeviceType device_type = DeviceInfo::kUnknown; | |
313 if ([device hasMediaType:AVFoundationGlue::AVMediaTypeVideo()]) { | |
314 if (suspended) | |
315 continue; | |
316 device_type = DeviceInfo::kVideo; | |
317 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeMuxed()]) { | |
318 device_type = suspended ? DeviceInfo::kAudio : DeviceInfo::kMuxed; | |
319 } else if ([device hasMediaType:AVFoundationGlue::AVMediaTypeAudio()]) { | |
320 device_type = DeviceInfo::kAudio; | |
321 } | |
322 snapshot_devices.push_back(DeviceInfo([[device uniqueID] UTF8String], | |
323 device_type)); | |
324 } | |
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]; | |
337 } | 381 } |
338 | 382 |
339 } // namespace | 383 } // namespace |
340 | 384 |
341 @implementation CrAVFoundationDeviceObserver | 385 @implementation CrAVFoundationDeviceObserver |
342 | 386 |
343 - (id)initWithChangeReceiver:(AVFoundationMonitorImpl*)receiver { | 387 - (id)initWithChangeReceiver:(SuspendObserverDelegate*)receiver { |
344 if ((self = [super init])) { | 388 if ((self = [super init])) { |
345 DCHECK(receiver != NULL); | 389 DCHECK(receiver != NULL); |
346 receiver_ = receiver; | 390 receiver_ = receiver; |
347 } | 391 } |
348 return self; | 392 return self; |
349 } | 393 } |
350 | 394 |
351 - (void)dealloc { | 395 - (void)dealloc { |
352 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); | 396 std::set<CrAVCaptureDevice*>::iterator it = monitoredDevices_.begin(); |
353 while (it != monitoredDevices_.end()) | 397 while (it != monitoredDevices_.end()) |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
427 } | 471 } |
428 | 472 |
429 void DeviceMonitorMac::NotifyDeviceChanged( | 473 void DeviceMonitorMac::NotifyDeviceChanged( |
430 base::SystemMonitor::DeviceType type) { | 474 base::SystemMonitor::DeviceType type) { |
431 DCHECK(thread_checker_.CalledOnValidThread()); | 475 DCHECK(thread_checker_.CalledOnValidThread()); |
432 // TODO(xians): Remove the global variable for SystemMonitor. | 476 // TODO(xians): Remove the global variable for SystemMonitor. |
433 base::SystemMonitor::Get()->ProcessDevicesChanged(type); | 477 base::SystemMonitor::Get()->ProcessDevicesChanged(type); |
434 } | 478 } |
435 | 479 |
436 } // namespace content | 480 } // namespace content |
OLD | NEW |