diff options
author | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-09 16:01:34 +0000 |
---|---|---|
committer | wjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-09 16:01:34 +0000 |
commit | 0874f3cb7daa931fcf72396f73879695622a4225 (patch) | |
tree | 8762b5639ca5ae11f969d2d2396f0e44c7144ee2 | |
parent | ce2b28efbc93b747f0ae37ac672ae2d831e68214 (diff) | |
download | chromium_src-0874f3cb7daa931fcf72396f73879695622a4225.zip chromium_src-0874f3cb7daa931fcf72396f73879695622a4225.tar.gz chromium_src-0874f3cb7daa931fcf72396f73879695622a4225.tar.bz2 |
reland http://codereview.chromium.org/10830063/
refactor EnumerateDevices to make it a persistent request.
After the requester calls EnumerateDevices, it should expect new device list as initial response and whenever devices are changed.
A new method StopEnumerateDevices is also added to allow the request to stop device enumeration.
It also includes corresponding change in Pepper since EnumerateDevices contract is changed (http://codereview.chromium.org/10837064/).
This patch keeps current Pepper API, while using the new API of EnumerateDevices from MediaStreamDispatcher.
Pepper calls StopEnumerateDevices when it receives the first enumerated result.
A new Pepper API will be created in a separate patch to make EnumerateDevices persistent.
BUG=137799
Review URL: https://chromiumcodereview.appspot.com/10828230
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150803 0039d316-1c4b-4281-b951-d872f2087c98
17 files changed, 510 insertions, 114 deletions
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc index fb817b9..1f6eb43 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc @@ -100,10 +100,9 @@ void MediaStreamDispatcherHost::DevicesEnumerated( StreamMap::iterator it = streams_.find(label); DCHECK(it != streams_.end()); StreamRequest request = it->second; - streams_.erase(it); Send(new MediaStreamMsg_DevicesEnumerated( - request.render_view_id, request.page_request_id, devices)); + request.render_view_id, request.page_request_id, label, devices)); } void MediaStreamDispatcherHost::DeviceOpened( @@ -196,7 +195,6 @@ void MediaStreamDispatcherHost::OnStopGeneratedStream( << ", {label = " << label << "})"; StreamMap::iterator it = streams_.find(label); - DCHECK(it != streams_.end()); GetManager()->StopGeneratedStream(label); streams_.erase(it); } diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc index 1329b97..b41b244 100644 --- a/content/browser/renderer_host/media/media_stream_manager.cc +++ b/content/browser/renderer_host/media/media_stream_manager.cc @@ -47,14 +47,10 @@ static std::string RandomLabel() { // Helper to verify if a media stream type is part of options or not. static bool Requested(const StreamOptions& options, MediaStreamType stream_type) { - if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE && - options.video) { - return true; - } else if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE && - options.audio) { - return true; - } - return false; + return (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE && + options.video) || + (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE && + options.audio); } DeviceThread::DeviceThread(const char* name) @@ -78,24 +74,24 @@ void DeviceThread::CleanUp() { // TODO(xians): Merge DeviceRequest with MediaStreamRequest. struct MediaStreamManager::DeviceRequest { enum RequestState { - kNotRequested = 0, - kRequested, - kPendingApproval, - kOpening, - kDone, - kError + STATE_NOT_REQUESTED = 0, + STATE_REQUESTED, + STATE_PENDING_APPROVAL, + STATE_OPENING, + STATE_DONE, + STATE_ERROR }; enum RequestType { - kGenerateStream = 0, - kEnumerateDevices, - kOpenDevice + GENERATE_STREAM = 0, + ENUMERATE_DEVICES, + OPEN_DEVICE }; DeviceRequest() : requester(NULL), - state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, kNotRequested), - type(kGenerateStream), + state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, STATE_NOT_REQUESTED), + type(GENERATE_STREAM), render_process_id(-1), render_view_id(-1) { options.audio = false; @@ -109,8 +105,8 @@ struct MediaStreamManager::DeviceRequest { const GURL& request_security_origin) : requester(requester), options(request_options), - state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, kNotRequested), - type(kGenerateStream), + state(content::NUM_MEDIA_STREAM_DEVICE_TYPES, STATE_NOT_REQUESTED), + type(GENERATE_STREAM), render_process_id(render_process_id), render_view_id(render_view_id), security_origin(request_security_origin) { @@ -131,6 +127,13 @@ struct MediaStreamManager::DeviceRequest { StreamDeviceInfoArray video_devices; }; +MediaStreamManager::EnumerationCache::EnumerationCache() + : valid(false) { +} + +MediaStreamManager::EnumerationCache::~EnumerationCache() { +} + MediaStreamManager::MediaStreamManager( AudioInputDeviceManager* audio_input_device_manager, VideoCaptureManager* video_capture_manager) @@ -138,8 +141,10 @@ MediaStreamManager::MediaStreamManager( device_settings_(new MediaStreamDeviceSettings(this))), audio_input_device_manager_(audio_input_device_manager), video_capture_manager_(video_capture_manager), - enumeration_in_progress_(content::NUM_MEDIA_STREAM_DEVICE_TYPES, false), + monitoring_started_(false), io_loop_(NULL) { + memset(active_enumeration_ref_count_, 0, + sizeof(active_enumeration_ref_count_)); } MediaStreamManager::~MediaStreamManager() { @@ -176,6 +181,17 @@ void MediaStreamManager::GenerateStream(MediaStreamRequester* requester, render_view_id, security_origin); StartEnumeration(&new_request, label); + + // Get user confirmation to use capture devices. + // Need to make an asynchronous call to make sure the |requester| gets the + // |label| before it would receive any event. + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&MediaStreamDeviceSettings::RequestCaptureDeviceUsage, + base::Unretained(device_settings_.get()), + *label, render_process_id, + render_view_id, options, + security_origin)); } void MediaStreamManager::CancelRequests(MediaStreamRequester* requester) { @@ -187,7 +203,7 @@ void MediaStreamManager::CancelRequests(MediaStreamRequester* requester) { // opened -> close them. DeviceRequest* request = &(it->second); if (request->state[content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE] == - DeviceRequest::kOpening) { + DeviceRequest::STATE_OPENING) { for (StreamDeviceInfoArray::iterator it = request->audio_devices.begin(); it != request->audio_devices.end(); ++it) { @@ -195,7 +211,7 @@ void MediaStreamManager::CancelRequests(MediaStreamRequester* requester) { } } if (request->state[content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE] == - DeviceRequest::kOpening) { + DeviceRequest::STATE_OPENING) { for (StreamDeviceInfoArray::iterator it = request->video_devices.begin(); it != request->video_devices.end(); ++it) { @@ -218,7 +234,7 @@ void MediaStreamManager::CancelGenerateStream(const std::string& label) { if (!RequestDone(it->second)) { DeviceRequest* request = &(it->second); if (request->state[content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE] == - DeviceRequest::kOpening) { + DeviceRequest::STATE_OPENING) { for (StreamDeviceInfoArray::iterator it = request->audio_devices.begin(); it != request->audio_devices.end(); ++it) { @@ -226,7 +242,7 @@ void MediaStreamManager::CancelGenerateStream(const std::string& label) { } } if (request->state[content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE] == - DeviceRequest::kOpening) { + DeviceRequest::STATE_OPENING) { for (StreamDeviceInfoArray::iterator it = request->video_devices.begin(); it != request->video_devices.end(); ++it) { @@ -246,6 +262,10 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) { // Find the request and close all open devices for the request. DeviceRequests::iterator it = requests_.find(label); if (it != requests_.end()) { + if (it->second.type == DeviceRequest::ENUMERATE_DEVICES) { + StopEnumerateDevices(label); + return; + } for (StreamDeviceInfoArray::iterator audio_it = it->second.audio_devices.begin(); audio_it != it->second.audio_devices.end(); ++audio_it) { @@ -256,11 +276,10 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) { video_it != it->second.video_devices.end(); ++video_it) { video_capture_manager()->Close(video_it->session_id); } - if (it->second.type == DeviceRequest::kGenerateStream) { + if (it->second.type == DeviceRequest::GENERATE_STREAM) { NotifyObserverDevicesClosed(&(it->second)); } requests_.erase(it); - return; } } @@ -272,21 +291,53 @@ void MediaStreamManager::EnumerateDevices( const GURL& security_origin, std::string* label) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE || + type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE); // Create a new request. StreamOptions options; - if (type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) + EnumerationCache* cache = NULL; + if (type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { options.audio = true; - else + cache = &audio_enumeration_cache_; + } else { options.video = true; + cache = &video_enumeration_cache_; + } DeviceRequest new_request(requester, options, render_process_id, render_view_id, security_origin); - new_request.type = DeviceRequest::kEnumerateDevices; + new_request.type = DeviceRequest::ENUMERATE_DEVICES; + + if (cache->valid) { + // Cached device list of this type exists. Just send it out. + new_request.state[type] = DeviceRequest::STATE_REQUESTED; + AddRequest(&new_request, label); + // Need to post a task since the requester won't have label till + // this function returns. + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(&MediaStreamManager::SendCachedDeviceList, + base::Unretained(this), cache, *label)); + } else { + StartEnumeration(&new_request, label); + StartMonitoring(); + } +} - StartEnumeration(&new_request, label); +void MediaStreamManager::StopEnumerateDevices(const std::string& label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + DeviceRequests::iterator it = requests_.find(label); + if (it != requests_.end()) { + DCHECK_EQ(it->second.type, DeviceRequest::ENUMERATE_DEVICES); + requests_.erase(it); + if (!HasEnumerationRequest()) { + StopMonitoring(); + } + } } void MediaStreamManager::OpenDevice( @@ -310,12 +361,47 @@ void MediaStreamManager::OpenDevice( render_process_id, render_view_id, security_origin); - new_request.type = DeviceRequest::kOpenDevice; + new_request.type = DeviceRequest::OPEN_DEVICE; new_request.requested_device_id = device_id; StartEnumeration(&new_request, label); } +void MediaStreamManager::SendCachedDeviceList( + EnumerationCache* cache, + const std::string& label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (cache->valid) { + DeviceRequests::iterator it = requests_.find(label); + if (it != requests_.end()) { + it->second.requester->DevicesEnumerated(label, cache->devices); + } + } +} + +void MediaStreamManager::StartMonitoring() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!monitoring_started_) { + monitoring_started_ = true; + base::SystemMonitor::Get()->AddDevicesChangedObserver(this); + } +} + +void MediaStreamManager::StopMonitoring() { + DCHECK_EQ(MessageLoop::current(), io_loop_); + if (monitoring_started_ && !HasEnumerationRequest()) { + base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this); + monitoring_started_ = false; + ClearEnumerationCache(&audio_enumeration_cache_); + ClearEnumerationCache(&video_enumeration_cache_); + } +} + +void MediaStreamManager::ClearEnumerationCache(EnumerationCache* cache) { + DCHECK_EQ(MessageLoop::current(), io_loop_); + cache->valid = false; +} + void MediaStreamManager::StartEnumeration( DeviceRequest* new_request, std::string* label) { @@ -323,21 +409,31 @@ void MediaStreamManager::StartEnumeration( MediaStreamType stream_type = content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE; if (Requested(new_request->options, stream_type)) { - new_request->state[stream_type] = DeviceRequest::kRequested; - if (!enumeration_in_progress_[stream_type]) { - enumeration_in_progress_[stream_type] = true; + new_request->state[stream_type] = DeviceRequest::STATE_REQUESTED; + DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); + if (!active_enumeration_ref_count_[stream_type]) { + ++active_enumeration_ref_count_[stream_type]; GetDeviceManager(stream_type)->EnumerateDevices(); } } stream_type = content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE; if (Requested(new_request->options, stream_type)) { - new_request->state[stream_type] = DeviceRequest::kRequested; - if (!enumeration_in_progress_[stream_type]) { - enumeration_in_progress_[stream_type] = true; + new_request->state[stream_type] = DeviceRequest::STATE_REQUESTED; + DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); + if (!active_enumeration_ref_count_[stream_type]) { + ++active_enumeration_ref_count_[stream_type]; GetDeviceManager(stream_type)->EnumerateDevices(); } } + AddRequest(new_request, label); +} + +void MediaStreamManager::AddRequest( + DeviceRequest* new_request, + std::string* label) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Create a label for this request and verify it is unique. std::string request_label; do { @@ -346,19 +442,6 @@ void MediaStreamManager::StartEnumeration( requests_.insert(std::make_pair(request_label, *new_request)); - // Get user confirmation to use capture devices. - // Need to make an asynchronous call to make sure the |requester| gets the - // |label| before it would receive any event. - if (new_request->type == DeviceRequest::kGenerateStream) { - BrowserThread::PostTask(BrowserThread::IO, - FROM_HERE, - base::Bind(&MediaStreamDeviceSettings::RequestCaptureDeviceUsage, - base::Unretained(device_settings_.get()), - request_label, new_request->render_process_id, - new_request->render_view_id, new_request->options, - new_request->security_origin)); - } - (*label) = request_label; } @@ -414,7 +497,7 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, return; } - DCHECK_NE(request->state[stream_type], DeviceRequest::kRequested); + DCHECK_NE(request->state[stream_type], DeviceRequest::STATE_REQUESTED); // Check if all devices for this stream type are opened. Update the state if // they are. @@ -425,7 +508,7 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, return; } } - request->state[stream_type] = DeviceRequest::kDone; + request->state[stream_type] = DeviceRequest::STATE_DONE; if (!RequestDone(*request)) { // This stream_type is done, but not the other type. @@ -433,10 +516,10 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, } switch (request->type) { - case DeviceRequest::kOpenDevice: + case DeviceRequest::OPEN_DEVICE: request->requester->DeviceOpened(label, (*devices)[0]); break; - case DeviceRequest::kGenerateStream: + case DeviceRequest::GENERATE_STREAM: request->requester->StreamGenerated(label, request->audio_devices, request->video_devices); NotifyObserverDevicesOpened(request); @@ -455,6 +538,21 @@ void MediaStreamManager::DevicesEnumerated( MediaStreamType stream_type, const StreamDeviceInfoArray& devices) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Only cache device list when there is EnumerateDevices request, since + // other requests don't turn on device monitoring. + bool need_update_clients = false; + EnumerationCache* cache = + (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE ? + &audio_enumeration_cache_ : &video_enumeration_cache_); + if (HasEnumerationRequest(stream_type) && + (!cache->valid || + !std::equal(devices.begin(), devices.end(), cache->devices.begin(), + media_stream::StreamDeviceInfo::IsEqual))) { + cache->valid = true; + cache->devices = devices; + need_update_clients = true; + } + // Publish the result for all requests waiting for device list(s). // Find the requests waiting for this device list, store their labels and // release the iterator before calling device settings. We might get a call @@ -462,9 +560,10 @@ void MediaStreamManager::DevicesEnumerated( std::list<std::string> label_list; for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); ++it) { - if (it->second.state[stream_type] == DeviceRequest::kRequested && + if (it->second.state[stream_type] == DeviceRequest::STATE_REQUESTED && Requested(it->second.options, stream_type)) { - it->second.state[stream_type] = DeviceRequest::kPendingApproval; + if (it->second.type != DeviceRequest::ENUMERATE_DEVICES) + it->second.state[stream_type] = DeviceRequest::STATE_PENDING_APPROVAL; label_list.push_back(it->first); } } @@ -472,11 +571,11 @@ void MediaStreamManager::DevicesEnumerated( it != label_list.end(); ++it) { DeviceRequest& request = requests_[*it]; switch (request.type) { - case DeviceRequest::kEnumerateDevices: - request.requester->DevicesEnumerated(*it, devices); - requests_.erase(*it); + case DeviceRequest::ENUMERATE_DEVICES: + if (need_update_clients) + request.requester->DevicesEnumerated(*it, devices); break; - case DeviceRequest::kOpenDevice: + case DeviceRequest::OPEN_DEVICE: for (StreamDeviceInfoArray::const_iterator device_it = devices.begin(); device_it != devices.end(); device_it++) { if (request.requested_device_id == device_it->device_id) { @@ -484,7 +583,8 @@ void MediaStreamManager::DevicesEnumerated( device.in_use = false; device.session_id = GetDeviceManager(device_it->stream_type)->Open(device); - request.state[device_it->stream_type] = DeviceRequest::kOpening; + request.state[device_it->stream_type] = + DeviceRequest::STATE_OPENING; if (stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) request.audio_devices.push_back(device); else @@ -502,7 +602,8 @@ void MediaStreamManager::DevicesEnumerated( } } label_list.clear(); - enumeration_in_progress_[stream_type] = false; + --active_enumeration_ref_count_[stream_type]; + DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); } void MediaStreamManager::Error(MediaStreamType stream_type, @@ -526,7 +627,7 @@ void MediaStreamManager::Error(MediaStreamType stream_type, device_it != devices->end(); ++device_it, ++device_idx) { if (device_it->session_id == capture_session_id) { // We've found the failing device. Find the error case: - if (it->second.state[stream_type] == DeviceRequest::kDone) { + if (it->second.state[stream_type] == DeviceRequest::STATE_DONE) { // 1. Already opened -> signal device failure and close device. // Use device_idx to signal which of the devices encountered an // error. @@ -576,12 +677,12 @@ void MediaStreamManager::DevicesAccepted(const std::string& label, // opened. in_use might be true if the device type can be used in more // than one session. DCHECK_EQ(request_it->second.state[device_it->stream_type], - DeviceRequest::kPendingApproval); + DeviceRequest::STATE_PENDING_APPROVAL); device_info.in_use = false; device_info.session_id = GetDeviceManager(device_info.stream_type)->Open(device_info); request_it->second.state[device_it->stream_type] = - DeviceRequest::kOpening; + DeviceRequest::STATE_OPENING; if (device_info.stream_type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) { request_it->second.audio_devices.push_back(device_info); @@ -597,12 +698,12 @@ void MediaStreamManager::DevicesAccepted(const std::string& label, content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE; if (Requested(request_it->second.options, stream_type) && request_it->second.audio_devices.size() == 0) { - request_it->second.state[stream_type] = DeviceRequest::kError; + request_it->second.state[stream_type] = DeviceRequest::STATE_ERROR; } stream_type = content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE; if (Requested(request_it->second.options, stream_type) && request_it->second.video_devices.size() == 0) { - request_it->second.state[stream_type] = DeviceRequest::kError; + request_it->second.state[stream_type] = DeviceRequest::STATE_ERROR; } return; } @@ -613,7 +714,7 @@ void MediaStreamManager::SettingsError(const std::string& label) { // Erase this request and report an error. DeviceRequests::iterator it = requests_.find(label); if (it != requests_.end()) { - DCHECK_EQ(it->second.type, DeviceRequest::kGenerateStream); + DCHECK_EQ(it->second.type, DeviceRequest::GENERATE_STREAM); it->second.requester->StreamGenerationFailed(label); requests_.erase(it); return; @@ -628,7 +729,10 @@ void MediaStreamManager::UseFakeDevice() { void MediaStreamManager::WillDestroyCurrentMessageLoop() { DCHECK_EQ(MessageLoop::current(), io_loop_); + DCHECK(requests_.empty()); if (device_thread_.get()) { + StopMonitoring(); + video_capture_manager_->Unregister(); audio_input_device_manager_->Unregister(); device_thread_.reset(); @@ -721,4 +825,57 @@ MediaStreamProvider* MediaStreamManager::GetDeviceManager( return NULL; } +void MediaStreamManager::OnDevicesChanged( + base::SystemMonitor::DeviceType device_type) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + MediaStreamType stream_type; + EnumerationCache* cache; + if (device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE) { + stream_type = content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE; + cache = &audio_enumeration_cache_; + } else if (device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE) { + stream_type = content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE; + cache = &video_enumeration_cache_; + } else { + return; // Uninteresting device change. + } + + if (!HasEnumerationRequest(stream_type)) { + // There is no request for that type, No need to enumerate devices. + // Therefore, invalidate the cache of that type. + ClearEnumerationCache(cache); + return; + } + + // Always do enumeration even though some enumeration is in progress, + // because those enumeration commands could be sent before these devices + // change. + ++active_enumeration_ref_count_[stream_type]; + GetDeviceManager(stream_type)->EnumerateDevices(); +} + +bool MediaStreamManager::HasEnumerationRequest() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + for (DeviceRequests::iterator it = requests_.begin(); + it != requests_.end(); ++it) { + if (it->second.type == DeviceRequest::ENUMERATE_DEVICES) { + return true; + } + } + return false; +} + +bool MediaStreamManager::HasEnumerationRequest( + MediaStreamType stream_type) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + for (DeviceRequests::iterator it = requests_.begin(); + it != requests_.end(); ++it) { + if (it->second.type == DeviceRequest::ENUMERATE_DEVICES && + Requested(it->second.options, stream_type)) { + return true; + } + } + return false; +} + } // namespace media_stream diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h index 648f21c..6fb5269 100644 --- a/content/browser/renderer_host/media/media_stream_manager.h +++ b/content/browser/renderer_host/media/media_stream_manager.h @@ -30,6 +30,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/ref_counted.h" #include "base/message_loop.h" +#include "base/system_monitor/system_monitor.h" #include "base/threading/thread.h" #include "content/browser/renderer_host/media/media_stream_provider.h" #include "content/browser/renderer_host/media/media_stream_settings_requester.h" @@ -72,7 +73,8 @@ class DeviceThread : public base::Thread { class CONTENT_EXPORT MediaStreamManager : public MediaStreamProviderListener, public MessageLoop::DestructionObserver, - public SettingsRequester { + public SettingsRequester, + public base::SystemMonitor::DevicesChangedObserver { public: // This class takes the ownerships of the |audio_input_device_manager| // and |video_capture_manager|. @@ -107,6 +109,9 @@ class CONTENT_EXPORT MediaStreamManager // Gets a list of devices of |type|. // The request is identified using |label|, which is pointing to a // std::string. + // The request is persistent, which means the client keeps listening to + // device changes, such as plug/unplug, and expects new device list for + // such a change, till the client stops the request. void EnumerateDevices(MediaStreamRequester* requester, int render_process_id, int render_view_id, @@ -141,6 +146,10 @@ class CONTENT_EXPORT MediaStreamManager const StreamDeviceInfoArray& devices) OVERRIDE; virtual void SettingsError(const std::string& label) OVERRIDE; + // Implements base::SystemMonitor::DevicesChangedObserver. + virtual void OnDevicesChanged( + base::SystemMonitor::DeviceType device_type) OVERRIDE; + // Used by unit test to make sure fake devices are used instead of a real // devices, which is needed for server based testing. // TODO(xians): Remove this hack since we can create our own @@ -156,6 +165,15 @@ class CONTENT_EXPORT MediaStreamManager // Contains all data needed to keep track of requests. struct DeviceRequest; + // Cache enumerated device list. + struct EnumerationCache { + EnumerationCache(); + ~EnumerationCache(); + + bool valid; + StreamDeviceInfoArray devices; + }; + // Helpers for signaling the media observer that new capture devices are // opened/closed. void NotifyObserverDevicesOpened(DeviceRequest* request); @@ -168,11 +186,26 @@ class CONTENT_EXPORT MediaStreamManager MediaStreamProvider* GetDeviceManager(MediaStreamType stream_type); void StartEnumeration(DeviceRequest* new_request, std::string* label); + void AddRequest(DeviceRequest* new_request, std::string* label); + bool HasEnumerationRequest(MediaStreamType type); + bool HasEnumerationRequest(); + void ClearEnumerationCache(EnumerationCache* cache); // Helper to ensure the device thread and pass the message loop to device // managers, it also register itself as the listener to the device managers. void EnsureDeviceThreadAndListener(); + // Sends cached device list to a client corresponding to the request + // identified by |label|. + void SendCachedDeviceList(EnumerationCache* cache, const std::string& label); + + // Stop the request of enumerating devices indentified by |label|. + void StopEnumerateDevices(const std::string& label); + + // Helpers to start and stop monitoring devices. + void StartMonitoring(); + void StopMonitoring(); + // Device thread shared by VideoCaptureManager and AudioInputDeviceManager. scoped_ptr<base::Thread> device_thread_; @@ -180,9 +213,17 @@ class CONTENT_EXPORT MediaStreamManager scoped_refptr<AudioInputDeviceManager> audio_input_device_manager_; scoped_refptr<VideoCaptureManager> video_capture_manager_; - // Keeps track of device types currently being enumerated to not enumerate - // when not necessary. - std::vector<bool> enumeration_in_progress_; + // Indicator of device monitoring state. + bool monitoring_started_; + + // Stores most recently enumerated device lists. The cache is cleared when + // monitoring is stopped or there is no request for that type of device. + EnumerationCache audio_enumeration_cache_; + EnumerationCache video_enumeration_cache_; + + // Keeps track of live enumeration commands sent to VideoCaptureManager or + // AudioInputDeviceManager, in order to only enumerate when necessary. + int active_enumeration_ref_count_[content::NUM_MEDIA_STREAM_DEVICE_TYPES]; // All non-closed request. typedef std::map<std::string, DeviceRequest> DeviceRequests; diff --git a/content/common/media/media_stream_messages.h b/content/common/media/media_stream_messages.h index 1f87b10..5c89f8d 100644 --- a/content/common/media/media_stream_messages.h +++ b/content/common/media/media_stream_messages.h @@ -55,8 +55,9 @@ IPC_MESSAGE_ROUTED2(MediaStreamHostMsg_AudioDeviceFailed, int /* index */) // The browser has enumerated devices successfully. -IPC_MESSAGE_ROUTED2(MediaStreamMsg_DevicesEnumerated, +IPC_MESSAGE_ROUTED3(MediaStreamMsg_DevicesEnumerated, int /* request id */, + std::string /* label */, media_stream::StreamDeviceInfoArray /* device_list */) // The browser has failed to enumerate devices. diff --git a/content/common/media/media_stream_options.cc b/content/common/media/media_stream_options.cc index 28ee038..9daa96a 100644 --- a/content/common/media/media_stream_options.cc +++ b/content/common/media/media_stream_options.cc @@ -24,4 +24,14 @@ StreamDeviceInfo::StreamDeviceInfo(MediaStreamType service_param, in_use(opened), session_id(kNoId) {} +// static +bool StreamDeviceInfo::IsEqual(const StreamDeviceInfo& first, + const StreamDeviceInfo& second) { + return first.stream_type == second.stream_type && + first.name == second.name && + first.device_id == second.device_id && + first.in_use == second.in_use && + first.session_id == second.session_id; +} + } // namespace media_stream diff --git a/content/common/media/media_stream_options.h b/content/common/media/media_stream_options.h index e5127ed..c51d700 100644 --- a/content/common/media/media_stream_options.h +++ b/content/common/media/media_stream_options.h @@ -39,6 +39,8 @@ struct CONTENT_EXPORT StreamDeviceInfo { const std::string& name_param, const std::string& device_param, bool opened); + static bool IsEqual(const StreamDeviceInfo& first, + const StreamDeviceInfo& second); // Describes the capture type. MediaStreamType stream_type; diff --git a/content/renderer/media/media_stream_dispatcher.cc b/content/renderer/media/media_stream_dispatcher.cc index ed55342..5d97ca0 100644 --- a/content/renderer/media/media_stream_dispatcher.cc +++ b/content/renderer/media/media_stream_dispatcher.cc @@ -31,8 +31,36 @@ struct MediaStreamDispatcher::Stream { media_stream::StreamDeviceInfoArray video_array; }; +MediaStreamDispatcher::EnumerationRequest::EnumerationRequest( + const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler, + int request_id) + : handler(handler), + request_id(request_id) { +} + +MediaStreamDispatcher::EnumerationRequest::~EnumerationRequest() {} + +MediaStreamDispatcher::EnumerationState::EnumerationState() + : ipc_id(-1) { +} + +MediaStreamDispatcher::EnumerationState::~EnumerationState() {} + +struct MediaStreamDispatcher::EnumerationState::CachedDevices { + CachedDevices(const std::string& label, + const media_stream::StreamDeviceInfoArray& device_array) + : label(label), + devices(device_array) { + } + ~CachedDevices() {} + + std::string label; + media_stream::StreamDeviceInfoArray devices; +}; + MediaStreamDispatcher::MediaStreamDispatcher(RenderViewImpl* render_view) : content::RenderViewObserver(render_view), + main_loop_(base::MessageLoopProxy::current()), next_ipc_id_(0) { } @@ -43,6 +71,7 @@ void MediaStreamDispatcher::GenerateStream( const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, media_stream::StreamOptions components, const GURL& security_origin) { + DCHECK(main_loop_->BelongsToCurrentThread()); DVLOG(1) << "MediaStreamDispatcher::GenerateStream(" << request_id << ")"; requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); @@ -53,6 +82,7 @@ void MediaStreamDispatcher::GenerateStream( } void MediaStreamDispatcher::CancelGenerateStream(int request_id) { + DCHECK(main_loop_->BelongsToCurrentThread()); DVLOG(1) << "MediaStreamDispatcher::CancelGenerateStream" << ", {request_id = " << request_id << "}"; @@ -69,6 +99,7 @@ void MediaStreamDispatcher::CancelGenerateStream(int request_id) { } void MediaStreamDispatcher::StopStream(const std::string& label) { + DCHECK(main_loop_->BelongsToCurrentThread()); DVLOG(1) << "MediaStreamDispatcher::StopStream" << ", {label = " << label << "}"; @@ -85,14 +116,64 @@ void MediaStreamDispatcher::EnumerateDevices( const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, media_stream::MediaStreamType type, const GURL& security_origin) { + DCHECK(main_loop_->BelongsToCurrentThread()); + DCHECK(type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE || + type == content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE); DVLOG(1) << "MediaStreamDispatcher::EnumerateDevices(" << request_id << ")"; - requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); - Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(), - next_ipc_id_++, - type, - security_origin)); + EnumerationState* state = + (type == content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE ? + &audio_enumeration_state_ : &video_enumeration_state_); + state->requests.push_back( + EnumerationRequest(event_handler, request_id)); + + if (state->cached_devices.get()) { + event_handler->OnDevicesEnumerated( + request_id, state->cached_devices->devices); + } else if (state->ipc_id < 0) { + Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(), + next_ipc_id_, + type, + security_origin)); + state->ipc_id = next_ipc_id_++; + } +} + +void MediaStreamDispatcher::StopEnumerateDevices( + int request_id, + const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler) { + DCHECK(main_loop_->BelongsToCurrentThread()); + DVLOG(1) << "MediaStreamDispatcher::StopEnumerateDevices(" + << request_id << ")"; + + // Remove the request. + RemoveEnumerationRequest( + request_id, event_handler, &audio_enumeration_state_); + RemoveEnumerationRequest( + request_id, event_handler, &video_enumeration_state_); +} + +void MediaStreamDispatcher::RemoveEnumerationRequest( + int request_id, + const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, + EnumerationState* state) { + EnumerationRequestList* requests = &state->requests; + for (EnumerationRequestList::iterator it = requests->begin(); + it != requests->end(); ++it) { + if (it->request_id == request_id && it->handler == event_handler) { + requests->erase(it); + if (requests->empty() && !state->cached_devices.get()) { + // No more request and has a label, try to stop the label + // and invalidate the state. + Send(new MediaStreamHostMsg_StopGeneratedStream( + routing_id(), state->cached_devices->label)); + state->ipc_id = -1; + state->cached_devices.reset(); + } + return; + } + } } void MediaStreamDispatcher::OpenDevice( @@ -101,6 +182,7 @@ void MediaStreamDispatcher::OpenDevice( const std::string& device_id, media_stream::MediaStreamType type, const GURL& security_origin) { + DCHECK(main_loop_->BelongsToCurrentThread()); DVLOG(1) << "MediaStreamDispatcher::OpenDevice(" << request_id << ")"; requests_.push_back(Request(event_handler, request_id, next_ipc_id_)); @@ -112,6 +194,7 @@ void MediaStreamDispatcher::OpenDevice( } void MediaStreamDispatcher::CloseDevice(const std::string& label) { + DCHECK(main_loop_->BelongsToCurrentThread()); DVLOG(1) << "MediaStreamDispatcher::CloseDevice" << ", {label = " << label << "}"; @@ -147,6 +230,7 @@ void MediaStreamDispatcher::OnStreamGenerated( const std::string& label, const media_stream::StreamDeviceInfoArray& audio_array, const media_stream::StreamDeviceInfoArray& video_array) { + DCHECK(main_loop_->BelongsToCurrentThread()); for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { @@ -170,6 +254,7 @@ void MediaStreamDispatcher::OnStreamGenerated( } void MediaStreamDispatcher::OnStreamGenerationFailed(int request_id) { + DCHECK(main_loop_->BelongsToCurrentThread()); for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { Request& request = *it; @@ -187,6 +272,7 @@ void MediaStreamDispatcher::OnStreamGenerationFailed(int request_id) { void MediaStreamDispatcher::OnVideoDeviceFailed(const std::string& label, int index) { + DCHECK(main_loop_->BelongsToCurrentThread()); LabelStreamMap::iterator it = label_stream_map_.find(label); if (it == label_stream_map_.end()) return; @@ -202,6 +288,7 @@ void MediaStreamDispatcher::OnVideoDeviceFailed(const std::string& label, void MediaStreamDispatcher::OnAudioDeviceFailed(const std::string& label, int index) { + DCHECK(main_loop_->BelongsToCurrentThread()); LabelStreamMap::iterator it = label_stream_map_.find(label); if (it == label_stream_map_.end()) return; @@ -217,24 +304,41 @@ void MediaStreamDispatcher::OnAudioDeviceFailed(const std::string& label, void MediaStreamDispatcher::OnDevicesEnumerated( int request_id, + const std::string& label, const media_stream::StreamDeviceInfoArray& device_array) { + DCHECK(main_loop_->BelongsToCurrentThread()); + DCHECK_GE(request_id, 0); + + EnumerationState* state; + if (request_id == audio_enumeration_state_.ipc_id) { + state = &audio_enumeration_state_; + } else if (request_id == video_enumeration_state_.ipc_id) { + state = &video_enumeration_state_; + } else { + // This could happen when requester has stopped enumeration while some + // enumerated response is on the way. Have to stop the |label| because + // this might be the first enumerated device list is received. This also + // lead to same label being stopped multiple times. + Send(new MediaStreamHostMsg_StopGeneratedStream(routing_id(), label)); + return; + } - for (RequestList::iterator it = requests_.begin(); - it != requests_.end(); ++it) { - Request& request = *it; - if (request.ipc_request == request_id) { - if (request.handler) { - request.handler->OnDevicesEnumerated(request.request_id, device_array); - DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerated(" - << request.request_id << ")"; - } - requests_.erase(it); - break; + DCHECK(!label.empty()); + state->cached_devices.reset(new EnumerationState::CachedDevices( + label, device_array)); + + for (EnumerationRequestList::iterator it = state->requests.begin(); + it != state->requests.end(); ++it) { + if (it->handler) { + it->handler->OnDevicesEnumerated(it->request_id, device_array); + DVLOG(1) << "MediaStreamDispatcher::OnDevicesEnumerated(" + << it->request_id << ")"; } } } void MediaStreamDispatcher::OnDevicesEnumerationFailed(int request_id) { + DCHECK(main_loop_->BelongsToCurrentThread()); for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { Request& request = *it; @@ -254,6 +358,7 @@ void MediaStreamDispatcher::OnDeviceOpened( int request_id, const std::string& label, const media_stream::StreamDeviceInfo& device_info) { + DCHECK(main_loop_->BelongsToCurrentThread()); for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { Request& request = *it; @@ -280,6 +385,7 @@ void MediaStreamDispatcher::OnDeviceOpened( } void MediaStreamDispatcher::OnDeviceOpenFailed(int request_id) { + DCHECK(main_loop_->BelongsToCurrentThread()); for (RequestList::iterator it = requests_.begin(); it != requests_.end(); ++it) { Request& request = *it; diff --git a/content/renderer/media/media_stream_dispatcher.h b/content/renderer/media/media_stream_dispatcher.h index a000c52..549807e 100644 --- a/content/renderer/media/media_stream_dispatcher.h +++ b/content/renderer/media/media_stream_dispatcher.h @@ -11,12 +11,17 @@ #include "base/basictypes.h" #include "base/gtest_prod_util.h" -#include "base/message_loop.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "content/common/media/media_stream_options.h" #include "content/public/renderer/render_view_observer.h" #include "content/renderer/media/media_stream_dispatcher_eventhandler.h" +namespace base { +class MessageLoopProxy; +} + class RenderViewImpl; // MediaStreamDispatcher is a delegate for the Media Stream API messages. @@ -25,7 +30,8 @@ class RenderViewImpl; // It's the complement of MediaStreamDispatcherHost (owned by // BrowserRenderProcessHost). class CONTENT_EXPORT MediaStreamDispatcher - : public content::RenderViewObserver { + : public content::RenderViewObserver, + public base::SupportsWeakPtr<MediaStreamDispatcher> { public: explicit MediaStreamDispatcher(RenderViewImpl* render_view); virtual ~MediaStreamDispatcher(); @@ -52,6 +58,11 @@ class CONTENT_EXPORT MediaStreamDispatcher media_stream::MediaStreamType type, const GURL& security_origin); + // Request to stop enumerating devices. + void StopEnumerateDevices( + int request_id, + const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler); + // Request to open a device. void OpenDevice( int request_id, @@ -83,6 +94,31 @@ class CONTENT_EXPORT MediaStreamDispatcher // opened it. struct Stream; + struct EnumerationRequest { + EnumerationRequest( + const base::WeakPtr<MediaStreamDispatcherEventHandler>& handler, + int request_id); + ~EnumerationRequest(); + + base::WeakPtr<MediaStreamDispatcherEventHandler> handler; + int request_id; + }; + + // List of requests made to EnumerateDevices. + typedef std::list<EnumerationRequest> EnumerationRequestList; + + struct EnumerationState { + EnumerationState(); + ~EnumerationState(); + + struct CachedDevices; + + // If |ipc_id| >= 0, then we've started. + int ipc_id; + scoped_ptr<CachedDevices> cached_devices; + EnumerationRequestList requests; + }; + // Messages from the browser. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; void OnStreamGenerated( @@ -95,6 +131,7 @@ class CONTENT_EXPORT MediaStreamDispatcher void OnAudioDeviceFailed(const std::string& label, int index); void OnDevicesEnumerated( int request_id, + const std::string& label, const media_stream::StreamDeviceInfoArray& device_array); void OnDevicesEnumerationFailed(int request_id); void OnDeviceOpened( @@ -103,10 +140,21 @@ class CONTENT_EXPORT MediaStreamDispatcher const media_stream::StreamDeviceInfo& device_info); void OnDeviceOpenFailed(int request_id); + void RemoveEnumerationRequest( + int request_id, + const base::WeakPtr<MediaStreamDispatcherEventHandler>& event_handler, + EnumerationState* state); + + // Used for DCHECKs so methods calls won't execute in the wrong thread. + scoped_refptr<base::MessageLoopProxy> main_loop_; + int next_ipc_id_; typedef std::map<std::string, Stream> LabelStreamMap; LabelStreamMap label_stream_map_; + EnumerationState audio_enumeration_state_; + EnumerationState video_enumeration_state_; + // List of calls made to GenerateStream that has not yet completed. typedef std::list<Request> RequestList; RequestList requests_; diff --git a/content/renderer/media/media_stream_dispatcher_unittest.cc b/content/renderer/media/media_stream_dispatcher_unittest.cc index c83c8b3..458d61b 100644 --- a/content/renderer/media/media_stream_dispatcher_unittest.cc +++ b/content/renderer/media/media_stream_dispatcher_unittest.cc @@ -22,6 +22,7 @@ const int kRequestId1 = 10; const int kRequestId2 = 20; const int kRequestId3 = 30; const int kRequestId4 = 40; +static const char kLabel[] = "test"; class MockMediaStreamDispatcherEventHandler : public MediaStreamDispatcherEventHandler, @@ -88,6 +89,7 @@ class MockMediaStreamDispatcherEventHandler } // namespace TEST(MediaStreamDispatcherTest, BasicStream) { + scoped_ptr<MessageLoop> message_loop(new MessageLoop()); scoped_ptr<MediaStreamDispatcher> dispatcher(new MediaStreamDispatcher(NULL)); scoped_ptr<MockMediaStreamDispatcherEventHandler> handler(new MockMediaStreamDispatcherEventHandler); @@ -163,21 +165,24 @@ TEST(MediaStreamDispatcherTest, BasicStream) { } TEST(MediaStreamDispatcherTest, BasicVideoDevice) { + scoped_ptr<MessageLoop> message_loop(new MessageLoop()); scoped_ptr<MediaStreamDispatcher> dispatcher(new MediaStreamDispatcher(NULL)); scoped_ptr<MockMediaStreamDispatcherEventHandler> - handler(new MockMediaStreamDispatcherEventHandler); + handler1(new MockMediaStreamDispatcherEventHandler); + scoped_ptr<MockMediaStreamDispatcherEventHandler> + handler2(new MockMediaStreamDispatcherEventHandler); GURL security_origin; int ipc_request_id1 = dispatcher->next_ipc_id_; - dispatcher->EnumerateDevices(kRequestId1, handler.get()->AsWeakPtr(), + dispatcher->EnumerateDevices(kRequestId1, handler1.get()->AsWeakPtr(), content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE, security_origin); int ipc_request_id2 = dispatcher->next_ipc_id_; EXPECT_NE(ipc_request_id1, ipc_request_id2); - dispatcher->EnumerateDevices(kRequestId2, handler.get()->AsWeakPtr(), + dispatcher->EnumerateDevices(kRequestId2, handler2.get()->AsWeakPtr(), content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE, security_origin); - EXPECT_EQ(dispatcher->requests_.size(), size_t(2)); + EXPECT_EQ(dispatcher->video_enumeration_state_.requests.size(), size_t(2)); media_stream::StreamDeviceInfoArray video_device_array(1); media_stream::StreamDeviceInfo video_device_info; @@ -188,27 +193,23 @@ TEST(MediaStreamDispatcherTest, BasicVideoDevice) { video_device_info.session_id = kVideoSessionId; video_device_array[0] = video_device_info; - // Complete the enumeration of request 1. - dispatcher->OnMessageReceived(MediaStreamMsg_DevicesEnumerated( - kRouteId, ipc_request_id1, video_device_array)); - EXPECT_EQ(handler->request_id_, kRequestId1); - - // Complete the enumeration of request 2. + // Complete the enumeration request and all requesters should receive reply. dispatcher->OnMessageReceived(MediaStreamMsg_DevicesEnumerated( - kRouteId, ipc_request_id2, video_device_array)); - EXPECT_EQ(handler->request_id_, kRequestId2); + kRouteId, ipc_request_id1, kLabel, video_device_array)); + EXPECT_EQ(handler1->request_id_, kRequestId1); + EXPECT_EQ(handler2->request_id_, kRequestId2); - EXPECT_EQ(dispatcher->requests_.size(), size_t(0)); + EXPECT_EQ(dispatcher->video_enumeration_state_.requests.size(), size_t(2)); EXPECT_EQ(dispatcher->label_stream_map_.size(), size_t(0)); int ipc_request_id3 = dispatcher->next_ipc_id_; - dispatcher->OpenDevice(kRequestId3, handler.get()->AsWeakPtr(), + dispatcher->OpenDevice(kRequestId3, handler1.get()->AsWeakPtr(), video_device_info.device_id, content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE, security_origin); int ipc_request_id4 = dispatcher->next_ipc_id_; EXPECT_NE(ipc_request_id3, ipc_request_id4); - dispatcher->OpenDevice(kRequestId4, handler.get()->AsWeakPtr(), + dispatcher->OpenDevice(kRequestId4, handler1.get()->AsWeakPtr(), video_device_info.device_id, content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE, security_origin); @@ -218,13 +219,13 @@ TEST(MediaStreamDispatcherTest, BasicVideoDevice) { std::string stream_label1 = std::string("stream1"); dispatcher->OnMessageReceived(MediaStreamMsg_DeviceOpened( kRouteId, ipc_request_id3, stream_label1, video_device_info)); - EXPECT_EQ(handler->request_id_, kRequestId3); + EXPECT_EQ(handler1->request_id_, kRequestId3); // Complete the OpenDevice of request 2. std::string stream_label2 = std::string("stream2"); dispatcher->OnMessageReceived(MediaStreamMsg_DeviceOpened( kRouteId, ipc_request_id4, stream_label2, video_device_info)); - EXPECT_EQ(handler->request_id_, kRequestId4); + EXPECT_EQ(handler1->request_id_, kRequestId4); EXPECT_EQ(dispatcher->requests_.size(), size_t(0)); EXPECT_EQ(dispatcher->label_stream_map_.size(), size_t(2)); @@ -250,6 +251,7 @@ TEST(MediaStreamDispatcherTest, BasicVideoDevice) { } TEST(MediaStreamDispatcherTest, TestFailure) { + scoped_ptr<MessageLoop> message_loop(new MessageLoop()); scoped_ptr<MediaStreamDispatcher> dispatcher(new MediaStreamDispatcher(NULL)); scoped_ptr<MockMediaStreamDispatcherEventHandler> handler(new MockMediaStreamDispatcherEventHandler); @@ -316,6 +318,7 @@ TEST(MediaStreamDispatcherTest, TestFailure) { } TEST(MediaStreamDispatcherTest, CancelGenerateStream) { + scoped_ptr<MessageLoop> message_loop(new MessageLoop()); scoped_ptr<MediaStreamDispatcher> dispatcher(new MediaStreamDispatcher(NULL)); scoped_ptr<MockMediaStreamDispatcherEventHandler> handler(new MockMediaStreamDispatcherEventHandler); diff --git a/content/renderer/pepper/pepper_device_enumeration_event_handler.cc b/content/renderer/pepper/pepper_device_enumeration_event_handler.cc index d5a1d20..3ea089c 100644 --- a/content/renderer/pepper/pepper_device_enumeration_event_handler.cc +++ b/content/renderer/pepper/pepper_device_enumeration_event_handler.cc @@ -127,7 +127,8 @@ void PepperDeviceEnumerationEventHandler::NotifyDevicesEnumerated( const media_stream::StreamDeviceInfoArray& device_array) { EnumerateCallbackMap::iterator iter = enumerate_callbacks_.find(request_id); if (iter == enumerate_callbacks_.end()) { - NOTREACHED(); + // This might be enumerated result sent before StopEnumerateDevices is + // called since EnumerateDevices is persistent request. return; } diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.cc b/content/renderer/pepper/pepper_plugin_delegate_impl.cc index 351b0a0..0675c2f 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.cc +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.cc @@ -1506,6 +1506,19 @@ int PepperPluginDelegateImpl::EnumerateDevices( return request_id; } +void PepperPluginDelegateImpl::StopEnumerateDevices(int request_id) { +#if defined(ENABLE_WEBRTC) + // Need to post task since this function might be called inside the callback + // of EnumerateDevices. + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind( + &MediaStreamDispatcher::StopEnumerateDevices, + render_view_->media_stream_dispatcher()->AsWeakPtr(), + request_id, device_enumeration_event_handler_.get()->AsWeakPtr())); +#endif +} + bool PepperPluginDelegateImpl::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PepperPluginDelegateImpl, message) diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.h b/content/renderer/pepper/pepper_plugin_delegate_impl.h index 83d3103..09e3986 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.h +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.h @@ -377,6 +377,7 @@ class PepperPluginDelegateImpl virtual int EnumerateDevices( PP_DeviceType_Dev type, const EnumerateDevicesCallback& callback) OVERRIDE; + virtual void StopEnumerateDevices(int request_id) OVERRIDE; virtual webkit_glue::ClipboardClient* CreateClipboardClient() const OVERRIDE; virtual std::string GetDeviceID() OVERRIDE; virtual PP_FlashLSORestrictions GetLocalDataRestrictions( diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.cc b/webkit/plugins/ppapi/mock_plugin_delegate.cc index 298ddc9..782c728 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.cc +++ b/webkit/plugins/ppapi/mock_plugin_delegate.cc @@ -451,6 +451,9 @@ int MockPluginDelegate::EnumerateDevices( return -1; } +void MockPluginDelegate::StopEnumerateDevices(int request_id) { +} + webkit_glue::ClipboardClient* MockPluginDelegate::CreateClipboardClient() const { return NULL; diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.h b/webkit/plugins/ppapi/mock_plugin_delegate.h index 3fa4677..038bd25 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.h +++ b/webkit/plugins/ppapi/mock_plugin_delegate.h @@ -202,6 +202,7 @@ class MockPluginDelegate : public PluginDelegate { virtual bool IsPageVisible() const; virtual int EnumerateDevices(PP_DeviceType_Dev type, const EnumerateDevicesCallback& callback); + virtual void StopEnumerateDevices(int request_id); virtual webkit_glue::ClipboardClient* CreateClipboardClient() const; virtual std::string GetDeviceID(); virtual PP_FlashLSORestrictions GetLocalDataRestrictions( diff --git a/webkit/plugins/ppapi/plugin_delegate.h b/webkit/plugins/ppapi/plugin_delegate.h index ab237a3..fd8a2a5 100644 --- a/webkit/plugins/ppapi/plugin_delegate.h +++ b/webkit/plugins/ppapi/plugin_delegate.h @@ -659,6 +659,9 @@ class PluginDelegate { // callback will be the same as the return value. virtual int EnumerateDevices(PP_DeviceType_Dev type, const EnumerateDevicesCallback& callback) = 0; + // Stop enumerating devices of the specified |request_id|. The |request_id| + // is the return value of EnumerateDevicesCallback. + virtual void StopEnumerateDevices(int request_id) = 0; // Create a ClipboardClient for writing to the clipboard. The caller will own // the pointer to this. virtual webkit_glue::ClipboardClient* CreateClipboardClient() const = 0; diff --git a/webkit/plugins/ppapi/ppb_audio_input_impl.cc b/webkit/plugins/ppapi/ppb_audio_input_impl.cc index 3b4d609..f4f40c0 100644 --- a/webkit/plugins/ppapi/ppb_audio_input_impl.cc +++ b/webkit/plugins/ppapi/ppb_audio_input_impl.cc @@ -175,6 +175,10 @@ void PPB_AudioInput_Impl::EnumerateDevicesCallbackFunc( if (succeeded) devices_data_ = devices; + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (plugin_delegate) + plugin_delegate->StopEnumerateDevices(request_id); + OnEnumerateDevicesComplete(succeeded ? PP_OK : PP_ERROR_FAILED, devices); } diff --git a/webkit/plugins/ppapi/ppb_video_capture_impl.cc b/webkit/plugins/ppapi/ppb_video_capture_impl.cc index a39f46a..caf9027 100644 --- a/webkit/plugins/ppapi/ppb_video_capture_impl.cc +++ b/webkit/plugins/ppapi/ppb_video_capture_impl.cc @@ -312,6 +312,10 @@ void PPB_VideoCapture_Impl::EnumerateDevicesCallbackFunc( if (succeeded) devices_data_ = devices; + PluginInstance* instance = ResourceHelper::GetPluginInstance(this); + if (instance) + instance->delegate()->StopEnumerateDevices(request_id); + OnEnumerateDevicesComplete(succeeded ? PP_OK : PP_ERROR_FAILED, devices); } |