blob: 686e6ad74c1a0746d5aed659680b0a3ac1de4c46 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Implementation notes about interactions with VideoCaptureImpl.
//
// How is VideoCaptureImpl used:
//
// VideoCaptureImpl is an IO thread object while VideoCaptureImplManager
// lives only on the render thread. It is only possible to access an
// object of VideoCaptureImpl via a task on the IO thread.
//
// How is VideoCaptureImpl deleted:
//
// A task is posted to the IO thread to delete a VideoCaptureImpl.
// Immediately after that the pointer to it is dropped. This means no
// access to this VideoCaptureImpl object is possible on the render
// thread. Also note that VideoCaptureImpl does not post task to itself.
//
// The use of Unretained:
//
// We make sure deletion is the last task on the IO thread for a
// VideoCaptureImpl object. This allows the use of Unretained() binding.
#include "content/renderer/media/video_capture_impl_manager.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "content/child/child_process.h"
#include "content/renderer/media/video_capture_impl.h"
#include "content/renderer/media/video_capture_message_filter.h"
#include "media/base/bind_to_current_loop.h"
namespace content {
VideoCaptureImplManager::VideoCaptureImplManager()
: next_client_id_(0),
filter_(new VideoCaptureMessageFilter()),
weak_factory_(this) {
}
VideoCaptureImplManager::~VideoCaptureImplManager() {
DCHECK(thread_checker_.CalledOnValidThread());
if (devices_.empty())
return;
// Forcibly release all video capture resources.
for (VideoCaptureDeviceMap::iterator it = devices_.begin();
it != devices_.end(); ++it) {
VideoCaptureImpl* impl = it->second.second;
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::DeInit,
base::Unretained(impl)));
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&base::DeletePointer<VideoCaptureImpl>,
base::Unretained(impl)));
}
devices_.clear();
}
base::Closure VideoCaptureImplManager::UseDevice(
media::VideoCaptureSessionId id) {
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureImpl* impl = NULL;
VideoCaptureDeviceMap::iterator it = devices_.find(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);
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::Init,
base::Unretained(impl)));
} else {
++it->second.first;
}
return base::Bind(&VideoCaptureImplManager::UnrefDevice,
weak_factory_.GetWeakPtr(), id);
}
base::Closure VideoCaptureImplManager::StartCapture(
media::VideoCaptureSessionId id,
const media::VideoCaptureParams& params,
const VideoCaptureStateUpdateCB& state_update_cb,
const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureDeviceMap::iterator it = devices_.find(id);
DCHECK(it != devices_.end());
VideoCaptureImpl* impl = it->second.second;
// This ID is used to identify a client of VideoCaptureImpl.
const int client_id = ++next_client_id_;
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::StartCapture,
base::Unretained(impl),
client_id,
params,
state_update_cb,
deliver_frame_cb));
return base::Bind(&VideoCaptureImplManager::StopCapture,
weak_factory_.GetWeakPtr(),
client_id, id);
}
void VideoCaptureImplManager::GetDeviceSupportedFormats(
media::VideoCaptureSessionId id,
const VideoCaptureDeviceFormatsCB& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureDeviceMap::iterator it = devices_.find(id);
DCHECK(it != devices_.end());
VideoCaptureImpl* impl = it->second.second;
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats,
base::Unretained(impl), callback));
}
void VideoCaptureImplManager::GetDeviceFormatsInUse(
media::VideoCaptureSessionId id,
const VideoCaptureDeviceFormatsCB& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureDeviceMap::iterator it = devices_.find(id);
DCHECK(it != devices_.end());
VideoCaptureImpl* impl = it->second.second;
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse,
base::Unretained(impl), callback));
}
VideoCaptureImpl*
VideoCaptureImplManager::CreateVideoCaptureImplForTesting(
media::VideoCaptureSessionId id,
VideoCaptureMessageFilter* filter) const {
return NULL;
}
void VideoCaptureImplManager::StopCapture(
int client_id, media::VideoCaptureSessionId id) {
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureDeviceMap::iterator it = devices_.find(id);
DCHECK(it != devices_.end());
VideoCaptureImpl* impl = it->second.second;
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::StopCapture,
base::Unretained(impl), client_id));
}
void VideoCaptureImplManager::UnrefDevice(
media::VideoCaptureSessionId id) {
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureDeviceMap::iterator it = devices_.find(id);
DCHECK(it != devices_.end());
VideoCaptureImpl* 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_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::DeInit,
base::Unretained(impl)));
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&base::DeletePointer<VideoCaptureImpl>,
base::Unretained(impl)));
}
}
void VideoCaptureImplManager::SuspendDevices(bool suspend) {
DCHECK(thread_checker_.CalledOnValidThread());
for (VideoCaptureDeviceMap::iterator it = devices_.begin();
it != devices_.end(); ++it) {
VideoCaptureImpl* impl = it->second.second;
ChildProcess::current()->io_message_loop_proxy()->PostTask(
FROM_HERE,
base::Bind(&VideoCaptureImpl::SuspendCapture,
base::Unretained(impl), suspend));
}
}
} // namespace content