Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1379)

Unified Diff: content/renderer/media/video_capture_impl.cc

Issue 6902166: Add VideoCaptureImpl (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: coding style Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/media/video_capture_impl.cc
===================================================================
--- content/renderer/media/video_capture_impl.cc (revision 0)
+++ content/renderer/media/video_capture_impl.cc (revision 0)
@@ -0,0 +1,444 @@
+// Copyright (c) 2011 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.
+
+#include "content/renderer/media/video_capture_impl.h"
+
+#include "base/memory/singleton.h"
+#include "content/common/child_process.h"
+#include "content/common/child_thread.h"
+#include "content/common/video_capture_messages.h"
+#include "content/common/view_messages.h"
+#include "media/base/message_loop_factory_impl.h"
+
+namespace {
+
+// VideoCaptureMessageFilterCreator is to be used as a singleton so we can get
+// access to a shared VideoCaptureMessageFilter.
+// Example usage:
+// VideoCaptureMessageFilter* filter =
+// VideoCaptureMessageFilterCreator::SharedFilter();
+
+class VideoCaptureMessageFilterCreator {
+ public:
+ VideoCaptureMessageFilterCreator() {
+ int routing_id;
+ ChildThread::current()->Send(
+ new ViewHostMsg_GenerateRoutingID(&routing_id));
+ filter_ = new VideoCaptureMessageFilter(routing_id);
+ filter_->AddFilter();
+ }
+
+ static VideoCaptureMessageFilter* SharedFilter() {
+ return GetInstance()->filter_.get();
+ }
+
+ static VideoCaptureMessageFilterCreator* GetInstance() {
+ return Singleton<VideoCaptureMessageFilterCreator>::get();
+ }
+
+ private:
+ scoped_refptr<VideoCaptureMessageFilter> filter_;
+};
+
+} // namespace
+
+VideoCaptureImplManager::VideoCaptureImplManager() {
+ ml_factory_.reset(new media::MessageLoopFactoryImpl());
+ ml_proxy_ = ml_factory_->GetMessageLoopProxy("VC manager");
+}
+
+// static
+media::VideoCapture* VideoCaptureImplManager::AddDevice(
+ media::VideoCaptureSessionId id,
+ media::VideoCapture::EventHandler* handler) {
+ DCHECK(handler);
+ VideoCaptureImplManager* manager = GetInstance();
+
+ base::AutoLock auto_lock(manager->lock_);
+ Devices::iterator it = manager->devices_.find(id);
+ if (it == manager->devices_.end()) {
+ VideoCaptureImpl* vc =
+ new VideoCaptureImpl(id, manager->ml_proxy_,
+ VideoCaptureMessageFilterCreator::SharedFilter());
+ if (vc) {
scherkus (not reviewing) 2011/05/14 23:33:42 not sure how valuable this check is chrome regist
wjia(left Chromium) 2011/05/17 04:00:25 removed the checking. What if some module has a ba
+ manager->devices_[id] = Device(vc, handler);
+ vc->Init();
+ }
+ return vc;
+ }
+
+ manager->devices_[id].clients.push_front(handler);
+ return it->second.vc;
+}
+
+// static
+void VideoCaptureImplManager::RemoveDevice(
+ media::VideoCaptureSessionId id,
+ media::VideoCapture::EventHandler* handler) {
+ DCHECK(handler);
+ VideoCaptureImplManager* manager = GetInstance();
+
+ base::AutoLock auto_lock(manager->lock_);
+ Devices::iterator it = manager->devices_.find(id);
+ if (it == manager->devices_.end())
scherkus (not reviewing) 2011/05/14 23:33:42 is it valid to call RemoveDevice w/ invalid id? i
wjia(left Chromium) 2011/05/17 04:00:25 The idea is to allow anyone to call RemoveDevice w
+ return;
+
+ size_t size = it->second.clients.size();
+ it->second.clients.remove(handler);
+
+ if (size == it->second.clients.size() || size > 1)
scherkus (not reviewing) 2011/05/14 23:33:42 is it valid to remove pass in an invalid handler p
wjia(left Chromium) 2011/05/17 04:00:25 same explanation as above.
+ return;
+
+ manager->devices_[id].vc->DeInit(NewRunnableMethod(manager,
+ &VideoCaptureImplManager::FreeDevice, manager->devices_[id].vc));
scherkus (not reviewing) 2011/05/14 23:33:42 are you transferring ownership of vc to this task
wjia(left Chromium) 2011/05/17 04:00:25 yes, because |vc| has to be removed from MessageFi
+ manager->devices_.erase(id);
+ return;
+}
+
+// static
+VideoCaptureImplManager* VideoCaptureImplManager::GetInstance() {
+ return Singleton<VideoCaptureImplManager>::get();
+}
+
+void VideoCaptureImplManager::FreeDevice(VideoCaptureImpl* vc) {
+ delete vc;
scherkus (not reviewing) 2011/05/14 23:33:42 looks like this method only gets called on the IO
wjia(left Chromium) 2011/05/17 04:00:25 VideoCaptureImplManager::AddDevice() can be called
+}
+
+VideoCaptureImpl::VideoCaptureImpl(
+ const media::VideoCaptureSessionId id,
+ scoped_refptr<base::MessageLoopProxy> ml_proxy,
+ VideoCaptureMessageFilter* filter)
+ : VideoCapture(),
+ filter_(filter),
+ session_id_(id),
+ ml_proxy_(ml_proxy),
+ device_id_(0),
+ width_(0),
+ height_(0),
+ frame_rate_(0),
+ video_type_(media::VideoFrame::I420),
scherkus (not reviewing) 2011/05/14 23:33:42 not initializing new_width_ / new_height_
wjia(left Chromium) 2011/05/17 04:00:25 Done.
+ state_(kStopped) {
+ DCHECK(filter);
+}
+
+VideoCaptureImpl::~VideoCaptureImpl() {
scherkus (not reviewing) 2011/05/14 23:33:42 nit: collapse {} onto one line if it's <80chars
wjia(left Chromium) 2011/05/17 04:00:25 Done.
+}
+
+void VideoCaptureImpl::Init() {
+ AddDelegateOnIOThread();
+}
+
+void VideoCaptureImpl::DeInit(Task* task) {
+ StopDevice();
scherkus (not reviewing) 2011/05/14 23:33:42 hrmmmm... so again I'm not 100% on which thread is
wjia(left Chromium) 2011/05/17 04:00:25 You are right. Thanks for pointing this out. I nee
+ RemoveDelegateOnIOThread(task);
+}
+
+void VideoCaptureImpl::StartCapture(
+ media::VideoCapture::EventHandler* handler,
+ const VideoCaptureCapability& capability) {
+ DCHECK_EQ(capability.raw_type, media::VideoFrame::I420);
+
+ if (!ml_proxy_->BelongsToCurrentThread()) {
+ ml_proxy_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::StartCapture, handler,
+ capability));
+ return;
+ }
+
+ if (capability.resolution_fixed && master_clients_.size() &&
+ (capability.width != width_ || capability.height != height_)) {
+ // Can't have 2 master clients with different resolutions.
+ handler->OnError(this, 1);
+ return;
+ }
+
+ clients_[handler] = capability;
+ if (capability.resolution_fixed) {
+ master_clients_.push_back(handler);
+ if (master_clients_.size() > 1)
+ return;
+ }
+
+ if (state_ == kStarted) {
+ // Take the resolution of master client.
+ if (capability.resolution_fixed &&
+ (capability.width != width_ || capability.height != height_)) {
+ new_width_ = capability.width;
+ new_height_ = capability.height;
+ DLOG(INFO) << "StartCapture: Got master client with new resolution "
+ "during started, try to restart.";
+ StopDevice();
+ }
+ handler->OnStarted(this);
+ return;
+ }
+
+ if (state_ == kStopping) {
+ if (capability.resolution_fixed || !pending_start()) {
+ new_width_ = capability.width;
+ new_height_ = capability.height;
+ DLOG(INFO) << "StartCapture: Got new resolution, already in stopping.";
+ }
+ handler->OnStarted(this);
+ return;
+ }
+
+ DCHECK_EQ(clients_.size(), 1ul);
+ video_type_ = capability.raw_type;
+ new_width_ = 0;
+ new_height_ = 0;
+ width_ = capability.width;
+ height_ = capability.height;
+
+ StartCaptureInternal();
+}
+
+void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) {
+ if (!ml_proxy_->BelongsToCurrentThread()) {
+ ml_proxy_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::StopCapture, handler));
+ return;
+ }
+
+ if (clients_.find(handler) == clients_.end())
+ return;
+
+ handler->OnStopped(this);
+ clients_.erase(handler);
+ master_clients_.remove(handler);
+
+ // Still have at least one master client.
+ if (master_clients_.size() > 0)
+ return;
+
+ // TODO(wjia): Is it really needed to handle resolution change for non-master
+ // clients, except no client case?
+ if (clients_.size() > 0) {
+ DLOG(INFO) << "StopCapture: No master client.";
+ int maxw = 0;
+ int maxh = 0;
+ for (ClientInfo::iterator it = clients_.begin();
+ it != clients_.end(); it++) {
+ if (it->second.width > maxw && it->second.height > maxh) {
+ maxw = it->second.width;
+ maxh = it->second.height;
+ }
+ }
+
+ if (state_ == kStarted) {
+ // Only handle resolution reduction.
+ if (maxw < width_ && maxh < height_) {
+ new_width_ = maxw;
+ new_height_ = maxh;
+ DLOG(INFO) << "StopCapture: New smaller resolution, stopping ...";
+ StopDevice();
+ }
+ return;
+ }
+
+ if (state_ == kStopping) {
+ new_width_ = maxw;
+ new_height_ = maxh;
+ DLOG(INFO) << "StopCapture: New resolution, during stopping.";
+ return;
+ }
+ } else {
+ new_width_ = width_ = 0;
+ new_height_ = height_ = 0;
+ DLOG(INFO) << "StopCapture: No more client, stopping ...";
+ StopDevice();
+ }
+}
+
+void VideoCaptureImpl::OnBufferReceived(TransportDIB::Handle handle,
+ base::Time timestamp) {
+ if (!ml_proxy_->BelongsToCurrentThread()) {
+ ml_proxy_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::OnBufferReceived,
+ handle, timestamp));
+ return;
+ }
+
+ if (state_ != kStarted) {
+ filter_->Send(
+ new VideoCaptureHostMsg_BufferReady(0, device_id_, handle));
+ return;
+ }
+
+ media::VideoCapture::VideoFrameBuffer* buffer;
+ CachedDIB::iterator it;
+ for (it = cached_dibs_.begin(); it != cached_dibs_.end(); it++) {
+ if ((*it)->dib->handle() == handle)
+ break;
+ }
+ if (it == cached_dibs_.end()) {
+ TransportDIB* dib = TransportDIB::Map(handle);
+ buffer = new VideoFrameBuffer;
scherkus (not reviewing) 2011/05/14 23:33:42 add ()
wjia(left Chromium) 2011/05/17 04:00:25 Done.
+ buffer->memory_pointer = dib->memory();
+ buffer->buffer_size = dib->size();
+ buffer->width = width_;
+ buffer->height = height_;
+
+ DIBBuffer* dib_buffer = new DIBBuffer(dib, buffer);
+ cached_dibs_.push_back(dib_buffer);
+ } else {
+ buffer = (*it)->mapped_memory;
+ }
+
+ // TODO(wjia): handle buffer sharing with downstream modules.
+ for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) {
+ it->first->OnBufferReady(this, buffer);
+ }
+
+ filter_->Send(
+ new VideoCaptureHostMsg_BufferReady(0, device_id_, handle));
+}
+
+void VideoCaptureImpl::OnStateChanged(
+ const media::VideoCapture::State& state) {
+ if (!ml_proxy_->BelongsToCurrentThread()) {
+ ml_proxy_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::OnStateChanged, state));
+ return;
+ }
+
+ switch (state) {
+ case media::VideoCapture::kStarted:
+ for (ClientInfo::iterator it = clients_.begin();
+ it != clients_.end(); it++) {
+ it->first->OnStarted(this);
+ }
+ break;
+ case media::VideoCapture::kStopped:
+ state_ = kStopped;
+ DLOG(INFO) << "OnStateChanged: stopped!, device_id = " << device_id_;
+ if (pending_start())
+ RestartCapture();
+ break;
+ case media::VideoCapture::kPaused:
+ for (ClientInfo::iterator it = clients_.begin();
+ it != clients_.end(); it++) {
+ it->first->OnPaused(this);
+ }
+ break;
+ case media::VideoCapture::kError:
+ for (ClientInfo::iterator it = clients_.begin();
+ it != clients_.end(); it++) {
+ // TODO(wjia): browser process would send error code.
+ it->first->OnError(this, 1);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void VideoCaptureImpl::OnDeviceInfoReceived(
+ const media::VideoCaptureParams& device_info) {
+ if (!ml_proxy_->BelongsToCurrentThread()) {
+ ml_proxy_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::OnDeviceInfoReceived,
+ device_info));
+ return;
+ }
+
+ for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) {
+ it->first->OnDeviceInfoReceived(this, device_info);
+ }
+}
+
+void VideoCaptureImpl::StopDevice() {
+ if (!ml_proxy_->BelongsToCurrentThread()) {
scherkus (not reviewing) 2011/05/14 23:33:42 the idea behind using the proxy is that you don't
+ ml_proxy_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::StopDevice));
+ return;
+ }
+
+ if (state_ == kStarted) {
+ state_ = kStopping;
+ filter_->Send(new VideoCaptureHostMsg_Stop(0, device_id_));
+ width_ = height_ = 0;
+ }
+}
+
+void VideoCaptureImpl::RestartCapture() {
+ DCHECK(ml_proxy_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, kStopped);
+
+ width_ = new_width_;
+ height_ = new_height_;
+ new_width_ = 0;
+ new_height_ = 0;
+
+ DLOG(INFO) << "RestartCapture, " << width_ << ", " << height_;
+ StartCaptureInternal();
+}
+
+void VideoCaptureImpl::StartCaptureInternal() {
+ DCHECK(ml_proxy_->BelongsToCurrentThread());
+
+ media::VideoCaptureParams params;
+ params.width = width_;
+ params.height = height_;
+ params.session_id = session_id_;
+
+ int try_times = 100;
+ bool result = false;
+ for (int i = 0; i < try_times; i++) {
scherkus (not reviewing) 2011/05/14 23:33:42 why do we have to try 100 times?
wjia(left Chromium) 2011/05/17 04:00:25 I just pick up this number for timeout. The reason
+ if (device_id_ &&
+ filter_->Send(new VideoCaptureHostMsg_Start(0, device_id_, params))) {
+ result = true;
+ break;
+ }
+
+ base::PlatformThread::Sleep(1000);
scherkus (not reviewing) 2011/05/14 23:33:42 ???
wjia(left Chromium) 2011/05/17 04:00:25 When either |channel_| or |device_id_| is not read
+ }
+
+ if (!result) {
+ state_ = kStopped;
+ width_ = height_ = 0;
+ for (ClientInfo::iterator it = clients_.begin(); it != clients_.end();
+ it++) {
+ it->first->OnError(this, 2);
+ }
+ } else {
+ state_ = kStarted;
+ for (ClientInfo::iterator it = clients_.begin(); it != clients_.end();
+ it++) {
+ it->first->OnStarted(this);
+ }
+ }
+}
+
+void VideoCaptureImpl::AddDelegateOnIOThread() {
+ DCHECK(ChildProcess::current()) << "Must be in the renderer";
+ base::MessageLoopProxy* message_loop_proxy =
+ ChildProcess::current()->io_message_loop_proxy();
+ DCHECK(message_loop_proxy);
+
+ if (!message_loop_proxy->BelongsToCurrentThread()) {
scherkus (not reviewing) 2011/05/14 23:33:42 considering you only have one caller of AddDelegat
wjia(left Chromium) 2011/05/17 04:00:25 Done.
+ message_loop_proxy->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::AddDelegateOnIOThread));
+ return;
+ }
+
+ device_id_ = filter_->AddDelegate(this);
scherkus (not reviewing) 2011/05/14 23:33:42 I think there's a race between StartCaptureInterna
wjia(left Chromium) 2011/05/17 04:00:25 The |device_id_| can be only modified on IO thread
+}
+
+void VideoCaptureImpl::RemoveDelegateOnIOThread(Task* task) {
+ DCHECK(ChildProcess::current()) << "Must be in the renderer";
+ base::MessageLoopProxy* message_loop_proxy =
+ ChildProcess::current()->io_message_loop_proxy();
+ DCHECK(message_loop_proxy);
+
+ if (!message_loop_proxy->BelongsToCurrentThread()) {
scherkus (not reviewing) 2011/05/14 23:33:42 ditto
wjia(left Chromium) 2011/05/17 04:00:25 Done.
+ message_loop_proxy->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &VideoCaptureImpl::RemoveDelegateOnIOThread,
+ task));
+ return;
+ }
+
+ filter_->RemoveDelegate(this);
+ media::AutoTaskRunner auto_runner(task);
+}
Property changes on: content/renderer/media/video_capture_impl.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« content/renderer/media/video_capture_impl.h ('K') | « content/renderer/media/video_capture_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698