summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-09 16:01:34 +0000
committerwjia@chromium.org <wjia@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-09 16:01:34 +0000
commit0874f3cb7daa931fcf72396f73879695622a4225 (patch)
tree8762b5639ca5ae11f969d2d2396f0e44c7144ee2
parentce2b28efbc93b747f0ae37ac672ae2d831e68214 (diff)
downloadchromium_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
-rw-r--r--content/browser/renderer_host/media/media_stream_dispatcher_host.cc4
-rw-r--r--content/browser/renderer_host/media/media_stream_manager.cc297
-rw-r--r--content/browser/renderer_host/media/media_stream_manager.h49
-rw-r--r--content/common/media/media_stream_messages.h3
-rw-r--r--content/common/media/media_stream_options.cc10
-rw-r--r--content/common/media/media_stream_options.h2
-rw-r--r--content/renderer/media/media_stream_dispatcher.cc138
-rw-r--r--content/renderer/media/media_stream_dispatcher.h52
-rw-r--r--content/renderer/media/media_stream_dispatcher_unittest.cc37
-rw-r--r--content/renderer/pepper/pepper_device_enumeration_event_handler.cc3
-rw-r--r--content/renderer/pepper/pepper_plugin_delegate_impl.cc13
-rw-r--r--content/renderer/pepper/pepper_plugin_delegate_impl.h1
-rw-r--r--webkit/plugins/ppapi/mock_plugin_delegate.cc3
-rw-r--r--webkit/plugins/ppapi/mock_plugin_delegate.h1
-rw-r--r--webkit/plugins/ppapi/plugin_delegate.h3
-rw-r--r--webkit/plugins/ppapi/ppb_audio_input_impl.cc4
-rw-r--r--webkit/plugins/ppapi/ppb_video_capture_impl.cc4
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);
}