Commit 91c2aafb authored by hclam@chromium.org's avatar hclam@chromium.org

Eliminate video capture thread in renderer

The main motivation of this change is to remove the video capture thread
in the renderer. All users of a video capture device already handles the
video frame on their thread. There is no need to call the clients with
an additional thread.

Summary of this change:
* Video capture thread eliminated
  VideoCaptureImpl now runs on the IO thread. Clients are called on the
  IO thread.
* Simplified VideoCaptureImplManager
  We still need to keep this object for the purpose of sharing a
  VideoCaptureImpl object with multiple clients. It should own these
  objects and maintain the usage count. A couple clean up items are done
  on this class:
  * It doesn't own the video capture thread now.
  * It is now a render thread only object.
  * It maintains refcount of a VideoCaptureImpl explicitly.
  * It is no longer refcounted.
  * Clients access it through RenderThreadImpl. Which ensures usage is
    on the render thread.
* New VideoCaptureHandle class
  Object of this class is returned by VideoCaptureImplManager to give
  access to a media::VideoCapture object. It is purely a wrapper and
  helps to do refcounting on the render thread.

Testing:
Added unit tests for VideoCaptureImplManager to test refcounting.
Also updated unit test for VideoCaptureImpl due to the threading
changes.

Review URL: https://codereview.chromium.org/120893002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244074 0039d316-1c4b-4281-b951-d872f2087c98
parent 0bc4efdc
...@@ -541,6 +541,7 @@ ...@@ -541,6 +541,7 @@
'renderer/media/media_stream_video_source_unittest.cc', 'renderer/media/media_stream_video_source_unittest.cc',
'renderer/media/test_response_generator.cc', 'renderer/media/test_response_generator.cc',
'renderer/media/test_response_generator.h', 'renderer/media/test_response_generator.h',
'renderer/media/video_capture_impl_manager_unittest.cc',
'renderer/media/video_capture_impl_unittest.cc', 'renderer/media/video_capture_impl_unittest.cc',
'renderer/media/video_capture_message_filter_unittest.cc', 'renderer/media/video_capture_message_filter_unittest.cc',
'renderer/media/video_destination_handler_unittest.cc', 'renderer/media/video_destination_handler_unittest.cc',
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include "content/renderer/media/rtc_video_capturer.h" #include "content/renderer/media/rtc_video_capturer.h"
#include "content/renderer/media/rtc_video_decoder_factory.h" #include "content/renderer/media/rtc_video_decoder_factory.h"
#include "content/renderer/media/rtc_video_encoder_factory.h" #include "content/renderer/media/rtc_video_encoder_factory.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/media/webaudio_capturer_source.h" #include "content/renderer/media/webaudio_capturer_source.h"
#include "content/renderer/media/webrtc_audio_device_impl.h" #include "content/renderer/media/webrtc_audio_device_impl.h"
#include "content/renderer/media/webrtc_local_audio_track.h" #include "content/renderer/media/webrtc_local_audio_track.h"
...@@ -241,13 +240,11 @@ class SourceStateObserver : public webrtc::ObserverInterface, ...@@ -241,13 +240,11 @@ class SourceStateObserver : public webrtc::ObserverInterface,
}; };
MediaStreamDependencyFactory::MediaStreamDependencyFactory( MediaStreamDependencyFactory::MediaStreamDependencyFactory(
VideoCaptureImplManager* vc_manager,
P2PSocketDispatcher* p2p_socket_dispatcher) P2PSocketDispatcher* p2p_socket_dispatcher)
: network_manager_(NULL), : network_manager_(NULL),
#if defined(GOOGLE_TV) #if defined(GOOGLE_TV)
decoder_factory_tv_(NULL), decoder_factory_tv_(NULL),
#endif #endif
vc_manager_(vc_manager),
p2p_socket_dispatcher_(p2p_socket_dispatcher), p2p_socket_dispatcher_(p2p_socket_dispatcher),
signaling_thread_(NULL), signaling_thread_(NULL),
worker_thread_(NULL), worker_thread_(NULL),
...@@ -730,7 +727,7 @@ MediaStreamDependencyFactory::CreateLocalVideoSource( ...@@ -730,7 +727,7 @@ MediaStreamDependencyFactory::CreateLocalVideoSource(
bool is_screencast, bool is_screencast,
const webrtc::MediaConstraintsInterface* constraints) { const webrtc::MediaConstraintsInterface* constraints) {
RtcVideoCapturer* capturer = new RtcVideoCapturer( RtcVideoCapturer* capturer = new RtcVideoCapturer(
video_session_id, vc_manager_.get(), is_screencast); video_session_id, is_screencast);
// The video source takes ownership of |capturer|. // The video source takes ownership of |capturer|.
scoped_refptr<webrtc::VideoSourceInterface> source = scoped_refptr<webrtc::VideoSourceInterface> source =
......
...@@ -46,7 +46,6 @@ namespace content { ...@@ -46,7 +46,6 @@ namespace content {
class IpcNetworkManager; class IpcNetworkManager;
class IpcPacketSocketFactory; class IpcPacketSocketFactory;
class RTCMediaConstraints; class RTCMediaConstraints;
class VideoCaptureImplManager;
class WebAudioCapturerSource; class WebAudioCapturerSource;
class WebRtcAudioCapturer; class WebRtcAudioCapturer;
class WebRtcAudioDeviceImpl; class WebRtcAudioDeviceImpl;
...@@ -67,7 +66,6 @@ class CONTENT_EXPORT MediaStreamDependencyFactory ...@@ -67,7 +66,6 @@ class CONTENT_EXPORT MediaStreamDependencyFactory
typedef base::Callback<void(blink::WebMediaStream* web_stream, typedef base::Callback<void(blink::WebMediaStream* web_stream,
bool live)> MediaSourcesCreatedCallback; bool live)> MediaSourcesCreatedCallback;
MediaStreamDependencyFactory( MediaStreamDependencyFactory(
VideoCaptureImplManager* vc_manager,
P2PSocketDispatcher* p2p_socket_dispatcher); P2PSocketDispatcher* p2p_socket_dispatcher);
virtual ~MediaStreamDependencyFactory(); virtual ~MediaStreamDependencyFactory();
...@@ -255,7 +253,6 @@ class CONTENT_EXPORT MediaStreamDependencyFactory ...@@ -255,7 +253,6 @@ class CONTENT_EXPORT MediaStreamDependencyFactory
RTCVideoDecoderFactoryTv* decoder_factory_tv_; RTCVideoDecoderFactoryTv* decoder_factory_tv_;
#endif #endif
scoped_refptr<VideoCaptureImplManager> vc_manager_;
scoped_refptr<P2PSocketDispatcher> p2p_socket_dispatcher_; scoped_refptr<P2PSocketDispatcher> p2p_socket_dispatcher_;
scoped_refptr<WebRtcAudioDeviceImpl> audio_device_; scoped_refptr<WebRtcAudioDeviceImpl> audio_device_;
......
...@@ -367,7 +367,7 @@ class MockIceCandidate : public IceCandidateInterface { ...@@ -367,7 +367,7 @@ class MockIceCandidate : public IceCandidateInterface {
}; };
MockMediaStreamDependencyFactory::MockMediaStreamDependencyFactory() MockMediaStreamDependencyFactory::MockMediaStreamDependencyFactory()
: MediaStreamDependencyFactory(NULL, NULL), : MediaStreamDependencyFactory(NULL),
mock_pc_factory_created_(false) { mock_pc_factory_created_(false) {
} }
......
...@@ -5,25 +5,27 @@ ...@@ -5,25 +5,27 @@
#include "content/renderer/media/rtc_video_capture_delegate.h" #include "content/renderer/media/rtc_video_capture_delegate.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/location.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/render_thread_impl.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
namespace content { namespace content {
RtcVideoCaptureDelegate::RtcVideoCaptureDelegate( RtcVideoCaptureDelegate::RtcVideoCaptureDelegate(
const media::VideoCaptureSessionId id, const media::VideoCaptureSessionId id)
VideoCaptureImplManager* vc_manager)
: session_id_(id), : session_id_(id),
vc_manager_(vc_manager),
capture_engine_(NULL),
got_first_frame_(false), got_first_frame_(false),
error_occured_(false) { error_occured_(false) {
DVLOG(3) << " RtcVideoCaptureDelegate::ctor"; DVLOG(3) << " RtcVideoCaptureDelegate::ctor";
capture_engine_ = vc_manager_->AddDevice(session_id_, this); capture_engine_ =
RenderThreadImpl::current()->video_capture_impl_manager()
->UseDevice(session_id_);
} }
RtcVideoCaptureDelegate::~RtcVideoCaptureDelegate() { RtcVideoCaptureDelegate::~RtcVideoCaptureDelegate() {
DVLOG(3) << " RtcVideoCaptureDelegate::dtor"; DVLOG(3) << " RtcVideoCaptureDelegate::dtor";
vc_manager_->RemoveDevice(session_id_, this); StopCapture();
} }
void RtcVideoCaptureDelegate::StartCapture( void RtcVideoCaptureDelegate::StartCapture(
......
...@@ -8,11 +8,12 @@ ...@@ -8,11 +8,12 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/message_loop/message_loop_proxy.h" #include "base/message_loop/message_loop_proxy.h"
#include "content/common/media/video_capture.h" #include "content/common/media/video_capture.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "media/video/capture/video_capture.h" #include "media/video/capture/video_capture.h"
namespace content { namespace content {
class VideoCaptureHandle;
// Implements a simple reference counted video capturer that guarantees that // Implements a simple reference counted video capturer that guarantees that
// methods in RtcVideoCaptureDelegateEventHandler is only called from when // methods in RtcVideoCaptureDelegateEventHandler is only called from when
// StartCapture have been called until after StopCapture have been called. // StartCapture have been called until after StopCapture have been called.
...@@ -33,16 +34,17 @@ class RtcVideoCaptureDelegate ...@@ -33,16 +34,17 @@ class RtcVideoCaptureDelegate
FrameCapturedCallback; FrameCapturedCallback;
typedef base::Callback<void(CaptureState)> StateChangeCallback; typedef base::Callback<void(CaptureState)> StateChangeCallback;
RtcVideoCaptureDelegate(const media::VideoCaptureSessionId id, RtcVideoCaptureDelegate(const media::VideoCaptureSessionId id);
VideoCaptureImplManager* vc_manager);
void StartCapture(const media::VideoCaptureParams& params, void StartCapture(const media::VideoCaptureParams& params,
const FrameCapturedCallback& captured_callback, const FrameCapturedCallback& captured_callback,
const StateChangeCallback& state_callback); const StateChangeCallback& state_callback);
void StopCapture(); void StopCapture();
protected:
// media::VideoCapture::EventHandler implementation. // media::VideoCapture::EventHandler implementation.
// These functions are called from a thread owned by |vc_manager_|. // These functions are called on the IO thread (same as where
// |capture_engine_| runs).
virtual void OnStarted(media::VideoCapture* capture) OVERRIDE; virtual void OnStarted(media::VideoCapture* capture) OVERRIDE;
virtual void OnStopped(media::VideoCapture* capture) OVERRIDE; virtual void OnStopped(media::VideoCapture* capture) OVERRIDE;
virtual void OnPaused(media::VideoCapture* capture) OVERRIDE; virtual void OnPaused(media::VideoCapture* capture) OVERRIDE;
...@@ -66,9 +68,7 @@ class RtcVideoCaptureDelegate ...@@ -66,9 +68,7 @@ class RtcVideoCaptureDelegate
// The id identifies which video capture device is used for this video // The id identifies which video capture device is used for this video
// capture session. // capture session.
media::VideoCaptureSessionId session_id_; media::VideoCaptureSessionId session_id_;
// The video capture manager handles open/close of video capture devices. scoped_ptr<VideoCaptureHandle> capture_engine_;
scoped_refptr<VideoCaptureImplManager> vc_manager_;
media::VideoCapture* capture_engine_;
// Accessed on the thread where StartCapture is called. // Accessed on the thread where StartCapture is called.
bool got_first_frame_; bool got_first_frame_;
......
...@@ -11,10 +11,9 @@ ...@@ -11,10 +11,9 @@
namespace content { namespace content {
RtcVideoCapturer::RtcVideoCapturer(const media::VideoCaptureSessionId id, RtcVideoCapturer::RtcVideoCapturer(const media::VideoCaptureSessionId id,
VideoCaptureImplManager* vc_manager,
bool is_screencast) bool is_screencast)
: is_screencast_(is_screencast), : is_screencast_(is_screencast),
delegate_(new RtcVideoCaptureDelegate(id, vc_manager)), delegate_(new RtcVideoCaptureDelegate(id)),
state_(VIDEO_CAPTURE_STATE_STOPPED) {} state_(VIDEO_CAPTURE_STATE_STOPPED) {}
RtcVideoCapturer::~RtcVideoCapturer() { RtcVideoCapturer::~RtcVideoCapturer() {
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "third_party/libjingle/source/talk/media/base/videocapturer.h" #include "third_party/libjingle/source/talk/media/base/videocapturer.h"
namespace content { namespace content {
class VideoCaptureImplManager;
// RtcVideoCapturer implements a simple cricket::VideoCapturer that is used for // RtcVideoCapturer implements a simple cricket::VideoCapturer that is used for
// VideoCapturing in libJingle and especially in PeerConnections. // VideoCapturing in libJingle and especially in PeerConnections.
...@@ -24,7 +23,6 @@ class RtcVideoCapturer ...@@ -24,7 +23,6 @@ class RtcVideoCapturer
: public cricket::VideoCapturer { : public cricket::VideoCapturer {
public: public:
RtcVideoCapturer(const media::VideoCaptureSessionId id, RtcVideoCapturer(const media::VideoCaptureSessionId id,
VideoCaptureImplManager* vc_manager,
bool is_screencast); bool is_screencast);
virtual ~RtcVideoCapturer(); virtual ~RtcVideoCapturer();
......
...@@ -9,23 +9,21 @@ ...@@ -9,23 +9,21 @@
// VideoCaptureImpl is also a delegate of VideoCaptureMessageFilter which relays // VideoCaptureImpl is also a delegate of VideoCaptureMessageFilter which relays
// operation of a capture device to the browser process and receives responses // operation of a capture device to the browser process and receives responses
// from browser process. // from browser process.
// The media::VideoCapture and VideoCaptureMessageFilter::Delegate are
// asynchronous interfaces, which means callers can call those interfaces
// from any threads without worrying about thread safety.
// The |capture_message_loop_proxy_| is the working thread of VideoCaptureImpl.
// All non-const members are accessed only on that working thread.
// //
// Implementation note: tasks are posted bound to Unretained(this) to both the // All public methods of VideoCaptureImpl can be called on any thread.
// I/O and Capture threads and this is safe (even though the I/O thread is // Internally it runs on the IO thread. Clients of this class implement
// scoped to the renderer process and the capture_message_loop_proxy_ thread is // interface media::VideoCapture::EventHandler which is called only on the IO
// scoped to the VideoCaptureImplManager) because VideoCaptureImplManager only // thread.
// triggers deletion of its VideoCaptureImpl's by calling DeInit which detours //
// through the capture & I/O threads, so as long as nobody posts tasks after the // Implementation note: tasks are posted bound to Unretained(this) to the I/O
// DeInit() call is made, it is guaranteed none of these Unretained posted tasks // thread and this is safe (even though the I/O thread is scoped to the renderer
// will dangle after the delete goes through. The "as long as" is guaranteed by // process) because VideoCaptureImplManager only triggers deletion of its
// clients of VideoCaptureImplManager not using devices after they've // VideoCaptureImpl's by calling DeInit which detours through the I/O thread, so
// RemoveDevice'd them. // as long as nobody posts tasks after the DeInit() call is made, it is
// guaranteed none of these Unretained posted tasks will dangle after the delete
// goes through. The "as long as" is guaranteed by clients of
// VideoCaptureImplManager not using devices after they've released
// VideoCaptureHandle, which is a wrapper of this object.
#ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_ #ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_ #define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
...@@ -49,6 +47,19 @@ namespace content { ...@@ -49,6 +47,19 @@ namespace content {
class CONTENT_EXPORT VideoCaptureImpl class CONTENT_EXPORT VideoCaptureImpl
: public media::VideoCapture, public VideoCaptureMessageFilter::Delegate { : public media::VideoCapture, public VideoCaptureMessageFilter::Delegate {
public: public:
VideoCaptureImpl(media::VideoCaptureSessionId session_id,
VideoCaptureMessageFilter* filter);
virtual ~VideoCaptureImpl();
// Start listening to IPC messages.
void Init();
// Stop listening to IPC messages. Call |done_cb| when done.
void DeInit(base::Closure done_cb);
// Stop/resume delivering video frames to clients, based on flag |suspend|.
void SuspendCapture(bool suspend);
// media::VideoCapture interface. // media::VideoCapture interface.
virtual void StartCapture( virtual void StartCapture(
media::VideoCapture::EventHandler* handler, media::VideoCapture::EventHandler* handler,
...@@ -57,23 +68,9 @@ class CONTENT_EXPORT VideoCaptureImpl ...@@ -57,23 +68,9 @@ class CONTENT_EXPORT VideoCaptureImpl
virtual bool CaptureStarted() OVERRIDE; virtual bool CaptureStarted() OVERRIDE;
virtual int CaptureFrameRate() OVERRIDE; virtual int CaptureFrameRate() OVERRIDE;
// VideoCaptureMessageFilter::Delegate interface. media::VideoCaptureSessionId session_id() const { return session_id_; }
virtual void OnBufferCreated(base::SharedMemoryHandle handle,
int length,
int buffer_id) OVERRIDE;
virtual void OnBufferDestroyed(int buffer_id) OVERRIDE;
virtual void OnBufferReceived(int buffer_id,
base::TimeTicks timestamp,
const media::VideoCaptureFormat& format)
OVERRIDE;
virtual void OnStateChanged(VideoCaptureState state) OVERRIDE;
virtual void OnDelegateAdded(int32 device_id) OVERRIDE;
// Stop/resume delivering video frames to clients, based on flag |suspend|.
virtual void SuspendCapture(bool suspend);
private: private:
friend class VideoCaptureImplManager;
friend class VideoCaptureImplTest; friend class VideoCaptureImplTest;
friend class MockVideoCaptureImpl; friend class MockVideoCaptureImpl;
...@@ -81,38 +78,35 @@ class CONTENT_EXPORT VideoCaptureImpl ...@@ -81,38 +78,35 @@ class CONTENT_EXPORT VideoCaptureImpl
typedef std::map<media::VideoCapture::EventHandler*, typedef std::map<media::VideoCapture::EventHandler*,
media::VideoCaptureParams> ClientInfo; media::VideoCaptureParams> ClientInfo;
VideoCaptureImpl(media::VideoCaptureSessionId session_id, void InitOnIOThread();
base::MessageLoopProxy* capture_message_loop_proxy, void DeInitOnIOThread(base::Closure done_cb);
VideoCaptureMessageFilter* filter); void SuspendCaptureOnIOThread(bool suspend);
virtual ~VideoCaptureImpl(); void StartCaptureOnIOThread(
void DoStartCaptureOnCaptureThread(
media::VideoCapture::EventHandler* handler, media::VideoCapture::EventHandler* handler,
const media::VideoCaptureParams& params); const media::VideoCaptureParams& params);
void DoStopCaptureOnCaptureThread(media::VideoCapture::EventHandler* handler); void StopCaptureOnIOThread(media::VideoCapture::EventHandler* handler);
void DoBufferCreatedOnCaptureThread(base::SharedMemoryHandle handle,
int length, // VideoCaptureMessageFilter::Delegate interface.
int buffer_id); virtual void OnBufferCreated(base::SharedMemoryHandle handle,
void DoBufferDestroyedOnCaptureThread(int buffer_id); int length,
void DoBufferReceivedOnCaptureThread(int buffer_id, int buffer_id) OVERRIDE;
base::TimeTicks timestamp, virtual void OnBufferDestroyed(int buffer_id) OVERRIDE;
const media::VideoCaptureFormat& format); virtual void OnBufferReceived(
void DoClientBufferFinishedOnCaptureThread(
int buffer_id, int buffer_id,
const scoped_refptr<ClientBuffer>& buffer); base::TimeTicks timestamp,
void DoStateChangedOnCaptureThread(VideoCaptureState state); const media::VideoCaptureFormat& format) OVERRIDE;
void DoDelegateAddedOnCaptureThread(int32 device_id); virtual void OnStateChanged(VideoCaptureState state) OVERRIDE;
virtual void OnDelegateAdded(int32 device_id) OVERRIDE;
void DoSuspendCaptureOnCaptureThread(bool suspend); // Sends an IPC message to browser process when all clients are done with the
// buffer.
void OnClientBufferFinished(
int buffer_id,
const scoped_refptr<ClientBuffer>& buffer);
void Init();
void DeInit(base::Closure task);
void DoDeInitOnCaptureThread(base::Closure task);
void StopDevice(); void StopDevice();
void RestartCapture(); void RestartCapture();
void StartCaptureInternal(); void StartCaptureInternal();
void AddDelegateOnIOThread();
void RemoveDelegateOnIOThread(base::Closure task);
virtual void Send(IPC::Message* message); virtual void Send(IPC::Message* message);
// Helpers. // Helpers.
...@@ -120,7 +114,6 @@ class CONTENT_EXPORT VideoCaptureImpl ...@@ -120,7 +114,6 @@ class CONTENT_EXPORT VideoCaptureImpl
ClientInfo* clients); ClientInfo* clients);
const scoped_refptr<VideoCaptureMessageFilter> message_filter_; const scoped_refptr<VideoCaptureMessageFilter> message_filter_;
const scoped_refptr<base::MessageLoopProxy> capture_message_loop_proxy_;
const scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; const scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
int device_id_; int device_id_;
const int session_id_; const int session_id_;
...@@ -134,7 +127,7 @@ class CONTENT_EXPORT VideoCaptureImpl ...@@ -134,7 +127,7 @@ class CONTENT_EXPORT VideoCaptureImpl
ClientInfo clients_pending_on_restart_; ClientInfo clients_pending_on_restart_;
// Member params_ represents the video format requested by the // Member params_ represents the video format requested by the
// client to this class via DoStartCaptureOnCaptureThread. // client to this class via StartCapture().
media::VideoCaptureParams params_; media::VideoCaptureParams params_;
// The device's video capture format sent from browser process side. // The device's video capture format sent from browser process side.
......
...@@ -5,84 +5,103 @@ ...@@ -5,84 +5,103 @@
#include "content/renderer/media/video_capture_impl_manager.h" #include "content/renderer/media/video_capture_impl_manager.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/stl_util.h" #include "base/bind_helpers.h"
#include "content/public/renderer/render_thread.h"
#include "content/renderer/media/video_capture_impl.h" #include "content/renderer/media/video_capture_impl.h"
#include "content/renderer/media/video_capture_message_filter.h" #include "content/renderer/media/video_capture_message_filter.h"
#include "media/base/bind_to_loop.h"
namespace content { namespace content {
VideoCaptureImplManager::VideoCaptureImplManager() VideoCaptureHandle::VideoCaptureHandle(
: thread_("VC manager") { media::VideoCapture* impl, base::Closure destruction_cb)
thread_.Start(); : impl_(impl), destruction_cb_(destruction_cb) {
message_loop_proxy_ = thread_.message_loop_proxy();
filter_ = new VideoCaptureMessageFilter();
} }
media::VideoCapture* VideoCaptureImplManager::AddDevice( VideoCaptureHandle::~VideoCaptureHandle() {
media::VideoCaptureSessionId id, destruction_cb_.Run();
media::VideoCapture::EventHandler* handler) { }
DCHECK(handler);
base::AutoLock auto_lock(lock_); void VideoCaptureHandle::StartCapture(
Devices::iterator it = devices_.find(id); EventHandler* handler,
if (it == devices_.end()) { const media::VideoCaptureParams& params) {
VideoCaptureImpl* vc = impl_->StartCapture(handler, params);
new VideoCaptureImpl(id, message_loop_proxy_.get(), filter_.get()); }
devices_[id] = new Device(vc, handler);
vc->Init();
return vc;
}
devices_[id]->clients.push_front(handler); void VideoCaptureHandle::StopCapture(EventHandler* handler) {
return it->second->vc; impl_->StopCapture(handler);
} }
void VideoCaptureImplManager::SuspendDevices(bool suspend) { bool VideoCaptureHandle::CaptureStarted() {
base::AutoLock auto_lock(lock_); return impl_->CaptureStarted();
for (Devices::iterator it = devices_.begin(); it != devices_.end(); ++it)
it->second->vc->SuspendCapture(suspend);
} }
void VideoCaptureImplManager::RemoveDevice( int VideoCaptureHandle::CaptureFrameRate() {
media::VideoCaptureSessionId id, return impl_->CaptureFrameRate();
media::VideoCapture::EventHandler* handler) { }
DCHECK(handler);
base::AutoLock auto_lock(lock_); VideoCaptureImplManager::VideoCaptureImplManager()
Devices::iterator it = devices_.find(id); : filter_(new VideoCaptureMessageFilter()),
if (it == devices_.end()) weak_factory_(this) {
return; }
size_t size = it->second->clients.size(); VideoCaptureImplManager::~VideoCaptureImplManager() {
it->second->clients.remove(handler); DCHECK(thread_checker_.CalledOnValidThread());
}
if (size == it->second->clients.size() || size > 1) scoped_ptr<VideoCaptureHandle> VideoCaptureImplManager::UseDevice(
return; media::VideoCaptureSessionId id) {
DCHECK(thread_checker_.CalledOnValidThread());
devices_[id]->vc->DeInit(base::Bind(&VideoCaptureImplManager::FreeDevice, VideoCaptureImpl* video_capture_device = NULL;
this, devices_[id]->vc)); VideoCaptureDeviceMap::iterator it = devices_.find(id);
delete devices_[id]; if (it == devices_.end()) {
devices_.erase(id); video_capture_device = CreateVideoCaptureImpl(id, filter_.get());
} devices_[id] =
std::make_pair(1, linked_ptr<VideoCaptureImpl>(video_capture_device));
video_capture_device->Init();
} else {
++it->second.first;
video_capture_device = it->second.second.get();
}
void VideoCaptureImplManager::FreeDevice(VideoCaptureImpl* vc) { // This callback ensures UnrefDevice() happens on the render thread.
delete vc; return scoped_ptr<VideoCaptureHandle>(
new VideoCaptureHandle(
video_capture_device,
media::BindToCurrentLoop(
base::Bind(
&VideoCaptureImplManager::UnrefDevice,
weak_factory_.GetWeakPtr(),
id))));
} }
VideoCaptureImplManager::~VideoCaptureImplManager() { VideoCaptureImpl* VideoCaptureImplManager::CreateVideoCaptureImpl(
thread_.Stop(); media::VideoCaptureSessionId id,
// TODO(wjia): uncomment the line below after collecting enough info for VideoCaptureMessageFilter* filter) const {
// crbug.com/152418. return new VideoCaptureImpl(id, filter);
// STLDeleteContainerPairSecondPointers(devices_.begin(), devices_.end());
} }
VideoCaptureImplManager::Device::Device( void VideoCaptureImplManager::UnrefDevice(
VideoCaptureImpl* device, media::VideoCaptureSessionId id) {
media::VideoCapture::EventHandler* handler) DCHECK(thread_checker_.CalledOnValidThread());
: vc(device) { VideoCaptureDeviceMap::iterator it = devices_.find(id);
clients.push_front(handler); DCHECK(it != devices_.end());
DCHECK(it->second.first);
--it->second.first;
if (!it->second.first) {
VideoCaptureImpl* impl = it->second.second.release();
devices_.erase(id);
impl->DeInit(base::Bind(&base::DeletePointer<VideoCaptureImpl>, impl));
}
} }
VideoCaptureImplManager::Device::~Device() {} void VideoCaptureImplManager::SuspendDevices(bool suspend) {
DCHECK(thread_checker_.CalledOnValidThread());
for (VideoCaptureDeviceMap::iterator it = devices_.begin();
it != devices_.end(); ++it)
it->second.second->SuspendCapture(suspend);
}
} // namespace content } // namespace content
...@@ -2,77 +2,104 @@ ...@@ -2,77 +2,104 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// VideoCaptureImplManager manages video capture devices in renderer process. // VideoCaptureImplManager owns VideoCaptureImpl objects. Clients who
// The video capture clients use AddDevice() to get a pointer to // want access to a video capture device call UseDevice() to get a handle
// video capture device. VideoCaputreImplManager supports multiple clients // to VideoCaptureImpl.
// accessing same device. //
// THREADING
//
// VideoCaptureImplManager lives only on the render thread. All methods
// must be called on this thread.
//
// The handle returned by UseDevice() is thread-safe. It ensures
// destruction is handled on the render thread.
#ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_ #ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_
#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_ #define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_
#include <list>
#include <map> #include <map>
#include "base/callback.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop_proxy.h" #include "base/message_loop/message_loop_proxy.h"
#include "base/threading/thread.h"
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "media/video/capture/video_capture.h" #include "media/video/capture/video_capture.h"
namespace content { namespace content {
class VideoCaptureImpl; class VideoCaptureImpl;
class VideoCaptureImplManager;
class VideoCaptureMessageFilter; class VideoCaptureMessageFilter;
class CONTENT_EXPORT VideoCaptureImplManager // Thread-safe wrapper for a media::VideoCapture object. During
: public base::RefCountedThreadSafe<VideoCaptureImplManager> { // destruction |destruction_cb| is called. This mechanism is used
// by VideoCaptureImplManager to ensure de-initialization and
// destruction of the media::VideoCapture object happens on the render
// thread.
class CONTENT_EXPORT VideoCaptureHandle : media::VideoCapture {
public: public:
VideoCaptureImplManager(); virtual ~VideoCaptureHandle();
// Called by video capture client |handler| to add device referenced // media::VideoCapture implementations.
// by |id| to VideoCaptureImplManager's list of opened device list. virtual void StartCapture(
// A pointer to VideoCapture is returned to client so that client can EventHandler* handler,
// operate on that pointer, such as StartCaptrue, StopCapture. const media::VideoCaptureParams& params) OVERRIDE;
virtual media::VideoCapture* AddDevice( virtual void StopCapture(EventHandler* handler) OVERRIDE;
media::VideoCaptureSessionId id, virtual bool CaptureStarted() OVERRIDE;
media::VideoCapture::EventHandler* handler); virtual int CaptureFrameRate() OVERRIDE;
private:
friend class VideoCaptureImplManager;
VideoCaptureHandle(media::VideoCapture* impl,
base::Closure destruction_cb);
// Called by video capture client |handler| to remove device referenced media::VideoCapture* impl_;
// by |id| from VideoCaptureImplManager's list of opened device list. base::Closure destruction_cb_;
virtual void RemoveDevice(media::VideoCaptureSessionId id,
media::VideoCapture::EventHandler* handler); DISALLOW_COPY_AND_ASSIGN(VideoCaptureHandle);
};
class CONTENT_EXPORT VideoCaptureImplManager {
public:
VideoCaptureImplManager();
virtual ~VideoCaptureImplManager();
// Returns a video capture device referenced by |id|.
scoped_ptr<VideoCaptureHandle> UseDevice(media::VideoCaptureSessionId id);
// Make all existing VideoCaptureImpl instances stop/resume delivering // Make all existing VideoCaptureImpl instances stop/resume delivering
// video frames to their clients, depends on flag |suspend|. // video frames to their clients, depends on flag |suspend|.
virtual void SuspendDevices(bool suspend); void SuspendDevices(bool suspend);
VideoCaptureMessageFilter* video_capture_message_filter() const { VideoCaptureMessageFilter* video_capture_message_filter() const {
return filter_.get(); return filter_.get();
} }
protected: protected:
virtual ~VideoCaptureImplManager(); // Used in tests to inject a mock VideoCaptureImpl.
virtual VideoCaptureImpl* CreateVideoCaptureImpl(
media::VideoCaptureSessionId id,
VideoCaptureMessageFilter* filter) const;
private: private:
friend class base::RefCountedThreadSafe<VideoCaptureImplManager>; void UnrefDevice(media::VideoCaptureSessionId id);
struct Device {
Device(VideoCaptureImpl* device,
media::VideoCapture::EventHandler* handler);
~Device();
VideoCaptureImpl* vc; // The int is used to count clients of the corresponding VideoCaptureImpl.
std::list<media::VideoCapture::EventHandler*> clients; typedef std::map<media::VideoCaptureSessionId,
}; std::pair<int, linked_ptr<VideoCaptureImpl> > >
VideoCaptureDeviceMap;
VideoCaptureDeviceMap devices_;
void FreeDevice(VideoCaptureImpl* vc);
typedef std::map<media::VideoCaptureSessionId, Device*> Devices;
Devices devices_;
base::Lock lock_;
scoped_refptr<VideoCaptureMessageFilter> filter_; scoped_refptr<VideoCaptureMessageFilter> filter_;
base::Thread thread_;
scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; // Following two members are bound to the render thread.
base::WeakPtrFactory<VideoCaptureImplManager> weak_factory_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplManager); DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplManager);
}; };
......
// Copyright 2014 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/bind.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/child/child_process.h"
#include "content/renderer/media/video_capture_impl.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/media/video_capture_message_filter.h"
#include "media/base/bind_to_loop.h"
#include "media/video/capture/mock_video_capture_event_handler.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::DoAll;
using ::testing::SaveArg;
using media::BindToCurrentLoop;
using media::MockVideoCaptureEventHandler;
namespace content {
ACTION_P(RunClosure, closure) {
closure.Run();
}
class MockVideoCaptureImpl : public VideoCaptureImpl {
public:
MockVideoCaptureImpl(media::VideoCaptureSessionId session_id,
VideoCaptureMessageFilter* filter)
: VideoCaptureImpl(session_id, filter) {
}
virtual ~MockVideoCaptureImpl() {
Destruct();
}
MOCK_METHOD0(Destruct, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImpl);
};
class MockVideoCaptureImplManager : public VideoCaptureImplManager {
public:
MockVideoCaptureImplManager() {}
protected:
virtual VideoCaptureImpl* CreateVideoCaptureImpl(
media::VideoCaptureSessionId id,
VideoCaptureMessageFilter* filter) const OVERRIDE {
return new MockVideoCaptureImpl(id, filter);
}
private:
DISALLOW_COPY_AND_ASSIGN(MockVideoCaptureImplManager);
};
class VideoCaptureImplManagerTest : public ::testing::Test {
public:
VideoCaptureImplManagerTest() {
params_.requested_format = media::VideoCaptureFormat(
gfx::Size(176, 144), 30, media::PIXEL_FORMAT_I420);
child_process_.reset(new ChildProcess());
}
void FakeChannelSetup() {
scoped_refptr<base::MessageLoopProxy> loop =
child_process_->io_message_loop_proxy();
if (!loop->BelongsToCurrentThread()) {
loop->PostTask(
FROM_HERE,
base::Bind(
&VideoCaptureImplManagerTest::FakeChannelSetup,
base::Unretained(this)));
return;
}
manager_.video_capture_message_filter()->OnFilterAdded(NULL);
}
void Quit(base::RunLoop* run_loop) {
message_loop_.PostTask(FROM_HERE, run_loop->QuitClosure());
}
protected:
base::MessageLoop message_loop_;
scoped_ptr<ChildProcess> child_process_;
media::VideoCaptureParams params_;
MockVideoCaptureImplManager manager_;
private:
DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplManagerTest);
};
// Multiple clients with the same session id. There is only one
// media::VideoCapture object.
TEST_F(VideoCaptureImplManagerTest, MultipleClients) {
scoped_ptr<MockVideoCaptureEventHandler> client1(
new MockVideoCaptureEventHandler);
scoped_ptr<MockVideoCaptureEventHandler> client2(
new MockVideoCaptureEventHandler);
media::VideoCapture* device1 = NULL;
media::VideoCapture* device2 = NULL;
scoped_ptr<VideoCaptureHandle> handle1;
scoped_ptr<VideoCaptureHandle> handle2;
{
base::RunLoop run_loop;
base::Closure quit_closure = BindToCurrentLoop(
run_loop.QuitClosure());
EXPECT_CALL(*client1, OnStarted(_)).WillOnce(SaveArg<0>(&device1));
EXPECT_CALL(*client2, OnStarted(_)).WillOnce(
DoAll(
SaveArg<0>(&device2),
RunClosure(quit_closure)));
handle1 = manager_.UseDevice(1);
handle2 = manager_.UseDevice(1);
handle1->StartCapture(client1.get(), params_);
handle2->StartCapture(client2.get(), params_);
FakeChannelSetup();
run_loop.Run();
}
{
base::RunLoop run_loop;
base::Closure quit_closure = BindToCurrentLoop(
run_loop.QuitClosure());
EXPECT_CALL(*client1, OnStopped(_));
EXPECT_CALL(*client1, OnRemoved(_));
EXPECT_CALL(*client2, OnStopped(_));
EXPECT_CALL(*client2, OnRemoved(_)).WillOnce(
RunClosure(quit_closure));
handle1->StopCapture(client1.get());
handle2->StopCapture(client2.get());
run_loop.Run();
}
EXPECT_TRUE(device1 == device2);
EXPECT_CALL(*static_cast<MockVideoCaptureImpl*>(device1), Destruct());
}
} // namespace content
...@@ -28,7 +28,6 @@ PepperPlatformVideoCapture::PepperPlatformVideoCapture( ...@@ -28,7 +28,6 @@ PepperPlatformVideoCapture::PepperPlatformVideoCapture(
handler_proxy_(new media::VideoCaptureHandlerProxy( handler_proxy_(new media::VideoCaptureHandlerProxy(
this, base::MessageLoopProxy::current())), this, base::MessageLoopProxy::current())),
handler_(handler), handler_(handler),
video_capture_(NULL),
unbalanced_start_(false), unbalanced_start_(false),
pending_open_device_(false), pending_open_device_(false),
pending_open_device_id_(-1) { pending_open_device_id_(-1) {
...@@ -83,12 +82,7 @@ void PepperPlatformVideoCapture::DetachEventHandler() { ...@@ -83,12 +82,7 @@ void PepperPlatformVideoCapture::DetachEventHandler() {
handler_ = NULL; handler_ = NULL;
StopCapture(NULL); StopCapture(NULL);
if (video_capture_) { video_capture_.reset();
VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager();
manager->RemoveDevice(session_id_, handler_proxy_.get());
video_capture_ = NULL;
}
if (render_view_.get()) { if (render_view_.get()) {
if (!label_.empty()) { if (!label_.empty()) {
...@@ -147,7 +141,7 @@ PepperPlatformVideoCapture::~PepperPlatformVideoCapture() { ...@@ -147,7 +141,7 @@ PepperPlatformVideoCapture::~PepperPlatformVideoCapture() {
void PepperPlatformVideoCapture::Initialize() { void PepperPlatformVideoCapture::Initialize() {
VideoCaptureImplManager* manager = VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager(); RenderThreadImpl::current()->video_capture_impl_manager();
video_capture_ = manager->AddDevice(session_id_, handler_proxy_.get()); video_capture_ = manager->UseDevice(session_id_);
} }
void PepperPlatformVideoCapture::OnDeviceOpened(int request_id, void PepperPlatformVideoCapture::OnDeviceOpened(int request_id,
......
...@@ -24,6 +24,7 @@ namespace content { ...@@ -24,6 +24,7 @@ namespace content {
class PepperMediaDeviceManager; class PepperMediaDeviceManager;
class PepperVideoCaptureHost; class PepperVideoCaptureHost;
class RenderViewImpl; class RenderViewImpl;
class VideoCaptureHandle;
class PepperPlatformVideoCapture class PepperPlatformVideoCapture
: public media::VideoCapture, : public media::VideoCapture,
...@@ -80,7 +81,7 @@ class PepperPlatformVideoCapture ...@@ -80,7 +81,7 @@ class PepperPlatformVideoCapture
PepperVideoCaptureHost* handler_; PepperVideoCaptureHost* handler_;
media::VideoCapture* video_capture_; scoped_ptr<VideoCaptureHandle> video_capture_;
// StartCapture() must be balanced by StopCapture(), otherwise this object // StartCapture() must be balanced by StopCapture(), otherwise this object
// will leak. // will leak.
......
...@@ -347,7 +347,7 @@ void RenderThreadImpl::Init() { ...@@ -347,7 +347,7 @@ void RenderThreadImpl::Init() {
db_message_filter_ = new DBMessageFilter(); db_message_filter_ = new DBMessageFilter();
AddFilter(db_message_filter_.get()); AddFilter(db_message_filter_.get());
vc_manager_ = new VideoCaptureImplManager(); vc_manager_.reset(new VideoCaptureImplManager());
AddFilter(vc_manager_->video_capture_message_filter()); AddFilter(vc_manager_->video_capture_message_filter());
#if defined(ENABLE_WEBRTC) #if defined(ENABLE_WEBRTC)
...@@ -361,7 +361,7 @@ void RenderThreadImpl::Init() { ...@@ -361,7 +361,7 @@ void RenderThreadImpl::Init() {
webrtc_identity_service_.reset(new WebRTCIdentityService()); webrtc_identity_service_.reset(new WebRTCIdentityService());
media_stream_factory_.reset(new MediaStreamDependencyFactory( media_stream_factory_.reset(new MediaStreamDependencyFactory(
vc_manager_.get(), p2p_socket_dispatcher_.get())); p2p_socket_dispatcher_.get()));
AddObserver(media_stream_factory_.get()); AddObserver(media_stream_factory_.get());
#endif // defined(ENABLE_WEBRTC) #endif // defined(ENABLE_WEBRTC)
...@@ -464,7 +464,13 @@ void RenderThreadImpl::Shutdown() { ...@@ -464,7 +464,13 @@ void RenderThreadImpl::Shutdown() {
RemoveFilter(audio_message_filter_.get()); RemoveFilter(audio_message_filter_.get());
audio_message_filter_ = NULL; audio_message_filter_ = NULL;
// |media_stream_factory_| produces users of |vc_manager_| so it must be
// destroyed first.
#if defined(ENABLE_WEBRTC)
media_stream_factory_.reset();
#endif
RemoveFilter(vc_manager_->video_capture_message_filter()); RemoveFilter(vc_manager_->video_capture_message_filter());
vc_manager_.reset();
RemoveFilter(db_message_filter_.get()); RemoveFilter(db_message_filter_.get());
db_message_filter_ = NULL; db_message_filter_ = NULL;
......
...@@ -426,8 +426,8 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, ...@@ -426,8 +426,8 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread,
// Dispatches all P2P sockets. // Dispatches all P2P sockets.
scoped_refptr<P2PSocketDispatcher> p2p_socket_dispatcher_; scoped_refptr<P2PSocketDispatcher> p2p_socket_dispatcher_;
// Used on multiple threads. // Used on the render thread.
scoped_refptr<VideoCaptureImplManager> vc_manager_; scoped_ptr<VideoCaptureImplManager> vc_manager_;
// The count of RenderWidgets running through this thread. // The count of RenderWidgets running through this thread.
int widget_count_; int widget_count_;
......
...@@ -1219,6 +1219,8 @@ ...@@ -1219,6 +1219,8 @@
'base/test_helpers.h', 'base/test_helpers.h',
'filters/mock_gpu_video_accelerator_factories.cc', 'filters/mock_gpu_video_accelerator_factories.cc',
'filters/mock_gpu_video_accelerator_factories.h', 'filters/mock_gpu_video_accelerator_factories.h',
'video/capture/mock_video_capture_event_handler.cc',
'video/capture/mock_video_capture_event_handler.h',
'video/mock_video_decode_accelerator.cc', 'video/mock_video_decode_accelerator.cc',
'video/mock_video_decode_accelerator.h', 'video/mock_video_decode_accelerator.h',
], ],
......
// Copyright 2014 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 "media/video/capture/mock_video_capture_event_handler.h"
namespace media {
MockVideoCaptureEventHandler::MockVideoCaptureEventHandler() {
}
MockVideoCaptureEventHandler::~MockVideoCaptureEventHandler() {
}
} // namespace media
// Copyright 2014 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.
#ifndef MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_EVENT_HANDLER_H_
#define MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_EVENT_HANDLER_H_
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "media/video/capture/video_capture.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
class MockVideoCaptureEventHandler : public VideoCapture::EventHandler {
public:
MockVideoCaptureEventHandler();
virtual ~MockVideoCaptureEventHandler();
// EventHandler implementation.
MOCK_METHOD1(OnStarted, void(VideoCapture* capture));
MOCK_METHOD1(OnStopped, void(VideoCapture* capture));
MOCK_METHOD1(OnPaused, void(VideoCapture* capture));
MOCK_METHOD2(OnError, void(VideoCapture* capture, int error_code));
MOCK_METHOD1(OnRemoved, void(VideoCapture* capture));
MOCK_METHOD2(OnFrameReady,
void(VideoCapture* capture,
const scoped_refptr<VideoFrame>& frame));
MOCK_METHOD2(OnDeviceInfoReceived,
void(VideoCapture* capture,
const VideoCaptureFormat& device_info));
};
} // namespace media
#endif // MEDIA_VIDEO_CAPTURE_VIDEO_CAPTURE_EVENT_HANDLER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment