Chromium Code Reviews| Index: content/browser/device_monitor_mac.mm |
| diff --git a/content/browser/device_monitor_mac.mm b/content/browser/device_monitor_mac.mm |
| index 7b12e91cbdf4ed8b57fc020d3b75c0579240d0ef..ba24e5b49d1e585495644f6ae44aeee136869aa8 100644 |
| --- a/content/browser/device_monitor_mac.mm |
| +++ b/content/browser/device_monitor_mac.mm |
| @@ -7,39 +7,61 @@ |
| #import <QTKit/QTKit.h> |
| #include "base/logging.h" |
| +#include "base/synchronization/lock.h" |
| +#include "media/video/capture/mac/avfoundation_glue.h" |
| -namespace content { |
| +namespace { |
| -class DeviceMonitorMac::QTMonitorImpl { |
| +// Interface used by DeviceMonitorMac to interact with either a QTKit or an |
| +// AVFoundation implementation of events and notifications. |
| +class MacMonitorInterface { |
| public: |
| - explicit QTMonitorImpl(DeviceMonitorMac* monitor); |
| - virtual ~QTMonitorImpl() {} |
| + virtual ~MacMonitorInterface() {}; |
| - void Start(); |
| - void Stop(); |
| + virtual void OnDeviceChanged() = 0; |
| +}; |
| - private: |
| - void OnDeviceChanged(); |
| +// This class is used to keep track of system devices names and their types. |
| +class DeviceInfo{ |
|
Mark Mentovai
2013/10/09 16:05:12
Fix the formatting of this class to match the styl
mcasas
2013/10/12 09:59:27
Done.
|
| +public: |
| + enum DeviceType{ |
| + audio, |
| + video |
| + }; |
| - DeviceMonitorMac* monitor_; |
| - int number_audio_devices_; |
| - int number_video_devices_; |
| + DeviceInfo(std::string the_uniqueID, DeviceType the_type): |
| + uniqueID(the_uniqueID), type(the_type) {}; |
| + |
| + bool operator==(const DeviceInfo& device) const { |
| + return uniqueID == device.uniqueID; |
| + } |
| + |
| + std::string uniqueID; |
| + DeviceType type; |
|
Mark Mentovai
2013/10/09 16:05:12
Maybe this can be a bit mask, so that you can stuf
mcasas
2013/10/12 09:59:27
Instead of that I added a new category "kMuxed" an
|
| + // Allow generated copy constructor and assignment. |
| +}; |
| + |
| +class QTKitMonitorImpl : public MacMonitorInterface { |
| + public: |
| + QTKitMonitorImpl() {}; |
| + explicit QTKitMonitorImpl(content::DeviceMonitorMac* monitor); |
| + virtual ~QTKitMonitorImpl(); |
| + |
| + virtual void OnDeviceChanged() OVERRIDE; |
| + private: |
| + content::DeviceMonitorMac* monitor_; |
| + std::vector<DeviceInfo> cached_devices; |
|
Mark Mentovai
2013/10/09 16:05:12
Naming: members have trailing underscores.
mcasas
2013/10/12 09:59:27
Done.
|
| id device_arrival_; |
| id device_removal_; |
| - |
| - DISALLOW_COPY_AND_ASSIGN(QTMonitorImpl); |
| }; |
| -DeviceMonitorMac::QTMonitorImpl::QTMonitorImpl(DeviceMonitorMac* monitor) |
| +QTKitMonitorImpl::QTKitMonitorImpl(content::DeviceMonitorMac* monitor) |
| : monitor_(monitor), |
| - number_audio_devices_(0), |
| - number_video_devices_(0), |
| + cached_devices(), |
| device_arrival_(nil), |
| device_removal_(nil) { |
| DCHECK(monitor); |
| -} |
| -void DeviceMonitorMac::QTMonitorImpl::Start() { |
| NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
| device_arrival_ = |
| [nc addObserverForName:QTCaptureDeviceWasConnectedNotification |
| @@ -56,7 +78,7 @@ void DeviceMonitorMac::QTMonitorImpl::Start() { |
| OnDeviceChanged();}]; |
| } |
| -void DeviceMonitorMac::QTMonitorImpl::Stop() { |
| +QTKitMonitorImpl::~QTKitMonitorImpl() { |
| if (!monitor_) |
| return; |
| @@ -65,39 +87,184 @@ void DeviceMonitorMac::QTMonitorImpl::Stop() { |
| [nc removeObserver:device_removal_]; |
| } |
| -void DeviceMonitorMac::QTMonitorImpl::OnDeviceChanged() { |
| +void QTKitMonitorImpl::OnDeviceChanged() { |
| + base::AutoLock lock(monitor_->lock_); |
| + int video_device_added = 0; |
| + int audio_device_added = 0; |
| + int video_device_removed = 0; |
| + int audio_device_removed = 0; |
| + |
| + std::vector<DeviceInfo> snapshot_devices; |
|
Mark Mentovai
2013/10/09 16:05:12
Maybe snapshot_devices and cached_devices would be
mcasas
2013/10/12 09:59:27
I think that would make sense if we would like to
Robert Sesek
2013/10/14 17:26:44
Side node: unless the order is explicitly guarante
|
| + |
| NSArray* devices = [QTCaptureDevice inputDevices]; |
| - int number_video_devices = 0; |
| - int number_audio_devices = 0; |
| for (QTCaptureDevice* device in devices) { |
| if ([device hasMediaType:QTMediaTypeVideo] || |
| - [device hasMediaType:QTMediaTypeMuxed]) |
| - ++number_video_devices; |
| - |
| + [device hasMediaType:QTMediaTypeMuxed]) { |
| + snapshot_devices.push_back(DeviceInfo( |
| + [[device uniqueID] UTF8String], DeviceInfo::video)); |
| + } |
| if ([device hasMediaType:QTMediaTypeSound] || |
| - [device hasMediaType:QTMediaTypeMuxed]) |
| - ++number_audio_devices; |
| + [device hasMediaType:QTMediaTypeMuxed]) { |
| + snapshot_devices.push_back(DeviceInfo( |
| + [[device uniqueID] UTF8String], DeviceInfo::audio)); |
| + } |
| } |
| - if (number_video_devices_ != number_video_devices) { |
| - number_video_devices_ = number_video_devices; |
| - monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); |
| + std::vector<DeviceInfo>::iterator it; |
| + for (it = snapshot_devices.begin(); it != snapshot_devices.end(); ++it) { |
| + if (std::find(cached_devices.begin(), cached_devices.end(), *it) |
| + == cached_devices.end()) { |
| + video_device_added += (it->type == DeviceInfo::video); |
| + audio_device_added += (it->type == DeviceInfo::audio); |
| + DVLOG(1) << "Device has been added id: " << it->uniqueID; |
| + } |
|
Mark Mentovai
2013/10/09 16:05:12
If cached_devices isn’t going to be used on any ot
mcasas
2013/10/12 09:59:27
Done.
|
| } |
| - if (number_audio_devices_ != number_audio_devices) { |
| - number_audio_devices_ = number_audio_devices; |
| + for (it = cached_devices.begin(); it != cached_devices.end(); ++it) { |
| + if (std::find(snapshot_devices.begin(), snapshot_devices.end(), *it) |
| + == snapshot_devices.end()) { |
| + video_device_removed += (it->type == DeviceInfo::video); |
| + audio_device_removed += (it->type == DeviceInfo::audio); |
| + DVLOG(1) << "Device has been removed id: " << it->uniqueID; |
| + } |
| + } |
| + |
| + cached_devices.swap(snapshot_devices); |
| + |
| + if (video_device_added || video_device_removed) |
| + monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); |
| + if (audio_device_added || video_device_removed) |
| monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE); |
| +} |
| + |
| +class AVFoundationMonitorImpl : public MacMonitorInterface { |
| + public: |
| + AVFoundationMonitorImpl() {}; |
| + explicit AVFoundationMonitorImpl(content::DeviceMonitorMac* monitor); |
| + virtual ~AVFoundationMonitorImpl(); |
| + |
| + virtual void OnDeviceChanged() OVERRIDE; |
| + private: |
| + content::DeviceMonitorMac* monitor_; |
| + std::vector<DeviceInfo> cached_devices; |
|
Mark Mentovai
2013/10/09 16:05:12
Similar comments apply in here.
mcasas
2013/10/12 09:59:27
Done.
|
| + id device_arrival_; |
| + id device_removal_; |
| +}; |
| + |
| +AVFoundationMonitorImpl::AVFoundationMonitorImpl( |
| + content::DeviceMonitorMac* monitor) |
| + : monitor_(monitor), |
| + cached_devices(), |
| + device_arrival_(nil), |
| + device_removal_(nil) { |
| + DCHECK(monitor); |
| + |
| + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
| + NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; |
| + |
| + device_arrival_ = |
| + [nc addObserverForName:[AVFoundationGlue |
| + avCaptureDeviceWasConnectedNotification] |
| + object:nil |
| + queue:mainQueue |
| + usingBlock:^(NSNotification* notification) { |
| + OnDeviceChanged();}]; |
| + DCHECK(device_arrival_); |
| + device_removal_ = |
| + [nc addObserverForName:[AVFoundationGlue |
| + avCaptureDeviceWasDisconnectedNotification] |
| + object:nil |
| + queue:mainQueue |
| + usingBlock:^(NSNotification* notification) { |
| + OnDeviceChanged();}]; |
| + DCHECK(device_removal_); |
| +} |
| + |
| +AVFoundationMonitorImpl::~AVFoundationMonitorImpl() { |
| + if (!monitor_) |
| + return; |
| + |
| + NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
| + [nc removeObserver:device_arrival_]; |
| + [nc removeObserver:device_removal_]; |
| +} |
| + |
| +void AVFoundationMonitorImpl::OnDeviceChanged() { |
| + base::AutoLock lock(monitor_->lock_); |
| + int video_device_added = 0; |
| + int audio_device_added = 0; |
| + int video_device_removed = 0; |
| + int audio_device_removed = 0; |
| + |
| + std::vector<DeviceInfo> snapshot_devices; |
| + NSArray* devices = [AVCaptureDeviceGlue devices]; |
| + for (CrAVCaptureDevice* device in devices) { |
| + if ([AVCaptureDeviceGlue hasMediaType:[AVFoundationGlue avMediaTypeVideo] |
| + forCaptureDevice:device] || |
| + [AVCaptureDeviceGlue hasMediaType:[AVFoundationGlue avMediaTypeMuxed] |
| + forCaptureDevice:device]) { |
| + snapshot_devices.push_back(DeviceInfo([[AVCaptureDeviceGlue |
| + uniqueID:device] UTF8String], DeviceInfo::video)); |
| + } |
| + if ([AVCaptureDeviceGlue hasMediaType:[AVFoundationGlue avMediaTypeAudio] |
| + forCaptureDevice:device] || |
| + [AVCaptureDeviceGlue hasMediaType:[AVFoundationGlue avMediaTypeMuxed] |
| + forCaptureDevice:device]) { |
| + snapshot_devices.push_back(DeviceInfo([[AVCaptureDeviceGlue |
| + uniqueID:device] UTF8String], DeviceInfo::audio)); |
| + } |
| + } |
| + |
| + // Compare the current system devices snapshot with the ones cached to detect |
|
Mark Mentovai
2013/10/09 16:05:12
Looks like all, or almost all, of the rest of this
mcasas
2013/10/12 09:59:27
Done. Beefing the MacMonitorInterface actually mad
|
| + // additions, present in the former but not in the latter. |
| + std::vector<DeviceInfo>::iterator it; |
| + for (it = snapshot_devices.begin(); it != snapshot_devices.end(); ++it) { |
| + if( std::find(cached_devices.begin(), cached_devices.end(), *it) |
|
Mark Mentovai
2013/10/09 16:05:12
Spacing is interesting here and on line 233.
mcasas
2013/10/12 09:59:27
Done.
|
| + == cached_devices.end()) { |
| + video_device_added += (it->type == DeviceInfo::video); |
| + audio_device_added += (it->type == DeviceInfo::audio); |
| + DVLOG(1) << "Device has been added id: " << it->uniqueID; |
| + } |
| + } |
| + |
| + // Compare the cached devices snapshot with the current ones to detect missing |
| + // devices. These will be present in the former but not in the latter. |
| + for (it = cached_devices.begin(); it != cached_devices.end(); ++it) { |
| + if( std::find(snapshot_devices.begin(), snapshot_devices.end(), *it) |
| + == snapshot_devices.end()) { |
| + video_device_removed += (it->type == DeviceInfo::video); |
| + audio_device_removed += (it->type == DeviceInfo::audio); |
| + DVLOG(1) << "Device has been removed id: " << it->uniqueID; |
| + } |
| } |
| + // Update the cached devices with the current system snapshot. |
| + cached_devices.swap(snapshot_devices); |
| + |
| + if (video_device_added || video_device_removed) |
| + monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); |
| + if (audio_device_added || video_device_removed) |
| + monitor_->NotifyDeviceChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE); |
| } |
| +} // namespace |
| + |
| +namespace content { |
| + |
| DeviceMonitorMac::DeviceMonitorMac() { |
| - qt_monitor_.reset(new QTMonitorImpl(this)); |
| - qt_monitor_->Start(); |
| + if ([AVFoundationGlue isAVFoundationSupported]){ |
| + DVLOG(1) << "Monitoring via AVFoundation"; |
| + device_monitor_impl_.reset(new AVFoundationMonitorImpl(this)); |
| + // Force the device enumeration so we enumerate correctly devices already in |
| + // the system and at the same time use the AVCaptureDeviceGlue so it in |
| + // turn forces the AVCaptureDeviceGlue alloc-init. |
| + device_monitor_impl_->OnDeviceChanged(); |
| + } else { |
| + DVLOG(1) << "Monitoring via QTKit"; |
| + device_monitor_impl_.reset(new QTKitMonitorImpl(this)); |
| + } |
| } |
| -DeviceMonitorMac::~DeviceMonitorMac() { |
| - qt_monitor_->Stop(); |
| -} |
| +DeviceMonitorMac::~DeviceMonitorMac() {} |
| void DeviceMonitorMac::NotifyDeviceChanged( |
| base::SystemMonitor::DeviceType type) { |