Index: content/renderer/media/video_capture_impl_manager.cc |
diff --git a/content/renderer/media/video_capture_impl_manager.cc b/content/renderer/media/video_capture_impl_manager.cc |
index 1c052d70415517084a8a689cf3e868421d39d8fe..39ba85a0dcd0628402e28242b8c3c4680374eff0 100644 |
--- a/content/renderer/media/video_capture_impl_manager.cc |
+++ b/content/renderer/media/video_capture_impl_manager.cc |
@@ -24,6 +24,8 @@ |
#include "content/renderer/media/video_capture_impl_manager.h" |
+#include <algorithm> |
+ |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
#include "base/location.h" |
@@ -35,10 +37,32 @@ |
namespace content { |
+struct VideoCaptureImplManager::DeviceEntry { |
+ media::VideoCaptureSessionId session_id; |
+ |
+ // To be used and destroyed only on the IO thread. |
+ std::unique_ptr<VideoCaptureImpl> impl; |
+ |
+ // Number of clients using |impl|. |
+ int client_count; |
+ |
+ // This is set to true if this device is being suspended, via |
+ // VideoCaptureImplManager::Suspend(). |
+ // See also: VideoCaptureImplManager::is_suspending_all_. |
+ bool is_individually_suspended; |
+ |
+ DeviceEntry() |
+ : session_id(0), client_count(0), is_individually_suspended(false) {} |
+ DeviceEntry(DeviceEntry&& other) = default; |
+ DeviceEntry& operator=(DeviceEntry&& other) = default; |
+ ~DeviceEntry() = default; |
+}; |
+ |
VideoCaptureImplManager::VideoCaptureImplManager() |
: next_client_id_(0), |
filter_(new VideoCaptureMessageFilter()), |
render_main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ is_suspending_all_(false), |
weak_factory_(this) {} |
VideoCaptureImplManager::~VideoCaptureImplManager() { |
@@ -46,12 +70,12 @@ VideoCaptureImplManager::~VideoCaptureImplManager() { |
if (devices_.empty()) |
return; |
// Forcibly release all video capture resources. |
- for (const auto& device : devices_) { |
- VideoCaptureImpl* const impl = device.second.second; |
+ for (auto& entry : devices_) { |
ChildProcess::current()->io_task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&VideoCaptureImpl::DeInit, base::Unretained(impl))); |
- ChildProcess::current()->io_task_runner()->DeleteSoon(FROM_HERE, impl); |
+ FROM_HERE, base::Bind(&VideoCaptureImpl::DeInit, |
+ base::Unretained(entry.impl.get()))); |
+ ChildProcess::current()->io_task_runner()->DeleteSoon( |
+ FROM_HERE, entry.impl.release()); |
} |
devices_.clear(); |
} |
@@ -59,18 +83,31 @@ VideoCaptureImplManager::~VideoCaptureImplManager() { |
base::Closure VideoCaptureImplManager::UseDevice( |
media::VideoCaptureSessionId id) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- VideoCaptureImpl* impl = NULL; |
- const VideoCaptureDeviceMap::iterator it = devices_.find(id); |
+ auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
if (it == devices_.end()) { |
- impl = CreateVideoCaptureImplForTesting(id, filter_.get()); |
- if (!impl) |
- impl = new VideoCaptureImpl(id, filter_.get()); |
- devices_[id] = std::make_pair(1, impl); |
+ devices_.push_back(DeviceEntry()); |
+ it = devices_.end() - 1; |
+ it->session_id = id; |
+ it->impl = CreateVideoCaptureImplForTesting(id, filter_.get()); |
+ if (!it->impl) |
+ it->impl.reset(new VideoCaptureImpl(id, filter_.get())); |
ChildProcess::current()->io_task_runner()->PostTask( |
- FROM_HERE, base::Bind(&VideoCaptureImpl::Init, base::Unretained(impl))); |
- } else { |
- ++it->second.first; |
+ FROM_HERE, base::Bind(&VideoCaptureImpl::Init, |
+ base::Unretained(it->impl.get()))); |
} |
+ ++it->client_count; |
+ |
+ // Design limit: When there are multiple clients, VideoCaptureImplManager |
+ // would have to individually track which ones requested suspending/resuming, |
+ // in order to determine whether the whole device should be suspended. |
+ // Instead, handle the non-common use case of multiple clients by just |
+ // resuming the suspended device, and disable suspend functionality while |
+ // there are multiple clients. |
+ if (it->is_individually_suspended) |
+ Resume(id); |
+ |
return base::Bind(&VideoCaptureImplManager::UnrefDevice, |
weak_factory_.GetWeakPtr(), id); |
} |
@@ -81,17 +118,19 @@ base::Closure VideoCaptureImplManager::StartCapture( |
const VideoCaptureStateUpdateCB& state_update_cb, |
const VideoCaptureDeliverFrameCB& deliver_frame_cb) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
DCHECK(it != devices_.end()); |
- VideoCaptureImpl* const impl = it->second.second; |
// This ID is used to identify a client of VideoCaptureImpl. |
const int client_id = ++next_client_id_; |
ChildProcess::current()->io_task_runner()->PostTask( |
FROM_HERE, |
- base::Bind(&VideoCaptureImpl::StartCapture, base::Unretained(impl), |
- client_id, params, state_update_cb, deliver_frame_cb)); |
+ base::Bind(&VideoCaptureImpl::StartCapture, |
+ base::Unretained(it->impl.get()), client_id, params, |
+ state_update_cb, deliver_frame_cb)); |
return base::Bind(&VideoCaptureImplManager::StopCapture, |
weak_factory_.GetWeakPtr(), client_id, id); |
} |
@@ -99,83 +138,125 @@ base::Closure VideoCaptureImplManager::StartCapture( |
void VideoCaptureImplManager::RequestRefreshFrame( |
media::VideoCaptureSessionId id) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
DCHECK(it != devices_.end()); |
- VideoCaptureImpl* const impl = it->second.second; |
ChildProcess::current()->io_task_runner()->PostTask( |
FROM_HERE, |
base::Bind(&VideoCaptureImpl::RequestRefreshFrame, |
- base::Unretained(impl))); |
+ base::Unretained(it->impl.get()))); |
+} |
+ |
+void VideoCaptureImplManager::Suspend(media::VideoCaptureSessionId id) { |
+ DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
+ DCHECK(it != devices_.end()); |
+ if (it->is_individually_suspended) |
+ return; // Device has already been individually suspended. |
+ if (it->client_count > 1) |
+ return; // Punt when there is >1 client (see comments in UseDevice()). |
+ it->is_individually_suspended = true; |
+ if (is_suspending_all_) |
+ return; // Device should already be suspended. |
+ ChildProcess::current()->io_task_runner()->PostTask( |
+ FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture, |
+ base::Unretained(it->impl.get()), true)); |
+} |
+ |
+void VideoCaptureImplManager::Resume(media::VideoCaptureSessionId id) { |
+ DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
+ DCHECK(it != devices_.end()); |
+ if (!it->is_individually_suspended) |
+ return; // Device was not individually suspended. |
+ it->is_individually_suspended = false; |
+ if (is_suspending_all_) |
+ return; // Device must remain suspended until all are resumed. |
+ ChildProcess::current()->io_task_runner()->PostTask( |
+ FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture, |
+ base::Unretained(it->impl.get()), false)); |
} |
void VideoCaptureImplManager::GetDeviceSupportedFormats( |
media::VideoCaptureSessionId id, |
const VideoCaptureDeviceFormatsCB& callback) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
DCHECK(it != devices_.end()); |
- VideoCaptureImpl* const impl = it->second.second; |
ChildProcess::current()->io_task_runner()->PostTask( |
FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats, |
- base::Unretained(impl), callback)); |
+ base::Unretained(it->impl.get()), callback)); |
} |
void VideoCaptureImplManager::GetDeviceFormatsInUse( |
media::VideoCaptureSessionId id, |
const VideoCaptureDeviceFormatsCB& callback) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
DCHECK(it != devices_.end()); |
- VideoCaptureImpl* const impl = it->second.second; |
ChildProcess::current()->io_task_runner()->PostTask( |
FROM_HERE, base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse, |
- base::Unretained(impl), callback)); |
+ base::Unretained(it->impl.get()), callback)); |
} |
-VideoCaptureImpl* |
+std::unique_ptr<VideoCaptureImpl> |
VideoCaptureImplManager::CreateVideoCaptureImplForTesting( |
media::VideoCaptureSessionId id, |
VideoCaptureMessageFilter* filter) const { |
- return NULL; |
+ return std::unique_ptr<VideoCaptureImpl>(); |
} |
void VideoCaptureImplManager::StopCapture(int client_id, |
media::VideoCaptureSessionId id) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- const VideoCaptureDeviceMap::const_iterator it = devices_.find(id); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
DCHECK(it != devices_.end()); |
- VideoCaptureImpl* const impl = it->second.second; |
ChildProcess::current()->io_task_runner()->PostTask( |
FROM_HERE, base::Bind(&VideoCaptureImpl::StopCapture, |
- base::Unretained(impl), client_id)); |
+ base::Unretained(it->impl.get()), client_id)); |
} |
void VideoCaptureImplManager::UnrefDevice( |
media::VideoCaptureSessionId id) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- const VideoCaptureDeviceMap::iterator it = devices_.find(id); |
+ const auto it = std::find_if( |
+ devices_.begin(), devices_.end(), |
+ [id] (const DeviceEntry& entry) { return entry.session_id == id; }); |
DCHECK(it != devices_.end()); |
- VideoCaptureImpl* const impl = it->second.second; |
- |
- // Unref and destroy on the IO thread if there's no more client. |
- DCHECK(it->second.first); |
- --it->second.first; |
- if (!it->second.first) { |
- devices_.erase(id); |
- ChildProcess::current()->io_task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&VideoCaptureImpl::DeInit, base::Unretained(impl))); |
- ChildProcess::current()->io_task_runner()->DeleteSoon(FROM_HERE, impl); |
- } |
+ DCHECK_GT(it->client_count, 0); |
+ --it->client_count; |
+ if (it->client_count > 0) |
+ return; |
+ ChildProcess::current()->io_task_runner()->PostTask( |
+ FROM_HERE, base::Bind(&VideoCaptureImpl::DeInit, |
+ base::Unretained(it->impl.get()))); |
+ ChildProcess::current()->io_task_runner()->DeleteSoon( |
+ FROM_HERE, it->impl.release()); |
+ devices_.erase(it); |
} |
void VideoCaptureImplManager::SuspendDevices(bool suspend) { |
DCHECK(render_main_task_runner_->BelongsToCurrentThread()); |
- for (const auto& device : devices_) { |
- VideoCaptureImpl* const impl = device.second.second; |
+ if (is_suspending_all_ == suspend) |
+ return; |
+ is_suspending_all_ = suspend; |
+ for (auto& entry : devices_) { |
+ if (entry.is_individually_suspended) |
+ continue; // Either: 1) Already suspended; or 2) Should not be resumed. |
ChildProcess::current()->io_task_runner()->PostTask( |
FROM_HERE, base::Bind(&VideoCaptureImpl::SuspendCapture, |
- base::Unretained(impl), suspend)); |
+ base::Unretained(entry.impl.get()), suspend)); |
} |
} |