diff options
author | vandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-26 05:33:40 +0000 |
---|---|---|
committer | vandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-26 05:33:40 +0000 |
commit | 6d8e27320c6e8d0f24df4d8be9480123b34d2ac4 (patch) | |
tree | 7452d135897cb2986e207d88c61f506bdaa50ff9 /content | |
parent | ad93c6baf3651dc050ab436bd0cfd84bdabf7127 (diff) | |
download | chromium_src-6d8e27320c6e8d0f24df4d8be9480123b34d2ac4.zip chromium_src-6d8e27320c6e8d0f24df4d8be9480123b34d2ac4.tar.gz chromium_src-6d8e27320c6e8d0f24df4d8be9480123b34d2ac4.tar.bz2 |
Undo mistaken revert.
Revert "Revert "Add VideoCaptureImpl and VideoCaptureImplManager""
This reverts commit r86770
BUG=NONE
TEST=NONE
TBR=wjia@google.com
Review URL: http://codereview.chromium.org/6962006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@86781 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/content_renderer.gypi | 4 | ||||
-rw-r--r-- | content/renderer/media/video_capture_impl.cc | 386 | ||||
-rw-r--r-- | content/renderer/media/video_capture_impl.h | 111 | ||||
-rw-r--r-- | content/renderer/media/video_capture_impl_manager.cc | 118 | ||||
-rw-r--r-- | content/renderer/media/video_capture_impl_manager.h | 68 | ||||
-rw-r--r-- | content/renderer/media/video_capture_impl_unittest.cc | 107 | ||||
-rw-r--r-- | content/renderer/video_capture_message_filter.cc | 24 | ||||
-rw-r--r-- | content/renderer/video_capture_message_filter.h | 11 | ||||
-rw-r--r-- | content/renderer/video_capture_message_filter_unittest.cc | 37 |
9 files changed, 849 insertions, 17 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index fc7e8683..b1d25d7 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -70,6 +70,10 @@ 'renderer/media/gles2_video_decode_context.h', 'renderer/media/ipc_video_decoder.cc', 'renderer/media/ipc_video_decoder.h', + 'renderer/media/video_capture_impl.cc', + 'renderer/media/video_capture_impl.h', + 'renderer/media/video_capture_impl_manager.cc', + 'renderer/media/video_capture_impl_manager.h', 'renderer/navigation_state.cc', 'renderer/navigation_state.h', 'renderer/notification_provider.cc', diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc new file mode 100644 index 0000000..faf4565 --- /dev/null +++ b/content/renderer/media/video_capture_impl.cc @@ -0,0 +1,386 @@ +// 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 "content/common/child_process.h" +#include "content/common/video_capture_messages.h" + +VideoCaptureImpl::DIBBuffer::DIBBuffer( + TransportDIB* d, media::VideoCapture::VideoFrameBuffer* ptr) + : dib(d), + mapped_memory(ptr) {} + +VideoCaptureImpl::DIBBuffer::~DIBBuffer() { + delete dib; +} + +bool VideoCaptureImpl::CaptureStarted() { + return state_ == kStarted; +} + +int VideoCaptureImpl::CaptureWidth() { + return width_; +} + +int VideoCaptureImpl::CaptureHeight() { + return height_; +} + +int VideoCaptureImpl::CaptureFrameRate() { + return frame_rate_; +} + +VideoCaptureImpl::VideoCaptureImpl( + const media::VideoCaptureSessionId id, + scoped_refptr<base::MessageLoopProxy> ml_proxy, + VideoCaptureMessageFilter* filter) + : VideoCapture(), + message_filter_(filter), + session_id_(id), + ml_proxy_(ml_proxy), + device_id_(0), + width_(0), + height_(0), + frame_rate_(0), + video_type_(media::VideoFrame::I420), + new_width_(0), + new_height_(0), + state_(kStopped) { + DCHECK(filter); +} + +VideoCaptureImpl::~VideoCaptureImpl() {} + +void VideoCaptureImpl::Init() { + base::MessageLoopProxy* io_message_loop_proxy = + ChildProcess::current()->io_message_loop_proxy(); + + if (!io_message_loop_proxy->BelongsToCurrentThread()) { + io_message_loop_proxy->PostTask(FROM_HERE, + NewRunnableMethod(this, &VideoCaptureImpl::AddDelegateOnIOThread)); + return; + } + + AddDelegateOnIOThread(); +} + +void VideoCaptureImpl::DeInit(Task* task) { + if (state_ == kStarted) + message_filter_->Send(new VideoCaptureHostMsg_Stop(0, device_id_)); + + base::MessageLoopProxy* io_message_loop_proxy = + ChildProcess::current()->io_message_loop_proxy(); + + if (!io_message_loop_proxy->BelongsToCurrentThread()) { + io_message_loop_proxy->PostTask(FROM_HERE, + NewRunnableMethod(this, &VideoCaptureImpl::RemoveDelegateOnIOThread, + task)); + return; + } + + 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; + } + + ClientInfo::iterator it = pending_clients_.find(handler); + + if (it != pending_clients_.end()) { + handler->OnError(this, 1); + return; + } + + if (!device_id_) { + pending_clients_[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; + } + + ClientInfo::iterator it = pending_clients_.find(handler); + if (it != pending_clients_.end()) { + handler->OnStopped(this); + pending_clients_.erase(it); + 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) { + message_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(); + 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); + } + + message_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::OnDelegateAdded(int32 device_id) { + if (!ml_proxy_->BelongsToCurrentThread()) { + ml_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &VideoCaptureImpl::OnDelegateAdded, device_id)); + return; + } + + device_id_ = device_id; + for (ClientInfo::iterator it = pending_clients_.begin(); + it != pending_clients_.end(); ) { + media::VideoCapture::EventHandler* handler = it->first; + const VideoCaptureCapability capability = it->second; + pending_clients_.erase(it++); + StartCapture(handler, capability); + } +} + +void VideoCaptureImpl::StopDevice() { + if (!ml_proxy_->BelongsToCurrentThread()) { + ml_proxy_->PostTask(FROM_HERE, + NewRunnableMethod(this, &VideoCaptureImpl::StopDevice)); + return; + } + + if (state_ == kStarted) { + state_ = kStopping; + message_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()); + DCHECK(device_id_); + + media::VideoCaptureParams params; + params.width = width_; + params.height = height_; + params.session_id = session_id_; + + message_filter_->Send(new VideoCaptureHostMsg_Start(0, device_id_, params)); + state_ = kStarted; + for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); it++) { + it->first->OnStarted(this); + } +} + +void VideoCaptureImpl::AddDelegateOnIOThread() { + message_filter_->AddDelegate(this); +} + +void VideoCaptureImpl::RemoveDelegateOnIOThread(Task* task) { + message_filter_->RemoveDelegate(this); + media::AutoTaskRunner auto_runner(task); +} diff --git a/content/renderer/media/video_capture_impl.h b/content/renderer/media/video_capture_impl.h new file mode 100644 index 0000000..4613e4b --- /dev/null +++ b/content/renderer/media/video_capture_impl.h @@ -0,0 +1,111 @@ +// 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. + +// VideoCaptureImpl represents a capture device in renderer process. It provides +// interfaces for clients to Start/Stop capture. It also communicates to clients +// when buffer is ready, state of capture device is changed. + +#ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_ +#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_ + +#include <list> +#include <map> + +#include "content/renderer/video_capture_message_filter.h" +#include "media/base/callback.h" +#include "media/video/capture/video_capture.h" +#include "ui/gfx/surface/transport_dib.h" + +namespace base { +class MessageLoopProxy; +} + +class VideoCaptureImpl + : public media::VideoCapture, + public VideoCaptureMessageFilter::Delegate { + public: + // media::VideoCapture interface. + virtual void StartCapture(media::VideoCapture::EventHandler* handler, + const VideoCaptureCapability& capability); + virtual void StopCapture(media::VideoCapture::EventHandler* handler); + virtual bool CaptureStarted(); + virtual int CaptureWidth(); + virtual int CaptureHeight(); + virtual int CaptureFrameRate(); + + // VideoCaptureMessageFilter::Delegate interface. + virtual void OnBufferReceived(TransportDIB::Handle handle, + base::Time timestamp); + virtual void OnStateChanged(const media::VideoCapture::State& state); + virtual void OnDeviceInfoReceived( + const media::VideoCaptureParams& device_info); + virtual void OnDelegateAdded(int32 device_id); + + bool pending_start() { + return (new_width_ > 0 && new_height_ > 0); + } + + private: + friend class VideoCaptureImplManager; + friend class VideoCaptureImplTest; + + enum State { + kStarted, + kStopping, + kStopped + }; + + struct DIBBuffer { + public: + DIBBuffer(TransportDIB* d, media::VideoCapture::VideoFrameBuffer* ptr); + ~DIBBuffer(); + + TransportDIB* dib; + scoped_refptr<media::VideoCapture::VideoFrameBuffer> mapped_memory; + }; + + VideoCaptureImpl(media::VideoCaptureSessionId id, + scoped_refptr<base::MessageLoopProxy> ml_proxy, + VideoCaptureMessageFilter* filter); + virtual ~VideoCaptureImpl(); + + void Init(); + void DeInit(Task* task); + void StopDevice(); + void RestartCapture(); + void StartCaptureInternal(); + void AddDelegateOnIOThread(); + void RemoveDelegateOnIOThread(Task* task); + + scoped_refptr<VideoCaptureMessageFilter> message_filter_; + media::VideoCaptureSessionId session_id_; + scoped_refptr<base::MessageLoopProxy> ml_proxy_; + int device_id_; + + // Pool of DIBs. + typedef std::list<DIBBuffer*> CachedDIB; + CachedDIB cached_dibs_; + + typedef std::map<media::VideoCapture::EventHandler*, VideoCaptureCapability> + ClientInfo; + ClientInfo clients_; + std::list<media::VideoCapture::EventHandler*> master_clients_; + + ClientInfo pending_clients_; + + int width_; + int height_; + int frame_rate_; + media::VideoFrame::Format video_type_; + + int new_width_; + int new_height_; + State state_; + + DISALLOW_COPY_AND_ASSIGN(VideoCaptureImpl); +}; + +DISABLE_RUNNABLE_METHOD_REFCOUNT(VideoCaptureImpl); + +#endif // CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_ diff --git a/content/renderer/media/video_capture_impl_manager.cc b/content/renderer/media/video_capture_impl_manager.cc new file mode 100644 index 0000000..b8611a6 --- /dev/null +++ b/content/renderer/media/video_capture_impl_manager.cc @@ -0,0 +1,118 @@ +// 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_manager.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"); +} + +VideoCaptureImplManager::~VideoCaptureImplManager() {} + +// 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()); + 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()) + return; + + size_t size = it->second.clients.size(); + it->second.clients.remove(handler); + + if (size == it->second.clients.size() || size > 1) + return; + + manager->devices_[id].vc->DeInit(NewRunnableMethod(manager, + &VideoCaptureImplManager::FreeDevice, manager->devices_[id].vc)); + manager->devices_.erase(id); + return; +} + +// static +VideoCaptureImplManager* VideoCaptureImplManager::GetInstance() { + return Singleton<VideoCaptureImplManager>::get(); +} + +void VideoCaptureImplManager::FreeDevice(VideoCaptureImpl* vc) { + delete vc; +} + +VideoCaptureImplManager::Device::Device() : vc(NULL) {} + +VideoCaptureImplManager::Device::Device( + VideoCaptureImpl* device, + media::VideoCapture::EventHandler* handler) + : vc(device) { + clients.push_front(handler); +} + +VideoCaptureImplManager::Device::~Device() {} + diff --git a/content/renderer/media/video_capture_impl_manager.h b/content/renderer/media/video_capture_impl_manager.h new file mode 100644 index 0000000..f6f46d0 --- /dev/null +++ b/content/renderer/media/video_capture_impl_manager.h @@ -0,0 +1,68 @@ +// 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. + +// VideoCaptureImplManager manages video capture devices in renderer process. +// The video capture clients use AddDevice() to get a pointer to +// video capture device. VideoCaputreImplManager supports multiple clients +// accessing same device. + +#ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_ +#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_ + +#include <list> +#include <map> + +#include "base/message_loop_proxy.h" +#include "base/synchronization/lock.h" +#include "content/renderer/media/video_capture_impl.h" +#include "content/renderer/video_capture_message_filter.h" +#include "media/base/callback.h" +#include "media/base/message_loop_factory.h" +#include "media/video/capture/video_capture.h" + +class VideoCaptureImplManager { + public: + VideoCaptureImplManager(); + ~VideoCaptureImplManager(); + + // Called by video capture client |handler| to add device referenced + // by |id| to VideoCaptureImplManager's list of opened device list. + // A pointer to VideoCapture is returned to client so that client can + // operate on that pointer, such as StartCaptrue, StopCapture. + static media::VideoCapture* AddDevice( + media::VideoCaptureSessionId id, + media::VideoCapture::EventHandler* handler); + + // Called by video capture client |handler| to remove device referenced + // by |id| from VideoCaptureImplManager's list of opened device list. + static void RemoveDevice(media::VideoCaptureSessionId id, + media::VideoCapture::EventHandler* handler); + + static VideoCaptureImplManager* GetInstance(); + + private: + struct Device { + Device(); + Device(VideoCaptureImpl* device, + media::VideoCapture::EventHandler* handler); + ~Device(); + + VideoCaptureImpl* vc; + std::list<media::VideoCapture::EventHandler*> clients; + }; + + void FreeDevice(VideoCaptureImpl* vc); + + typedef std::map<media::VideoCaptureSessionId, Device> Devices; + Devices devices_; + base::Lock lock_; + scoped_refptr<base::MessageLoopProxy> ml_proxy_; + scoped_ptr<media::MessageLoopFactory> ml_factory_; + + DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplManager); +}; + +DISABLE_RUNNABLE_METHOD_REFCOUNT(VideoCaptureImplManager); + +#endif // CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_ diff --git a/content/renderer/media/video_capture_impl_unittest.cc b/content/renderer/media/video_capture_impl_unittest.cc new file mode 100644 index 0000000..e3db8b6 --- /dev/null +++ b/content/renderer/media/video_capture_impl_unittest.cc @@ -0,0 +1,107 @@ +// 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 "base/message_loop.h" +#include "content/renderer/media/video_capture_impl.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::Return; + +#define DEFAULT_CAPABILITY {176, 144, 30, 0, media::VideoFrame::I420, \ + false, false } + +ACTION_P(DeleteMessage, return_value) { + delete arg0; + return return_value; +} + +class MockVideoCaptureMessageFilter : public VideoCaptureMessageFilter { + public: + MockVideoCaptureMessageFilter() : VideoCaptureMessageFilter(1) {} + virtual ~MockVideoCaptureMessageFilter() {} + + // Filter implementation. + MOCK_METHOD1(Send, bool(IPC::Message* message)); + MOCK_METHOD0(ReadyToSend, bool()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureMessageFilter); +}; + +class MockVideoCaptureClient : public media::VideoCapture::EventHandler { + public: + MockVideoCaptureClient() {} + virtual ~MockVideoCaptureClient() {} + + // Filter implementation. + MOCK_METHOD1(OnStarted, void(media::VideoCapture* capture)); + MOCK_METHOD1(OnStopped, void(media::VideoCapture* capture)); + MOCK_METHOD1(OnPaused, void(media::VideoCapture* capture)); + MOCK_METHOD2(OnError, void(media::VideoCapture* capture, int error_code)); + MOCK_METHOD2(OnBufferReady, + void(media::VideoCapture* capture, + scoped_refptr<media::VideoCapture::VideoFrameBuffer> buf)); + MOCK_METHOD2(OnDeviceInfoReceived, + void(media::VideoCapture* capture, + const media::VideoCaptureParams& device_info)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureClient); +}; + +class VideoCaptureImplTest : public ::testing::Test { + public: + VideoCaptureImplTest() { + message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO)); + message_loop_proxy_ = + base::MessageLoopProxy::CreateForCurrentThread().get(); + + message_filter_ = new MockVideoCaptureMessageFilter; + session_id_ = 1; + + video_capture_impl_ = new VideoCaptureImpl(session_id_, message_loop_proxy_, + message_filter_); + + video_capture_impl_->device_id_ = 2; + } + + virtual ~VideoCaptureImplTest() { + delete video_capture_impl_; + } + + protected: + scoped_ptr<MessageLoop> message_loop_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + scoped_refptr<MockVideoCaptureMessageFilter> message_filter_; + media::VideoCaptureSessionId session_id_; + VideoCaptureImpl* video_capture_impl_; + + private: + DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplTest); +}; + +TEST_F(VideoCaptureImplTest, Simple) { + // Execute SetCapture() and StopCapture(). + + scoped_ptr<MockVideoCaptureClient> client(new MockVideoCaptureClient); + media::VideoCapture::VideoCaptureCapability capability = DEFAULT_CAPABILITY; + + EXPECT_CALL(*message_filter_, Send(_)) + .WillRepeatedly(DeleteMessage(true)); + + EXPECT_CALL(*message_filter_, ReadyToSend()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(*client, OnStarted(_)) + .WillOnce(Return()); + + video_capture_impl_->StartCapture(client.get(), capability); + + EXPECT_CALL(*client, OnStopped(_)) + .WillOnce(Return()); + + video_capture_impl_->StopCapture(client.get()); +} diff --git a/content/renderer/video_capture_message_filter.cc b/content/renderer/video_capture_message_filter.cc index c679e2b..f9db7e5 100644 --- a/content/renderer/video_capture_message_filter.cc +++ b/content/renderer/video_capture_message_filter.cc @@ -54,6 +54,13 @@ void VideoCaptureMessageFilter::OnFilterAdded(IPC::Channel* channel) { // Captures the message loop proxy for IPC. message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread(); channel_ = channel; + + for (Delegates::iterator it = pending_delegates_.begin(); + it != pending_delegates_.end(); it++) { + it->second->OnDelegateAdded(it->first); + delegates_[it->first] = it->second; + } + pending_delegates_.clear(); } void VideoCaptureMessageFilter::OnFilterRemoved() { @@ -114,14 +121,18 @@ void VideoCaptureMessageFilter::OnDeviceInfoReceived( delegate->OnDeviceInfoReceived(params); } -int32 VideoCaptureMessageFilter::AddDelegate(Delegate* delegate) { +void VideoCaptureMessageFilter::AddDelegate(Delegate* delegate) { if (++last_device_id_ <= 0) last_device_id_ = 1; while (delegates_.find(last_device_id_) != delegates_.end()) last_device_id_++; - delegates_[last_device_id_] = delegate; - return last_device_id_; + if (channel_) { + delegates_[last_device_id_] = delegate; + delegate->OnDelegateAdded(last_device_id_); + } else { + pending_delegates_[last_device_id_] = delegate; + } } void VideoCaptureMessageFilter::RemoveDelegate(Delegate* delegate) { @@ -132,6 +143,13 @@ void VideoCaptureMessageFilter::RemoveDelegate(Delegate* delegate) { break; } } + for (Delegates::iterator it = pending_delegates_.begin(); + it != pending_delegates_.end(); it++) { + if (it->second == delegate) { + pending_delegates_.erase(it); + break; + } + } } void VideoCaptureMessageFilter::AddFilter() { diff --git a/content/renderer/video_capture_message_filter.h b/content/renderer/video_capture_message_filter.h index 2a885e9..c476616 100644 --- a/content/renderer/video_capture_message_filter.h +++ b/content/renderer/video_capture_message_filter.h @@ -34,6 +34,10 @@ class VideoCaptureMessageFilter : public IPC::ChannelProxy::MessageFilter { virtual void OnDeviceInfoReceived( const media::VideoCaptureParams& device_info) = 0; + // Called when the delegate has been added to filter's delegate list. + // |device_id| is the device id for the delegate. + virtual void OnDelegateAdded(int32 device_id) = 0; + protected: virtual ~Delegate() {} }; @@ -41,14 +45,14 @@ class VideoCaptureMessageFilter : public IPC::ChannelProxy::MessageFilter { explicit VideoCaptureMessageFilter(int32 route_id); virtual ~VideoCaptureMessageFilter(); - // Add a delegate to the map and return id of the entry. - int32 AddDelegate(Delegate* delegate); + // Add a delegate to the map. + void AddDelegate(Delegate* delegate); // Remove a delegate from the map. void RemoveDelegate(Delegate* delegate); // Send a message asynchronously. - bool Send(IPC::Message* message); + virtual bool Send(IPC::Message* message); void AddFilter(); @@ -79,6 +83,7 @@ class VideoCaptureMessageFilter : public IPC::ChannelProxy::MessageFilter { // A map of device ids to delegates. Delegates delegates_; + Delegates pending_delegates_; int32 last_device_id_; int32 route_id_; diff --git a/content/renderer/video_capture_message_filter_unittest.cc b/content/renderer/video_capture_message_filter_unittest.cc index d41c709..1d647db 100644 --- a/content/renderer/video_capture_message_filter_unittest.cc +++ b/content/renderer/video_capture_message_filter_unittest.cc @@ -13,6 +13,8 @@ class MockVideoCaptureDelegate : public VideoCaptureMessageFilter::Delegate { public: MockVideoCaptureDelegate() { Reset(); + device_id_received_ = false; + device_id_ = 0; } virtual void OnBufferReceived(TransportDIB::Handle handle, @@ -34,6 +36,11 @@ class MockVideoCaptureDelegate : public VideoCaptureMessageFilter::Delegate { params_.frame_per_second = params.frame_per_second; } + virtual void OnDelegateAdded(int32 device_id) { + device_id_received_ = true; + device_id_ = device_id; + } + void Reset() { buffer_received_ = false; handle_ = TransportDIB::DefaultHandleValue(); @@ -58,6 +65,9 @@ class MockVideoCaptureDelegate : public VideoCaptureMessageFilter::Delegate { bool device_info_receive() { return device_info_received_; } const media::VideoCaptureParams& received_device_info() { return params_; } + bool device_id_received() { return device_id_received_; } + int32 device_id() { return device_id_; } + private: bool buffer_received_; TransportDIB::Handle handle_; @@ -69,6 +79,9 @@ class MockVideoCaptureDelegate : public VideoCaptureMessageFilter::Delegate { bool device_info_received_; media::VideoCaptureParams params_; + bool device_id_received_; + int32 device_id_; + DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureDelegate); }; @@ -80,14 +93,15 @@ TEST(VideoCaptureMessageFilterTest, Basic) { const int kRouteId = 0; scoped_refptr<VideoCaptureMessageFilter> filter( new VideoCaptureMessageFilter(kRouteId)); + filter->channel_ = reinterpret_cast<IPC::Channel*>(1); MockVideoCaptureDelegate delegate; - int device_id = filter->AddDelegate(&delegate); + filter->AddDelegate(&delegate); // VideoCaptureMsg_StateChanged EXPECT_FALSE(delegate.state_changed_received()); filter->OnMessageReceived( - VideoCaptureMsg_StateChanged(kRouteId, device_id, + VideoCaptureMsg_StateChanged(kRouteId, delegate.device_id(), media::VideoCapture::kStarted)); EXPECT_TRUE(delegate.state_changed_received()); EXPECT_TRUE(media::VideoCapture::kStarted == delegate.state()); @@ -99,7 +113,7 @@ TEST(VideoCaptureMessageFilterTest, Basic) { EXPECT_FALSE(delegate.buffer_received()); filter->OnMessageReceived(VideoCaptureMsg_BufferReady( - kRouteId, device_id, handle, timestamp)); + kRouteId, delegate.device_id(), handle, timestamp)); EXPECT_TRUE(delegate.buffer_received()); #if defined(OS_MACOSX) EXPECT_EQ(handle.fd, delegate.received_buffer_handle().fd); @@ -117,7 +131,7 @@ TEST(VideoCaptureMessageFilterTest, Basic) { EXPECT_FALSE(delegate.device_info_receive()); filter->OnMessageReceived(VideoCaptureMsg_DeviceInfo( - kRouteId, device_id, params)); + kRouteId, delegate.device_id(), params)); EXPECT_TRUE(delegate.device_info_receive()); EXPECT_EQ(params.width, delegate.received_device_info().width); EXPECT_EQ(params.height, delegate.received_device_info().height); @@ -134,18 +148,19 @@ TEST(VideoCaptureMessageFilterTest, Delegates) { const int kRouteId = 0; scoped_refptr<VideoCaptureMessageFilter> filter( new VideoCaptureMessageFilter(kRouteId)); + filter->channel_ = reinterpret_cast<IPC::Channel*>(1); MockVideoCaptureDelegate delegate1; MockVideoCaptureDelegate delegate2; - int device_id1 = filter->AddDelegate(&delegate1); - int device_id2 = filter->AddDelegate(&delegate2); + filter->AddDelegate(&delegate1); + filter->AddDelegate(&delegate2); // Send an IPC message. Make sure the correct delegate gets called. EXPECT_FALSE(delegate1.state_changed_received()); EXPECT_FALSE(delegate2.state_changed_received()); filter->OnMessageReceived( - VideoCaptureMsg_StateChanged(kRouteId, device_id1, + VideoCaptureMsg_StateChanged(kRouteId, delegate1.device_id(), media::VideoCapture::kStarted)); EXPECT_TRUE(delegate1.state_changed_received()); EXPECT_FALSE(delegate2.state_changed_received()); @@ -154,7 +169,7 @@ TEST(VideoCaptureMessageFilterTest, Delegates) { EXPECT_FALSE(delegate1.state_changed_received()); EXPECT_FALSE(delegate2.state_changed_received()); filter->OnMessageReceived( - VideoCaptureMsg_StateChanged(kRouteId, device_id2, + VideoCaptureMsg_StateChanged(kRouteId, delegate2.device_id(), media::VideoCapture::kStarted)); EXPECT_FALSE(delegate1.state_changed_received()); EXPECT_TRUE(delegate2.state_changed_received()); @@ -163,7 +178,7 @@ TEST(VideoCaptureMessageFilterTest, Delegates) { // Send a message of a different route id, a message is not received. EXPECT_FALSE(delegate1.state_changed_received()); filter->OnMessageReceived( - VideoCaptureMsg_StateChanged(kRouteId+1, device_id1, + VideoCaptureMsg_StateChanged(kRouteId+1, delegate1.device_id(), media::VideoCapture::kStarted)); EXPECT_FALSE(delegate1.state_changed_received()); @@ -171,14 +186,14 @@ TEST(VideoCaptureMessageFilterTest, Delegates) { filter->RemoveDelegate(&delegate1); EXPECT_FALSE(delegate1.state_changed_received()); filter->OnMessageReceived( - VideoCaptureMsg_StateChanged(kRouteId, device_id1, + VideoCaptureMsg_StateChanged(kRouteId, delegate1.device_id(), media::VideoCapture::kStarted)); EXPECT_FALSE(delegate1.state_changed_received()); filter->RemoveDelegate(&delegate2); EXPECT_FALSE(delegate2.state_changed_received()); filter->OnMessageReceived( - VideoCaptureMsg_StateChanged(kRouteId, device_id2, + VideoCaptureMsg_StateChanged(kRouteId, delegate2.device_id(), media::VideoCapture::kStarted)); EXPECT_FALSE(delegate2.state_changed_received()); |