| 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));
 | 
|    }
 | 
|  }
 | 
|  
 | 
| 
 |