Index: content/browser/renderer_host/media/video_capture_manager.cc |
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc |
index 617a8696deeacb2c2f8d0ef5e8ceb1b24416b5f7..03f0967a98a9958400f0c44bf8263f3c9847e778 100644 |
--- a/content/browser/renderer_host/media/video_capture_manager.cc |
+++ b/content/browser/renderer_host/media/video_capture_manager.cc |
@@ -4,6 +4,7 @@ |
#include "content/browser/renderer_host/media/video_capture_manager.h" |
+#include <algorithm> |
#include <set> |
#include "base/bind.h" |
@@ -94,6 +95,9 @@ void LogVideoCaptureEvent(VideoCaptureEvent event) { |
NUM_VIDEO_CAPTURE_EVENT); |
} |
+// Counter used for identifying a DeviceRequest to start a capture device. |
+static int g_device_start_id = 0; |
+ |
} // namespace |
namespace content { |
@@ -102,12 +106,23 @@ VideoCaptureManager::DeviceEntry::DeviceEntry( |
MediaStreamType stream_type, |
const std::string& id, |
scoped_ptr<VideoCaptureController> controller) |
- : stream_type(stream_type), |
+ : serial_id(g_device_start_id++), |
+ stream_type(stream_type), |
id(id), |
video_capture_controller(controller.Pass()) {} |
VideoCaptureManager::DeviceEntry::~DeviceEntry() {} |
+VideoCaptureManager::CaptureDeviceStartRequest::CaptureDeviceStartRequest( |
+ int serial_id, |
+ media::VideoCaptureSessionId session_id, |
+ const media::VideoCaptureParams& params) |
+ : serial_id_(serial_id), |
+ session_id_(session_id), |
+ params_(params), |
+ abort_start_(false) { |
+} |
+ |
VideoCaptureManager::VideoCaptureManager( |
scoped_ptr<media::VideoCaptureDeviceFactory> factory) |
@@ -118,6 +133,7 @@ VideoCaptureManager::VideoCaptureManager( |
VideoCaptureManager::~VideoCaptureManager() { |
DCHECK(devices_.empty()); |
+ DCHECK(device_start_queue_.empty()); |
} |
void VideoCaptureManager::Register( |
@@ -216,22 +232,131 @@ void VideoCaptureManager::Close(int capture_session_id) { |
sessions_.erase(session_it); |
} |
-void VideoCaptureManager::DoStartDeviceOnDeviceThread( |
+void VideoCaptureManager::QueueStartDevice( |
media::VideoCaptureSessionId session_id, |
DeviceEntry* entry, |
+ const media::VideoCaptureParams& params) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::IO); |
+ device_start_queue_.push_back( |
+ CaptureDeviceStartRequest(entry->serial_id, session_id, params)); |
+ if (device_start_queue_.size() == 1) |
+ HandleQueuedStartRequest(); |
+} |
+ |
+void VideoCaptureManager::DoStopDevice(DeviceEntry* entry) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::IO); |
+ DCHECK(std::find(devices_.begin(), devices_.end(), entry) != devices_.end()); |
+ |
+ // Find the matching start request. |
+ for (DeviceStartQueue::reverse_iterator request = |
+ device_start_queue_.rbegin(); |
+ request != device_start_queue_.rend(); ++request) { |
+ if (request->serial_id() == entry->serial_id) { |
+ request->set_abort_start(); |
+ DVLOG(3) << "DoStopDevice, aborting start request for device " |
+ << entry->id << " serial_id = " << entry->serial_id; |
+ return; |
+ } |
+ } |
+ |
+ DVLOG(3) << "DoStopDevice. Send stop request for device = " << entry->id |
+ << " serial_id = " << entry->serial_id << "."; |
+ if (entry->video_capture_device.get()) { |
+ // |entry->video_capture_device| can be null if creating the device fails. |
+ device_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
+ base::Passed(&entry->video_capture_device))); |
+ } |
+} |
+ |
+void VideoCaptureManager::HandleQueuedStartRequest() { |
+ // Remove all start requests that have been aborted. |
+ while (device_start_queue_.begin() != device_start_queue_.end() && |
+ device_start_queue_.begin()->abort_start()) { |
+ device_start_queue_.pop_front(); |
+ } |
+ DeviceStartQueue::iterator request = device_start_queue_.begin(); |
+ if (request == device_start_queue_.end()) |
+ return; |
+ |
+ const int serial_id = request->serial_id(); |
+ DeviceEntries::iterator entry_it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [serial_id] (const DeviceEntry* e) { |
+ return e->serial_id == serial_id; |
+ }); |
+ DCHECK(entry_it != devices_.end()); |
+ DeviceEntry* entry = (*entry_it); |
+ |
+ DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = " |
+ << entry->id << " start id = " << entry->serial_id; |
+ base::PostTaskAndReplyWithResult( |
+ device_task_runner_.get(), |
+ FROM_HERE, |
+ base::Bind( |
+ &VideoCaptureManager::DoStartDeviceOnDeviceThread, |
+ this, |
+ request->session_id(), |
+ entry->id, |
+ entry->stream_type, |
+ request->params(), |
+ base::Passed(entry->video_capture_controller->NewDeviceClient())), |
+ base::Bind(&VideoCaptureManager::OnDeviceStarted, this, |
+ request->serial_id())); |
+} |
+ |
+void VideoCaptureManager::OnDeviceStarted( |
+ int serial_id, |
+ scoped_ptr<media::VideoCaptureDevice> device) { |
+ DCHECK_CURRENTLY_ON(BrowserThread::IO); |
+ DCHECK(serial_id == device_start_queue_.begin()->serial_id()); |
+ DVLOG(3) << "OnDeviceStarted"; |
+ if (device_start_queue_.front().abort_start()) { |
+ // |device| can be null if creation failed in DoStartDeviceOnDeviceThread. |
+ // The device is no longer wanted. Stop the device again. |
+ DVLOG(3) << "OnDeviceStarted but start request have been aborted."; |
+ media::VideoCaptureDevice* device_ptr = device.get(); |
+ base::Closure closure = |
+ base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
+ base::Passed(&device)); |
+ if (device_ptr && !device_task_runner_->PostTask(FROM_HERE, closure)) { |
+ // PostTask failed. The device must be stopped anyway. |
+ device_ptr->StopAndDeAllocate(); |
+ } |
+ } else { |
+ DeviceEntries::iterator entry_it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [serial_id] (const DeviceEntry* e) { |
+ return e->serial_id == serial_id; |
+ }); |
+ DCHECK(entry_it != devices_.end()); |
+ DCHECK(!(*entry_it)->video_capture_device.get()); |
+ (*entry_it)->video_capture_device.swap(device); |
+ } |
+ |
+ device_start_queue_.pop_front(); |
+ HandleQueuedStartRequest(); |
+} |
+ |
+scoped_ptr<media::VideoCaptureDevice> |
+VideoCaptureManager::DoStartDeviceOnDeviceThread( |
+ media::VideoCaptureSessionId session_id, |
+ const std::string& id, |
+ MediaStreamType stream_type, |
const media::VideoCaptureParams& params, |
scoped_ptr<media::VideoCaptureDevice::Client> device_client) { |
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); |
DCHECK(IsOnDeviceThread()); |
scoped_ptr<media::VideoCaptureDevice> video_capture_device; |
- switch (entry->stream_type) { |
+ switch (stream_type) { |
case MEDIA_DEVICE_VIDEO_CAPTURE: { |
// We look up the device id from the renderer in our local enumeration |
// since the renderer does not have all the information that might be |
// held in the browser-side VideoCaptureDevice::Name structure. |
media::VideoCaptureDeviceInfo* found = |
- FindDeviceInfoById(entry->id, devices_info_cache_); |
+ FindDeviceInfoById(id, devices_info_cache_); |
if (found) { |
video_capture_device = |
video_capture_device_factory_->Create(found->name); |
@@ -240,20 +365,21 @@ void VideoCaptureManager::DoStartDeviceOnDeviceThread( |
} |
case MEDIA_TAB_VIDEO_CAPTURE: { |
video_capture_device.reset( |
- WebContentsVideoCaptureDevice::Create(entry->id)); |
+ WebContentsVideoCaptureDevice::Create(id)); |
break; |
} |
case MEDIA_DESKTOP_VIDEO_CAPTURE: { |
#if defined(ENABLE_SCREEN_CAPTURE) |
- DesktopMediaID id = DesktopMediaID::Parse(entry->id); |
+ DesktopMediaID desktop_id = DesktopMediaID::Parse(id); |
#if defined(USE_AURA) |
- if (id.type == DesktopMediaID::TYPE_AURA_WINDOW) { |
- video_capture_device.reset(DesktopCaptureDeviceAura::Create(id)); |
+ if (desktop_id.type == DesktopMediaID::TYPE_AURA_WINDOW) { |
+ video_capture_device.reset( |
+ DesktopCaptureDeviceAura::Create(desktop_id)); |
} else |
#endif |
- if (id.type != DesktopMediaID::TYPE_NONE && |
- id.type != DesktopMediaID::TYPE_AURA_WINDOW) { |
- video_capture_device = DesktopCaptureDevice::Create(id); |
+ if (desktop_id.type != DesktopMediaID::TYPE_NONE && |
+ desktop_id.type != DesktopMediaID::TYPE_AURA_WINDOW) { |
+ video_capture_device = DesktopCaptureDevice::Create(desktop_id); |
if (notification_window_ids_.find(session_id) != |
notification_window_ids_.end()) { |
static_cast<DesktopCaptureDevice*>(video_capture_device.get()) |
@@ -273,11 +399,11 @@ void VideoCaptureManager::DoStartDeviceOnDeviceThread( |
if (!video_capture_device) { |
device_client->OnError("Could not create capture device"); |
- return; |
+ return nullptr; |
} |
video_capture_device->AllocateAndStart(params, device_client.Pass()); |
- entry->video_capture_device = video_capture_device.Pass(); |
+ return video_capture_device.Pass(); |
} |
void VideoCaptureManager::StartCaptureForClient( |
@@ -306,16 +432,7 @@ void VideoCaptureManager::StartCaptureForClient( |
if (entry->video_capture_controller->GetActiveClientCount() == 0) { |
DVLOG(1) << "VideoCaptureManager starting device (type = " |
<< entry->stream_type << ", id = " << entry->id << ")"; |
- |
- device_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind( |
- &VideoCaptureManager::DoStartDeviceOnDeviceThread, |
- this, |
- session_id, |
- entry, |
- params, |
- base::Passed(entry->video_capture_controller->NewDeviceClient()))); |
+ QueueStartDevice(session_id, entry, params); |
} |
// Run the callback first, as AddClient() may trigger OnFrameInfo(). |
done_cb.Run(entry->video_capture_controller->GetWeakPtr()); |
@@ -392,10 +509,7 @@ void VideoCaptureManager::PauseCaptureForClient( |
return; |
// There is no more client, release the camera. |
- device_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
- base::Unretained(entry))); |
+ DoStopDevice(entry); |
} |
void VideoCaptureManager::ResumeCaptureForClient( |
@@ -423,15 +537,7 @@ void VideoCaptureManager::ResumeCaptureForClient( |
return; |
// This is first active client, allocate the camera. |
- device_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind( |
- &VideoCaptureManager::DoStartDeviceOnDeviceThread, |
- this, |
- session_id, |
- entry, |
- params, |
- base::Passed(entry->video_capture_controller->NewDeviceClient()))); |
+ QueueStartDevice(session_id, entry, params); |
} |
bool VideoCaptureManager::GetDeviceSupportedFormats( |
@@ -517,13 +623,12 @@ void VideoCaptureManager::SetDesktopCaptureWindowId( |
window_id)); |
} |
-void VideoCaptureManager::DoStopDeviceOnDeviceThread(DeviceEntry* entry) { |
+void VideoCaptureManager::DoStopDeviceOnDeviceThread( |
+ scoped_ptr<media::VideoCaptureDevice> device) { |
SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StopDeviceTime"); |
DCHECK(IsOnDeviceThread()); |
- if (entry->video_capture_device) { |
- entry->video_capture_device->StopAndDeAllocate(); |
- } |
- entry->video_capture_device.reset(); |
+ device->StopAndDeAllocate(); |
+ DVLOG(3) << "DoStopDeviceOnDeviceThread"; |
} |
void VideoCaptureManager::OnOpened( |
@@ -653,12 +758,11 @@ void VideoCaptureManager::DestroyDeviceEntryIfNoClients(DeviceEntry* entry) { |
// deleted immediately, and the device is freed asynchronously. After this |
// point, subsequent requests to open this same device ID will create a new |
// DeviceEntry, VideoCaptureController, and VideoCaptureDevice. |
- devices_.erase(entry); |
- entry->video_capture_controller.reset(); |
- device_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&VideoCaptureManager::DoStopDeviceOnDeviceThread, this, |
- base::Owned(entry))); |
+ DoStopDevice(entry); |
+ DeviceEntries::iterator device_it = std::find(devices_.begin(), |
+ devices_.end(), |
+ entry); |
+ devices_.erase(device_it); |
} |
} |
@@ -688,7 +792,7 @@ VideoCaptureManager::DeviceEntry* VideoCaptureManager::GetOrCreateDeviceEntry( |
DeviceEntry* new_device = new DeviceEntry(device_info.type, |
device_info.id, |
video_capture_controller.Pass()); |
- devices_.insert(new_device); |
+ devices_.push_back(new_device); |
return new_device; |
} |