summaryrefslogtreecommitdiffstats
path: root/content/renderer/media
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-28 08:56:20 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-28 08:56:20 +0000
commitfac792746fd46e9b64aedf45e0094200b8aed9db (patch)
treea48a1a5a708c0eea3cf87c342240e304d8ae9a98 /content/renderer/media
parent15443b7fa4263b624816023e249814885f1a31ec (diff)
downloadchromium_src-fac792746fd46e9b64aedf45e0094200b8aed9db.zip
chromium_src-fac792746fd46e9b64aedf45e0094200b8aed9db.tar.gz
chromium_src-fac792746fd46e9b64aedf45e0094200b8aed9db.tar.bz2
Refactor video capturing code in the render process
This is a large refactoring to cleanup the code that handles video capturing in the render process. The goal of this change is to: * Simplify threading model for objects involved. * Clarify ownership model. * Remove extra complexity caused by media::VideoCapture. Summary of this change: * Interface media::VideoCapture is removed completely. This interface doesn't add much value. It fails to define threading model and ownership. Some of the methods are obsolete. * Pepper code that performs video capturing now do not inherit from media::VideoCapture. The inheritance is not necessary * VideoCaptureImpl is now a purely IO thread object. VideoCaptureImpl can only be accessed on the IO thread. It now becomes and inner object of VideoCaptureImplManager. Client is not allowed to access this object directly. This helps remove code that accepts call from the render thread and hopping to the IO thread. This also makes cleanup much simpler. * VideoCaptureHandle is removed. The function of VideoCaptureHandle, i.e. handle cleanup of video capture resource is now folded into VideoCaptureImplManager. It's function is now replaced by a closure. * VideoCaptureImplManager becomes the public interface for accessing video capture device and start/stop capture. It takes VideoCaptureImpl as an internal object and sheild it from clients. We can now perform cleanup to prevent leak. Also ensures VideoCaptureImpl objects are deleted on the IO thread. * VideoFrames delivery done using callback insteasd of an interface. Using callback to deliver VideoFrames and state changes make thread hopping much simpler. Clients no longer need to provide an EventHandler interface. * Net deleted 450 lines of code. Tested with apprtc.appspot.com and example pepper plugin. Additional test to verify there's no leakage of VideoCaptureImpl objects. BUG=335327, 362558 Review URL: https://codereview.chromium.org/242013002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266492 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer/media')
-rw-r--r--content/renderer/media/media_stream_video_capture_source_unittest.cc14
-rw-r--r--content/renderer/media/media_stream_video_capturer_source.cc157
-rw-r--r--content/renderer/media/media_stream_video_capturer_source.h68
-rw-r--r--content/renderer/media/media_stream_video_source.cc3
-rw-r--r--content/renderer/media/media_stream_video_source.h4
-rw-r--r--content/renderer/media/media_stream_video_source_unittest.cc11
-rw-r--r--content/renderer/media/video_capture_impl.cc280
-rw-r--r--content/renderer/media/video_capture_impl.h111
-rw-r--r--content/renderer/media/video_capture_impl_manager.cc195
-rw-r--r--content/renderer/media/video_capture_impl_manager.h106
-rw-r--r--content/renderer/media/video_capture_impl_manager_unittest.cc90
-rw-r--r--content/renderer/media/video_capture_impl_unittest.cc256
-rw-r--r--content/renderer/media/video_capture_message_filter.h2
-rw-r--r--content/renderer/media/webrtc/media_stream_remote_video_source.cc2
-rw-r--r--content/renderer/media/webrtc/video_destination_handler.cc3
15 files changed, 680 insertions, 622 deletions
diff --git a/content/renderer/media/media_stream_video_capture_source_unittest.cc b/content/renderer/media/media_stream_video_capture_source_unittest.cc
index ea203a2..f721070 100644
--- a/content/renderer/media/media_stream_video_capture_source_unittest.cc
+++ b/content/renderer/media/media_stream_video_capture_source_unittest.cc
@@ -17,11 +17,11 @@ class MockVideoCapturerDelegate : public VideoCapturerDelegate {
explicit MockVideoCapturerDelegate(const StreamDeviceInfo& device_info)
: VideoCapturerDelegate(device_info) {}
- MOCK_METHOD3(StartDeliver,
+ MOCK_METHOD3(StartCapture,
void(const media::VideoCaptureParams& params,
- const NewFrameCallback& new_frame_callback,
+ const VideoCaptureDeliverFrameCB& new_frame_callback,
const StartedCallback& started_callback));
- MOCK_METHOD0(StopDeliver,void());
+ MOCK_METHOD0(StopCapture,void());
private:
virtual ~MockVideoCapturerDelegate() {}
@@ -68,13 +68,13 @@ TEST_F(MediaStreamVideoCapturerSourceTest, TabCaptureAllowResolutionChange) {
device_info.device.type = MEDIA_TAB_VIDEO_CAPTURE;
InitWithDeviceInfo(device_info);
- EXPECT_CALL(*delegate_, StartDeliver(
+ EXPECT_CALL(*delegate_, StartCapture(
testing::Field(&media::VideoCaptureParams::allow_resolution_change, true),
testing::_,
testing::_)).Times(1);
blink::WebMediaStreamTrack track = StartSource();
// When the track goes out of scope, the source will be stopped.
- EXPECT_CALL(*delegate_, StopDeliver());
+ EXPECT_CALL(*delegate_, StopCapture());
}
TEST_F(MediaStreamVideoCapturerSourceTest,
@@ -83,13 +83,13 @@ TEST_F(MediaStreamVideoCapturerSourceTest,
device_info.device.type = MEDIA_DESKTOP_VIDEO_CAPTURE;
InitWithDeviceInfo(device_info);
- EXPECT_CALL(*delegate_, StartDeliver(
+ EXPECT_CALL(*delegate_, StartCapture(
testing::Field(&media::VideoCaptureParams::allow_resolution_change, true),
testing::_,
testing::_)).Times(1);
blink::WebMediaStreamTrack track = StartSource();
// When the track goes out of scope, the source will be stopped.
- EXPECT_CALL(*delegate_, StopDeliver());
+ EXPECT_CALL(*delegate_, StopCapture());
}
} // namespace content
diff --git a/content/renderer/media/media_stream_video_capturer_source.cc b/content/renderer/media/media_stream_video_capturer_source.cc
index b6bc73a..0eb3104 100644
--- a/content/renderer/media/media_stream_video_capturer_source.cc
+++ b/content/renderer/media/media_stream_video_capturer_source.cc
@@ -5,9 +5,11 @@
#include "content/renderer/media/media_stream_video_capturer_source.h"
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/location.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/render_thread_impl.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h"
namespace {
@@ -38,24 +40,27 @@ VideoCapturerDelegate::VideoCapturerDelegate(
device_info.device.type == MEDIA_DESKTOP_VIDEO_CAPTURE),
got_first_frame_(false) {
DVLOG(3) << "VideoCapturerDelegate::ctor";
- // RenderThreadImpl::current() may be NULL in testing.
+
+ // NULL in unit test.
if (RenderThreadImpl::current()) {
- capture_engine_ = RenderThreadImpl::current()->video_capture_impl_manager()
- ->UseDevice(device_info.session_id);
- DCHECK(capture_engine_);
+ VideoCaptureImplManager* manager =
+ RenderThreadImpl::current()->video_capture_impl_manager();
+ if (manager)
+ release_device_cb_ = manager->UseDevice(session_id_);
}
- message_loop_proxy_ = base::MessageLoopProxy::current();
}
VideoCapturerDelegate::~VideoCapturerDelegate() {
DVLOG(3) << "VideoCapturerDelegate::dtor";
DCHECK(new_frame_callback_.is_null());
+ if (!release_device_cb_.is_null())
+ release_device_cb_.Run();
}
void VideoCapturerDelegate::GetCurrentSupportedFormats(
int max_requested_width,
int max_requested_height,
- const SupportedFormatsCallback& callback) {
+ const VideoCaptureDeviceFormatsCB& callback) {
DVLOG(3) << "GetCurrentSupportedFormats("
<< " { max_requested_height = " << max_requested_height << "})"
<< " { max_requested_width = " << max_requested_width << "})";
@@ -75,85 +80,64 @@ void VideoCapturerDelegate::GetCurrentSupportedFormats(
return;
}
+ // NULL in unit test.
+ if (!RenderThreadImpl::current())
+ return;
+ VideoCaptureImplManager* manager =
+ RenderThreadImpl::current()->video_capture_impl_manager();
+ if (!manager)
+ return;
DCHECK(source_formats_callback_.is_null());
source_formats_callback_ = callback;
- capture_engine_->GetDeviceFormatsInUse(base::Bind(
- &VideoCapturerDelegate::OnDeviceFormatsInUseReceived, this));
+ manager->GetDeviceFormatsInUse(
+ session_id_,
+ media::BindToCurrentLoop(
+ base::Bind(
+ &VideoCapturerDelegate::OnDeviceFormatsInUseReceived, this)));
}
-void VideoCapturerDelegate::StartDeliver(
+void VideoCapturerDelegate::StartCapture(
const media::VideoCaptureParams& params,
- const NewFrameCallback& new_frame_callback,
+ const VideoCaptureDeliverFrameCB& new_frame_callback,
const StartedCallback& started_callback) {
DCHECK(params.requested_format.IsValid());
- DCHECK(message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
new_frame_callback_ = new_frame_callback;
started_callback_ = started_callback;
got_first_frame_ = false;
- // Increase the reference count to ensure the object is not deleted until
- // it is unregistered in VideoCapturerDelegate::OnRemoved.
- AddRef();
- capture_engine_->StartCapture(this, params);
+ // NULL in unit test.
+ if (!RenderThreadImpl::current())
+ return;
+ VideoCaptureImplManager* manager =
+ RenderThreadImpl::current()->video_capture_impl_manager();
+ if (!manager)
+ return;
+ stop_capture_cb_ =
+ manager->StartCapture(
+ session_id_,
+ params,
+ media::BindToCurrentLoop(base::Bind(
+ &VideoCapturerDelegate::OnStateUpdateOnRenderThread, this)),
+ media::BindToCurrentLoop(base::Bind(
+ &VideoCapturerDelegate::OnFrameReadyOnRenderThread, this)));
}
-void VideoCapturerDelegate::StopDeliver() {
+void VideoCapturerDelegate::StopCapture() {
// Immediately make sure we don't provide more frames.
- DVLOG(3) << "VideoCapturerDelegate::StopDeliver()";
- DCHECK(message_loop_proxy_->BelongsToCurrentThread());
- capture_engine_->StopCapture(this);
+ DVLOG(3) << "VideoCapturerDelegate::StopCapture()";
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!stop_capture_cb_.is_null()) {
+ base::ResetAndReturn(&stop_capture_cb_).Run();
+ }
new_frame_callback_.Reset();
started_callback_.Reset();
source_formats_callback_.Reset();
}
-void VideoCapturerDelegate::OnStarted(media::VideoCapture* capture) {
- DVLOG(3) << "VideoCapturerDelegate::OnStarted";
- DCHECK(!message_loop_proxy_->BelongsToCurrentThread());
-}
-
-void VideoCapturerDelegate::OnStopped(media::VideoCapture* capture) {
- DCHECK(!message_loop_proxy_->BelongsToCurrentThread());
-}
-
-void VideoCapturerDelegate::OnPaused(media::VideoCapture* capture) {
- DCHECK(!message_loop_proxy_->BelongsToCurrentThread());
-}
-
-void VideoCapturerDelegate::OnError(media::VideoCapture* capture,
- int error_code) {
- DVLOG(3) << "VideoCapturerDelegate::OnError";
- DCHECK(!message_loop_proxy_->BelongsToCurrentThread());
- message_loop_proxy_->PostTask(
- FROM_HERE,
- base::Bind(&VideoCapturerDelegate::OnErrorOnRenderThread, this, capture));
-}
-
-void VideoCapturerDelegate::OnRemoved(media::VideoCapture* capture) {
- DVLOG(3) << " MediaStreamVideoCapturerSource::OnRemoved";
- DCHECK(!message_loop_proxy_->BelongsToCurrentThread());
-
- // Balance the AddRef in StartDeliver.
- // This means we are no longer registered as an event handler and can safely
- // be deleted.
- Release();
-}
-
-void VideoCapturerDelegate::OnFrameReady(
- media::VideoCapture* capture,
- const scoped_refptr<media::VideoFrame>& frame) {
- DCHECK(!message_loop_proxy_->BelongsToCurrentThread());
- message_loop_proxy_->PostTask(
- FROM_HERE,
- base::Bind(&VideoCapturerDelegate::OnFrameReadyOnRenderThread,
- this,
- capture,
- frame));
-}
-
void VideoCapturerDelegate::OnFrameReadyOnRenderThread(
- media::VideoCapture* capture,
- const scoped_refptr<media::VideoFrame>& frame) {
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format) {
if (!got_first_frame_) {
got_first_frame_ = true;
if (!started_callback_.is_null())
@@ -161,41 +145,54 @@ void VideoCapturerDelegate::OnFrameReadyOnRenderThread(
}
if (!new_frame_callback_.is_null()) {
- new_frame_callback_.Run(frame);
+ new_frame_callback_.Run(frame, format);
}
}
-void VideoCapturerDelegate::OnErrorOnRenderThread(
- media::VideoCapture* capture) {
- if (!started_callback_.is_null())
+void VideoCapturerDelegate::OnStateUpdateOnRenderThread(
+ VideoCaptureState state) {
+ if (state == VIDEO_CAPTURE_STATE_ERROR && !started_callback_.is_null()) {
started_callback_.Run(false);
+ }
}
void VideoCapturerDelegate::OnDeviceFormatsInUseReceived(
const media::VideoCaptureFormats& formats_in_use) {
DVLOG(3) << "OnDeviceFormatsInUseReceived: " << formats_in_use.size();
- DCHECK(message_loop_proxy_ == base::MessageLoopProxy::current());
- // StopDeliver() might have destroyed |source_formats_callback_| before
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // StopCapture() might have destroyed |source_formats_callback_| before
// arriving here.
if (source_formats_callback_.is_null())
return;
+ // If there are no formats in use, try to retrieve the whole list of
+ // supported form.
if (!formats_in_use.empty()) {
source_formats_callback_.Run(formats_in_use);
source_formats_callback_.Reset();
- } else {
- // If there are no formats in use, try to retrieve the whole list of
- // supported formats.
- capture_engine_->GetDeviceSupportedFormats(base::Bind(
- &VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated, this));
+ return;
}
+
+ // NULL in unit test.
+ if (!RenderThreadImpl::current())
+ return;
+ VideoCaptureImplManager* manager =
+ RenderThreadImpl::current()->video_capture_impl_manager();
+ if (!manager)
+ return;
+ manager->GetDeviceSupportedFormats(
+ session_id_,
+ media::BindToCurrentLoop(
+ base::Bind(
+ &VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated,
+ this)));
}
void VideoCapturerDelegate::OnDeviceSupportedFormatsEnumerated(
const media::VideoCaptureFormats& formats) {
DVLOG(3) << "OnDeviceSupportedFormatsEnumerated: " << formats.size()
<< " received";
- DCHECK(message_loop_proxy_ == base::MessageLoopProxy::current());
- // StopDeliver() might have destroyed |source_formats_callback_| before
+ DCHECK(thread_checker_.CalledOnValidThread());
+ // StopCapture() might have destroyed |source_formats_callback_| before
// arriving here.
if (source_formats_callback_.is_null())
return;
@@ -246,7 +243,7 @@ void MediaStreamVideoCapturerSource::StartSourceImpl(
device_info().device.type == MEDIA_DESKTOP_VIDEO_CAPTURE) {
new_params.allow_resolution_change = true;
}
- delegate_->StartDeliver(
+ delegate_->StartCapture(
new_params,
base::Bind(&MediaStreamVideoCapturerSource::DeliverVideoFrame,
base::Unretained(this)),
@@ -255,7 +252,7 @@ void MediaStreamVideoCapturerSource::StartSourceImpl(
}
void MediaStreamVideoCapturerSource::StopSourceImpl() {
- delegate_->StopDeliver();
+ delegate_->StopCapture();
}
} // namespace content
diff --git a/content/renderer/media/media_stream_video_capturer_source.h b/content/renderer/media/media_stream_video_capturer_source.h
index b719887..0fbef5d 100644
--- a/content/renderer/media/media_stream_video_capturer_source.h
+++ b/content/renderer/media/media_stream_video_capturer_source.h
@@ -7,26 +7,21 @@
#include "base/callback.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/thread_checker.h"
#include "content/common/media/video_capture.h"
#include "content/renderer/media/media_stream_video_source.h"
-#include "media/video/capture/video_capture.h"
namespace content {
-class VideoCaptureHandle;
-
// VideoCapturerDelegate is a delegate used by MediaStreamVideoCapturerSource
// for local video capturer. It uses VideoCaptureImplManager to start / stop
// and receive I420 frames from Chrome's video capture implementation.
+//
+// This is a render thread only object.
class CONTENT_EXPORT VideoCapturerDelegate
- : public media::VideoCapture::EventHandler,
- public base::RefCountedThreadSafe<VideoCapturerDelegate> {
+ : public base::RefCountedThreadSafe<VideoCapturerDelegate> {
public:
- typedef base::Callback<void(const scoped_refptr<media::VideoFrame>&)>
- NewFrameCallback;
typedef base::Callback<void(bool running)> StartedCallback;
- typedef base::Callback<void(const media::VideoCaptureFormats& formats)>
- SupportedFormatsCallback;
explicit VideoCapturerDelegate(
const StreamDeviceInfo& device_info);
@@ -38,33 +33,20 @@ class CONTENT_EXPORT VideoCapturerDelegate
virtual void GetCurrentSupportedFormats(
int max_requested_width,
int max_requested_height,
- const SupportedFormatsCallback& callback);
+ const VideoCaptureDeviceFormatsCB& callback);
- // Starts deliver frames using the resolution in |params|.
+ // Starts capturing frames using the resolution in |params|.
// |new_frame_callback| is triggered when a new video frame is available.
// |started_callback| is triggered before the first video frame is received
// or if the underlying video capturer fails to start.
- virtual void StartDeliver(
+ virtual void StartCapture(
const media::VideoCaptureParams& params,
- const NewFrameCallback& new_frame_callback,
+ const VideoCaptureDeliverFrameCB& new_frame_callback,
const StartedCallback& started_callback);
- // Stops deliver frames and clears all callbacks including the
+ // Stops capturing frames and clears all callbacks including the
// SupportedFormatsCallback callback.
- virtual void StopDeliver();
-
- protected:
- // media::VideoCapture::EventHandler implementation.
- // These functions are called on the IO thread (same as where
- // |capture_engine_| runs).
- virtual void OnStarted(media::VideoCapture* capture) OVERRIDE;
- virtual void OnStopped(media::VideoCapture* capture) OVERRIDE;
- virtual void OnPaused(media::VideoCapture* capture) OVERRIDE;
- virtual void OnError(media::VideoCapture* capture, int error_code) OVERRIDE;
- virtual void OnRemoved(media::VideoCapture* capture) OVERRIDE;
- virtual void OnFrameReady(
- media::VideoCapture* capture,
- const scoped_refptr<media::VideoFrame>& frame) OVERRIDE;
+ virtual void StopCapture();
private:
friend class base::RefCountedThreadSafe<VideoCapturerDelegate>;
@@ -73,9 +55,9 @@ class CONTENT_EXPORT VideoCapturerDelegate
virtual ~VideoCapturerDelegate();
void OnFrameReadyOnRenderThread(
- media::VideoCapture* capture,
- const scoped_refptr<media::VideoFrame>& frame);
- void OnErrorOnRenderThread(media::VideoCapture* capture);
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format);
+ void OnStateUpdateOnRenderThread(VideoCaptureState state);
void OnDeviceFormatsInUseReceived(const media::VideoCaptureFormats& formats);
void OnDeviceSupportedFormatsEnumerated(
const media::VideoCaptureFormats& formats);
@@ -83,27 +65,31 @@ class CONTENT_EXPORT VideoCapturerDelegate
// The id identifies which video capture device is used for this video
// capture session.
media::VideoCaptureSessionId session_id_;
- scoped_ptr<VideoCaptureHandle> capture_engine_;
+ base::Closure release_device_cb_;
+ base::Closure stop_capture_cb_;
bool is_screen_cast_;
-
- // Accessed on the thread where StartDeliver is called.
bool got_first_frame_;
- // |new_frame_callback_| is provided to this class in StartDeliver and must be
- // valid until StopDeliver is called.
- NewFrameCallback new_frame_callback_;
- // |started_callback| is provided to this class in StartDeliver and must be
+ // |new_frame_callback_| is provided to this class in StartToDeliver and must
+ // be valid until StopDeliver is called.
+ VideoCaptureDeliverFrameCB new_frame_callback_;
+ // |started_callback| is provided to this class in StartToDeliver and must be
// valid until StopDeliver is called.
StartedCallback started_callback_;
- // Message loop of the caller of StartDeliver.
- scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
- SupportedFormatsCallback source_formats_callback_;
+ VideoCaptureDeviceFormatsCB source_formats_callback_;
+
+ // Bound to the render thread.
+ base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(VideoCapturerDelegate);
};
+// Owned by WebMediaStreamSource in Blink as a representation of a video
+// stream coming from a camera.
+// This is a render thread only object. All methods must be called on the
+// render thread.
class CONTENT_EXPORT MediaStreamVideoCapturerSource
: public MediaStreamVideoSource {
public:
diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc
index 28cc3e2..64d3184 100644
--- a/content/renderer/media/media_stream_video_source.cc
+++ b/content/renderer/media/media_stream_video_source.cc
@@ -368,7 +368,8 @@ void MediaStreamVideoSource::DoStopSource() {
}
void MediaStreamVideoSource::DeliverVideoFrame(
- const scoped_refptr<media::VideoFrame>& frame) {
+ const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format) {
DCHECK(CalledOnValidThread());
scoped_refptr<media::VideoFrame> video_frame(frame);
diff --git a/content/renderer/media/media_stream_video_source.h b/content/renderer/media/media_stream_video_source.h
index 5c5289d..933017c 100644
--- a/content/renderer/media/media_stream_video_source.h
+++ b/content/renderer/media/media_stream_video_source.h
@@ -82,7 +82,9 @@ class CONTENT_EXPORT MediaStreamVideoSource
// Delivers |frame| to registered tracks according to their constraints.
// Note: current implementation assumes |frame| be contiguous layout of image
// planes and I420.
- virtual void DeliverVideoFrame(const scoped_refptr<media::VideoFrame>& frame);
+ // |format| contains extra information like the frame rate of this source.
+ virtual void DeliverVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
+ const media::VideoCaptureFormat& format);
// An implementation must fetch the formats that can currently be used by
// the source and call OnSupportedFormats when done.
diff --git a/content/renderer/media/media_stream_video_source_unittest.cc b/content/renderer/media/media_stream_video_source_unittest.cc
index c6e4bf8..1274cc5 100644
--- a/content/renderer/media/media_stream_video_source_unittest.cc
+++ b/content/renderer/media/media_stream_video_source_unittest.cc
@@ -106,7 +106,7 @@ class MediaStreamVideoSourceTest
scoped_refptr<media::VideoFrame> frame =
media::VideoFrame::CreateBlackFrame(gfx::Size(capture_width,
capture_height));
- mock_source()->DeliverVideoFrame(frame);
+ mock_source()->DeliverVideoFrame(frame, media::VideoCaptureFormat());
EXPECT_EQ(1, sink.number_of_frames());
// Expect the delivered frame to be cropped.
@@ -418,7 +418,8 @@ TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) {
{
scoped_refptr<media::VideoFrame> frame1 =
media::VideoFrame::CreateBlackFrame(gfx::Size(320, 240));
- mock_source()->DeliverVideoFrame(frame1);
+ mock_source()->DeliverVideoFrame(frame1,
+ media::VideoCaptureFormat());
}
EXPECT_EQ(1, sink.number_of_frames());
// Expect the delivered frame to be passed unchanged since its smaller than
@@ -429,7 +430,8 @@ TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) {
{
scoped_refptr<media::VideoFrame> frame2 =
media::VideoFrame::CreateBlackFrame(gfx::Size(640, 480));
- mock_source()->DeliverVideoFrame(frame2);
+ mock_source()->DeliverVideoFrame(frame2,
+ media::VideoCaptureFormat());
}
EXPECT_EQ(2, sink.number_of_frames());
// Expect the delivered frame to be passed unchanged since its smaller than
@@ -440,7 +442,8 @@ TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) {
{
scoped_refptr<media::VideoFrame> frame3 =
media::VideoFrame::CreateBlackFrame(gfx::Size(1280, 720));
- mock_source()->DeliverVideoFrame(frame3);
+ mock_source()->DeliverVideoFrame(frame3,
+ media::VideoCaptureFormat());
}
EXPECT_EQ(3, sink.number_of_frames());
// Expect a frame to be cropped since its larger than max requested.
diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc
index b9a61f3..bda203a 100644
--- a/content/renderer/media/video_capture_impl.cc
+++ b/content/renderer/media/video_capture_impl.cc
@@ -1,6 +1,13 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+//
+// Notes about usage of this object by VideoCaptureImplManager.
+//
+// VideoCaptureImplManager access this object by using a Unretained()
+// binding and tasks on the IO thread. It is then important that
+// VideoCaptureImpl never post task to itself. All operations must be
+// synchronous.
#include "content/renderer/media/video_capture_impl.h"
@@ -32,130 +39,81 @@ class VideoCaptureImpl::ClientBuffer
DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
};
-bool VideoCaptureImpl::CaptureStarted() {
- return state_ == VIDEO_CAPTURE_STATE_STARTED;
-}
-
-int VideoCaptureImpl::CaptureFrameRate() {
- return last_frame_format_.frame_rate;
-}
+VideoCaptureImpl::ClientInfo::ClientInfo() {}
+VideoCaptureImpl::ClientInfo::~ClientInfo() {}
VideoCaptureImpl::VideoCaptureImpl(
const media::VideoCaptureSessionId session_id,
VideoCaptureMessageFilter* filter)
- : VideoCapture(),
- message_filter_(filter),
- io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
+ : message_filter_(filter),
device_id_(0),
session_id_(session_id),
suspended_(false),
state_(VIDEO_CAPTURE_STATE_STOPPED),
weak_factory_(this) {
DCHECK(filter);
+ thread_checker_.DetachFromThread();
}
-VideoCaptureImpl::~VideoCaptureImpl() {}
-
-void VideoCaptureImpl::Init() {
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureImpl::InitOnIOThread,
- base::Unretained(this)));
-}
-
-void VideoCaptureImpl::DeInit(base::Closure done_cb) {
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureImpl::DeInitOnIOThread,
- base::Unretained(this),
- done_cb));
-}
-
-void VideoCaptureImpl::SuspendCapture(bool suspend) {
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureImpl::SuspendCaptureOnIOThread,
- base::Unretained(this),
- suspend));
-}
-
-void VideoCaptureImpl::StartCapture(
- media::VideoCapture::EventHandler* handler,
- const media::VideoCaptureParams& params) {
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureImpl::StartCaptureOnIOThread,
- base::Unretained(this), handler, params));
-}
-
-void VideoCaptureImpl::StopCapture(
- media::VideoCapture::EventHandler* handler) {
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureImpl::StopCaptureOnIOThread,
- base::Unretained(this), handler));
-}
-
-void VideoCaptureImpl::GetDeviceSupportedFormats(
- const DeviceFormatsCallback& callback) {
- DCHECK(!callback.is_null());
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormatsOnIOThread,
- base::Unretained(this), media::BindToCurrentLoop(callback)));
-}
-
-void VideoCaptureImpl::GetDeviceFormatsInUse(
- const DeviceFormatsInUseCallback& callback) {
- DCHECK(!callback.is_null());
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUseOnIOThread,
- base::Unretained(this), media::BindToCurrentLoop(callback)));
+VideoCaptureImpl::~VideoCaptureImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
}
-void VideoCaptureImpl::InitOnIOThread() {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+void VideoCaptureImpl::Init() {
+ DCHECK(thread_checker_.CalledOnValidThread());
message_filter_->AddDelegate(this);
}
-void VideoCaptureImpl::DeInitOnIOThread(base::Closure done_cb) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+void VideoCaptureImpl::DeInit() {
+ DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == VIDEO_CAPTURE_STATE_STARTED)
Send(new VideoCaptureHostMsg_Stop(device_id_));
message_filter_->RemoveDelegate(this);
- done_cb.Run();
}
-void VideoCaptureImpl::SuspendCaptureOnIOThread(bool suspend) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+void VideoCaptureImpl::SuspendCapture(bool suspend) {
+ DCHECK(thread_checker_.CalledOnValidThread());
suspended_ = suspend;
}
-void VideoCaptureImpl::StartCaptureOnIOThread(
- media::VideoCapture::EventHandler* handler,
- const media::VideoCaptureParams& params) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+void VideoCaptureImpl::StartCapture(
+ int client_id,
+ const media::VideoCaptureParams& params,
+ const VideoCaptureStateUpdateCB& state_update_cb,
+ const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ ClientInfo client_info;
+ client_info.params = params;
+ client_info.state_update_cb = state_update_cb;
+ client_info.deliver_frame_cb = deliver_frame_cb;
+
if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
- handler->OnError(this, 1);
- handler->OnRemoved(this);
- } else if ((clients_pending_on_filter_.find(handler) !=
- clients_pending_on_filter_.end()) ||
- (clients_pending_on_restart_.find(handler) !=
- clients_pending_on_restart_.end()) ||
- clients_.find(handler) != clients_.end() ) {
- // This client has started.
+ state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
+ } else if (clients_pending_on_filter_.count(client_id) ||
+ clients_pending_on_restart_.count(client_id) ||
+ clients_.count(client_id)) {
+ LOG(FATAL) << "This client has already started.";
} else if (!device_id_) {
- clients_pending_on_filter_[handler] = params;
+ clients_pending_on_filter_[client_id] = client_info;
} else {
- handler->OnStarted(this);
+ // Note: |state_| might not be started at this point. But we tell
+ // client that we have started.
+ state_update_cb.Run(VIDEO_CAPTURE_STATE_STARTED);
if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
- clients_[handler] = params;
+ clients_[client_id] = client_info;
// TODO(sheu): Allowing resolution change will require that all
// outstanding clients of a capture session support resolution change.
DCHECK_EQ(params_.allow_resolution_change,
params.allow_resolution_change);
} else if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
- clients_pending_on_restart_[handler] = params;
+ clients_pending_on_restart_[client_id] = client_info;
DVLOG(1) << "StartCapture: Got new resolution "
<< params.requested_format.frame_size.ToString()
<< " during stopping.";
} else {
- clients_[handler] = params;
- DCHECK_EQ(1ul, clients_.size());
+ clients_[client_id] = client_info;
+ if (state_ == VIDEO_CAPTURE_STATE_STARTED)
+ return;
params_ = params;
if (params_.requested_format.frame_rate >
media::limits::kMaxFramesPerSecond) {
@@ -170,16 +128,18 @@ void VideoCaptureImpl::StartCaptureOnIOThread(
}
}
-void VideoCaptureImpl::StopCaptureOnIOThread(
- media::VideoCapture::EventHandler* handler) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+void VideoCaptureImpl::StopCapture(int client_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
- // A handler can be in only one client list.
- // If this handler is in any client list, we can just remove it from
+ // A client ID can be in only one client list.
+ // If this ID is in any client list, we can just remove it from
// that client list and don't have to run the other following RemoveClient().
- RemoveClient(handler, &clients_pending_on_filter_) ||
- RemoveClient(handler, &clients_pending_on_restart_) ||
- RemoveClient(handler, &clients_);
+ if (!RemoveClient(client_id, &clients_pending_on_filter_)) {
+ if (!RemoveClient(client_id, &clients_pending_on_restart_)) {
+ bool removed = RemoveClient(client_id, &clients_);
+ DCHECK(removed) << "Removing a non-existent client.";
+ }
+ }
if (clients_.empty()) {
DVLOG(1) << "StopCapture: No more client, stopping ...";
@@ -189,20 +149,20 @@ void VideoCaptureImpl::StopCaptureOnIOThread(
}
}
-void VideoCaptureImpl::GetDeviceSupportedFormatsOnIOThread(
- const DeviceFormatsCallback& callback) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
- device_formats_callback_queue_.push_back(callback);
- if (device_formats_callback_queue_.size() == 1)
+void VideoCaptureImpl::GetDeviceSupportedFormats(
+ const VideoCaptureDeviceFormatsCB& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ device_formats_cb_queue_.push_back(callback);
+ if (device_formats_cb_queue_.size() == 1)
Send(new VideoCaptureHostMsg_GetDeviceSupportedFormats(device_id_,
session_id_));
}
-void VideoCaptureImpl::GetDeviceFormatsInUseOnIOThread(
- const DeviceFormatsInUseCallback& callback) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
- device_formats_in_use_callback_queue_.push_back(callback);
- if (device_formats_in_use_callback_queue_.size() == 1)
+void VideoCaptureImpl::GetDeviceFormatsInUse(
+ const VideoCaptureDeviceFormatsCB& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ device_formats_in_use_cb_queue_.push_back(callback);
+ if (device_formats_in_use_cb_queue_.size() == 1)
Send(
new VideoCaptureHostMsg_GetDeviceFormatsInUse(device_id_, session_id_));
}
@@ -210,7 +170,7 @@ void VideoCaptureImpl::GetDeviceFormatsInUseOnIOThread(
void VideoCaptureImpl::OnBufferCreated(
base::SharedMemoryHandle handle,
int length, int buffer_id) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
// In case client calls StopCapture before the arrival of created buffer,
// just close this buffer and return.
@@ -234,7 +194,7 @@ void VideoCaptureImpl::OnBufferCreated(
}
void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
if (iter == client_buffers_.end())
@@ -248,7 +208,7 @@ void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
void VideoCaptureImpl::OnBufferReceived(int buffer_id,
const media::VideoCaptureFormat& format,
base::TimeTicks timestamp) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
// The capture pipeline supports only I420 for now.
DCHECK_EQ(format.pixel_format, media::PIXEL_FORMAT_I420);
@@ -289,8 +249,10 @@ void VideoCaptureImpl::OnBufferReceived(int buffer_id,
buffer,
base::Passed(scoped_ptr<gpu::MailboxHolder>().Pass()))));
- for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it)
- it->first->OnFrameReady(this, frame);
+ for (ClientInfoMap::iterator it = clients_.begin(); it != clients_.end();
+ ++it) {
+ it->second.deliver_frame_cb.Run(frame, format);
+ }
}
static void NullReadPixelsCB(const SkBitmap& bitmap) { NOTIMPLEMENTED(); }
@@ -300,7 +262,7 @@ void VideoCaptureImpl::OnMailboxBufferReceived(
const gpu::MailboxHolder& mailbox_holder,
const media::VideoCaptureFormat& format,
base::TimeTicks timestamp) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
Send(new VideoCaptureHostMsg_BufferReady(
@@ -325,24 +287,28 @@ void VideoCaptureImpl::OnMailboxBufferReceived(
timestamp - first_frame_timestamp_,
base::Bind(&NullReadPixelsCB));
- for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it)
- it->first->OnFrameReady(this, frame);
+ for (ClientInfoMap::iterator it = clients_.begin(); it != clients_.end();
+ ++it) {
+ it->second.deliver_frame_cb.Run(frame, format);
+ }
}
void VideoCaptureImpl::OnClientBufferFinished(
int buffer_id,
const scoped_refptr<ClientBuffer>& /* ignored_buffer */,
scoped_ptr<gpu::MailboxHolder> mailbox_holder) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
const uint32 sync_point = (mailbox_holder ? mailbox_holder->sync_point : 0);
Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id, sync_point));
}
void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
switch (state) {
case VIDEO_CAPTURE_STATE_STARTED:
+ // Camera has started in the browser process. Since we have already
+ // told all clients that we have started there's nothing to do.
break;
case VIDEO_CAPTURE_STATE_STOPPED:
state_ = VIDEO_CAPTURE_STATE_STOPPED;
@@ -353,27 +319,26 @@ void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
RestartCapture();
break;
case VIDEO_CAPTURE_STATE_PAUSED:
- for (ClientInfo::iterator it = clients_.begin();
+ for (ClientInfoMap::iterator it = clients_.begin();
it != clients_.end(); ++it) {
- it->first->OnPaused(this);
+ it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_PAUSED);
}
break;
case VIDEO_CAPTURE_STATE_ERROR:
DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_;
- for (ClientInfo::iterator it = clients_.begin();
+ for (ClientInfoMap::iterator it = clients_.begin();
it != clients_.end(); ++it) {
- // TODO(wjia): browser process would send error code.
- it->first->OnError(this, 1);
- it->first->OnRemoved(this);
+ it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
}
clients_.clear();
state_ = VIDEO_CAPTURE_STATE_ERROR;
break;
case VIDEO_CAPTURE_STATE_ENDED:
DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_;
- for (ClientInfo::iterator it = clients_.begin();
+ for (ClientInfoMap::iterator it = clients_.begin();
it != clients_.end(); ++it) {
- it->first->OnRemoved(this);
+ // We'll only notify the client that the stream has stopped.
+ it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
}
clients_.clear();
state_ = VIDEO_CAPTURE_STATE_ENDED;
@@ -385,36 +350,41 @@ void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
void VideoCaptureImpl::OnDeviceSupportedFormatsEnumerated(
const media::VideoCaptureFormats& supported_formats) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
- for (size_t i = 0; i < device_formats_callback_queue_.size(); ++i)
- device_formats_callback_queue_[i].Run(supported_formats);
- device_formats_callback_queue_.clear();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (size_t i = 0; i < device_formats_cb_queue_.size(); ++i)
+ device_formats_cb_queue_[i].Run(supported_formats);
+ device_formats_cb_queue_.clear();
}
void VideoCaptureImpl::OnDeviceFormatsInUseReceived(
const media::VideoCaptureFormats& formats_in_use) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
- for (size_t i = 0; i < device_formats_in_use_callback_queue_.size(); ++i)
- device_formats_in_use_callback_queue_[i].Run(formats_in_use);
- device_formats_in_use_callback_queue_.clear();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (size_t i = 0; i < device_formats_in_use_cb_queue_.size(); ++i)
+ device_formats_in_use_cb_queue_[i].Run(formats_in_use);
+ device_formats_in_use_cb_queue_.clear();
}
void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
DVLOG(1) << "OnDelegateAdded: device_id " << device_id;
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
device_id_ = device_id;
- for (ClientInfo::iterator it = clients_pending_on_filter_.begin();
+ for (ClientInfoMap::iterator it = clients_pending_on_filter_.begin();
it != clients_pending_on_filter_.end(); ) {
- media::VideoCapture::EventHandler* handler = it->first;
- const media::VideoCaptureParams params = it->second;
+ int client_id = it->first;
+ VideoCaptureStateUpdateCB state_update_cb =
+ it->second.state_update_cb;
+ VideoCaptureDeliverFrameCB deliver_frame_cb =
+ it->second.deliver_frame_cb;
+ const media::VideoCaptureParams params = it->second.params;
clients_pending_on_filter_.erase(it++);
- StartCapture(handler, params);
+ StartCapture(client_id, params, state_update_cb,
+ deliver_frame_cb);
}
}
void VideoCaptureImpl::StopDevice() {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
state_ = VIDEO_CAPTURE_STATE_STOPPING;
@@ -424,22 +394,20 @@ void VideoCaptureImpl::StopDevice() {
}
void VideoCaptureImpl::RestartCapture() {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
int width = 0;
int height = 0;
- for (ClientInfo::iterator it = clients_.begin();
+ clients_.insert(clients_pending_on_restart_.begin(),
+ clients_pending_on_restart_.end());
+ clients_pending_on_restart_.clear();
+ for (ClientInfoMap::iterator it = clients_.begin();
it != clients_.end(); ++it) {
- width = std::max(width, it->second.requested_format.frame_size.width());
- height = std::max(height, it->second.requested_format.frame_size.height());
- }
- for (ClientInfo::iterator it = clients_pending_on_restart_.begin();
- it != clients_pending_on_restart_.end(); ) {
- width = std::max(width, it->second.requested_format.frame_size.width());
- height = std::max(height, it->second.requested_format.frame_size.height());
- clients_[it->first] = it->second;
- clients_pending_on_restart_.erase(it++);
+ width = std::max(width,
+ it->second.params.requested_format.frame_size.width());
+ height = std::max(height,
+ it->second.params.requested_format.frame_size.height());
}
params_.requested_format.frame_size.SetSize(width, height);
DVLOG(1) << "RestartCapture, "
@@ -448,7 +416,7 @@ void VideoCaptureImpl::RestartCapture() {
}
void VideoCaptureImpl::StartCaptureInternal() {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(device_id_);
Send(new VideoCaptureHostMsg_Start(device_id_, session_id_, params_));
@@ -456,21 +424,17 @@ void VideoCaptureImpl::StartCaptureInternal() {
}
void VideoCaptureImpl::Send(IPC::Message* message) {
- io_message_loop_proxy_->PostTask(FROM_HERE,
- base::Bind(base::IgnoreResult(&VideoCaptureMessageFilter::Send),
- message_filter_.get(), message));
+ DCHECK(thread_checker_.CalledOnValidThread());
+ message_filter_->Send(message);
}
-bool VideoCaptureImpl::RemoveClient(
- media::VideoCapture::EventHandler* handler,
- ClientInfo* clients) {
- DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+bool VideoCaptureImpl::RemoveClient(int client_id, ClientInfoMap* clients) {
+ DCHECK(thread_checker_.CalledOnValidThread());
bool found = false;
- ClientInfo::iterator it = clients->find(handler);
+ ClientInfoMap::iterator it = clients->find(client_id);
if (it != clients->end()) {
- handler->OnStopped(this);
- handler->OnRemoved(this);
+ it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
clients->erase(it);
found = true;
}
diff --git a/content/renderer/media/video_capture_impl.h b/content/renderer/media/video_capture_impl.h
index 7988669..66c393c 100644
--- a/content/renderer/media/video_capture_impl.h
+++ b/content/renderer/media/video_capture_impl.h
@@ -10,20 +10,12 @@
// operation of a capture device to the browser process and receives responses
// from browser process.
//
-// All public methods of VideoCaptureImpl can be called on any thread.
-// Internally it runs on the IO thread. Clients of this class implement
-// interface media::VideoCapture::EventHandler which is called only on the IO
-// thread.
+// VideoCaptureImpl is an IO thread only object. See the comments in
+// video_capture_impl_manager.cc for the lifetime of this object.
+// All methods must be called on the IO thread.
//
-// Implementation note: tasks are posted bound to Unretained(this) to the I/O
-// thread and this is safe (even though the I/O thread is scoped to the renderer
-// process) because VideoCaptureImplManager only triggers deletion of its
-// VideoCaptureImpl's by calling DeInit which detours through the I/O thread, so
-// 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.
+// This is an internal class used by VideoCaptureImplManager only. Do not access
+// this directly.
#ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
@@ -32,10 +24,10 @@
#include <map>
#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
#include "content/common/media/video_capture.h"
#include "content/renderer/media/video_capture_message_filter.h"
-#include "media/video/capture/video_capture.h"
#include "media/video/capture/video_capture_types.h"
namespace base {
@@ -46,35 +38,52 @@ namespace gpu {
struct MailboxHolder;
} // namespace gpu
+namespace media {
+class VideoFrame;
+} // namespace media
+
namespace content {
class CONTENT_EXPORT VideoCaptureImpl
- : public media::VideoCapture, public VideoCaptureMessageFilter::Delegate {
+ : public VideoCaptureMessageFilter::Delegate {
public:
+ virtual ~VideoCaptureImpl();
+
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 listening to IPC messages.
+ void DeInit();
// Stop/resume delivering video frames to clients, based on flag |suspend|.
void SuspendCapture(bool suspend);
- // media::VideoCapture interface.
- virtual void StartCapture(
- media::VideoCapture::EventHandler* handler,
- const media::VideoCaptureParams& params) OVERRIDE;
- virtual void StopCapture(media::VideoCapture::EventHandler* handler) OVERRIDE;
- virtual bool CaptureStarted() OVERRIDE;
- virtual int CaptureFrameRate() OVERRIDE;
- virtual void GetDeviceSupportedFormats(
- const DeviceFormatsCallback& callback) OVERRIDE;
- virtual void GetDeviceFormatsInUse(
- const DeviceFormatsInUseCallback& callback) OVERRIDE;
+ // Start capturing using the provided parameters.
+ // |client_id| must be unique to this object in the render process. It is
+ // used later to stop receiving video frames.
+ // |state_update_cb| will be called when state changes.
+ // |deliver_frame_cb| will be called when a frame is ready.
+ void StartCapture(
+ int client_id,
+ const media::VideoCaptureParams& params,
+ const VideoCaptureStateUpdateCB& state_update_cb,
+ const VideoCaptureDeliverFrameCB& deliver_frame_cb);
+
+ // Stop capturing. |client_id| is the identifier used to call StartCapture.
+ void StopCapture(int client_id);
+
+ // Get capturing formats supported by this device.
+ // |callback| will be invoked with the results.
+ void GetDeviceSupportedFormats(
+ const VideoCaptureDeviceFormatsCB& callback);
+
+ // Get capturing formats currently in use by this device.
+ // |callback| will be invoked with the results.
+ void GetDeviceFormatsInUse(
+ const VideoCaptureDeviceFormatsCB& callback);
media::VideoCaptureSessionId session_id() const { return session_id_; }
@@ -82,21 +91,20 @@ class CONTENT_EXPORT VideoCaptureImpl
friend class VideoCaptureImplTest;
friend class MockVideoCaptureImpl;
+ // Carries a shared memory for transferring video frames from browser to
+ // renderer.
class ClientBuffer;
- typedef std::map<media::VideoCapture::EventHandler*,
- media::VideoCaptureParams> ClientInfo;
-
- void InitOnIOThread();
- void DeInitOnIOThread(base::Closure done_cb);
- void SuspendCaptureOnIOThread(bool suspend);
- void StartCaptureOnIOThread(
- media::VideoCapture::EventHandler* handler,
- const media::VideoCaptureParams& params);
- void StopCaptureOnIOThread(media::VideoCapture::EventHandler* handler);
- void GetDeviceSupportedFormatsOnIOThread(
- const DeviceFormatsCallback& callback);
- void GetDeviceFormatsInUseOnIOThread(
- const DeviceFormatsInUseCallback& callback);
+
+ // Contains information for a video capture client. Including parameters
+ // for capturing and callbacks to the client.
+ struct ClientInfo {
+ ClientInfo();
+ ~ClientInfo();
+ media::VideoCaptureParams params;
+ VideoCaptureStateUpdateCB state_update_cb;
+ VideoCaptureDeliverFrameCB deliver_frame_cb;
+ };
+ typedef std::map<int, ClientInfo> ClientInfoMap;
// VideoCaptureMessageFilter::Delegate interface.
virtual void OnBufferCreated(base::SharedMemoryHandle handle,
@@ -130,28 +138,26 @@ class CONTENT_EXPORT VideoCaptureImpl
virtual void Send(IPC::Message* message);
// Helpers.
- bool RemoveClient(media::VideoCapture::EventHandler* handler,
- ClientInfo* clients);
+ bool RemoveClient(int client_id, ClientInfoMap* clients);
const scoped_refptr<VideoCaptureMessageFilter> message_filter_;
- const scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
int device_id_;
const int session_id_;
// Vector of callbacks to be notified of device format enumerations, used only
// on IO Thread.
- std::vector<DeviceFormatsCallback> device_formats_callback_queue_;
+ std::vector<VideoCaptureDeviceFormatsCB> device_formats_cb_queue_;
// Vector of callbacks to be notified of a device's in use capture format(s),
// used only on IO Thread.
- std::vector<DeviceFormatsInUseCallback> device_formats_in_use_callback_queue_;
+ std::vector<VideoCaptureDeviceFormatsCB> device_formats_in_use_cb_queue_;
// Buffers available for sending to the client.
typedef std::map<int32, scoped_refptr<ClientBuffer> > ClientBufferMap;
ClientBufferMap client_buffers_;
- ClientInfo clients_;
- ClientInfo clients_pending_on_filter_;
- ClientInfo clients_pending_on_restart_;
+ ClientInfoMap clients_;
+ ClientInfoMap clients_pending_on_filter_;
+ ClientInfoMap clients_pending_on_restart_;
// Member params_ represents the video format requested by the
// client to this class via StartCapture().
@@ -166,6 +172,9 @@ class CONTENT_EXPORT VideoCaptureImpl
bool suspended_;
VideoCaptureState state_;
+ // |weak_factory_| and |thread_checker_| are bound to the IO thread.
+ base::ThreadChecker thread_checker_;
+
// WeakPtrFactory pointing back to |this| object, for use with
// media::VideoFrames constructed in OnBufferReceived() from buffers cached
// in |client_buffers_|.
diff --git a/content/renderer/media/video_capture_impl_manager.cc b/content/renderer/media/video_capture_impl_manager.cc
index 07460fb..686e6ad 100644
--- a/content/renderer/media/video_capture_impl_manager.cc
+++ b/content/renderer/media/video_capture_impl_manager.cc
@@ -1,95 +1,155 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+//
+// Implementation notes about interactions with VideoCaptureImpl.
+//
+// How is VideoCaptureImpl used:
+//
+// VideoCaptureImpl is an IO thread object while VideoCaptureImplManager
+// lives only on the render thread. It is only possible to access an
+// object of VideoCaptureImpl via a task on the IO thread.
+//
+// How is VideoCaptureImpl deleted:
+//
+// A task is posted to the IO thread to delete a VideoCaptureImpl.
+// Immediately after that the pointer to it is dropped. This means no
+// access to this VideoCaptureImpl object is possible on the render
+// thread. Also note that VideoCaptureImpl does not post task to itself.
+//
+// The use of Unretained:
+//
+// We make sure deletion is the last task on the IO thread for a
+// VideoCaptureImpl object. This allows the use of Unretained() binding.
#include "content/renderer/media/video_capture_impl_manager.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "content/public/renderer/render_thread.h"
+#include "content/child/child_process.h"
#include "content/renderer/media/video_capture_impl.h"
#include "content/renderer/media/video_capture_message_filter.h"
#include "media/base/bind_to_current_loop.h"
namespace content {
-VideoCaptureHandle::VideoCaptureHandle(
- media::VideoCapture* impl, base::Closure destruction_cb)
- : impl_(impl), destruction_cb_(destruction_cb) {
-}
-
-VideoCaptureHandle::~VideoCaptureHandle() {
- destruction_cb_.Run();
-}
-
-void VideoCaptureHandle::StartCapture(
- EventHandler* handler,
- const media::VideoCaptureParams& params) {
- impl_->StartCapture(handler, params);
-}
-
-void VideoCaptureHandle::StopCapture(EventHandler* handler) {
- impl_->StopCapture(handler);
-}
-
-bool VideoCaptureHandle::CaptureStarted() {
- return impl_->CaptureStarted();
-}
-
-int VideoCaptureHandle::CaptureFrameRate() {
- return impl_->CaptureFrameRate();
-}
-
-void VideoCaptureHandle::GetDeviceSupportedFormats(
- const DeviceFormatsCallback& callback) {
- impl_->GetDeviceSupportedFormats(callback);
-}
-
-void VideoCaptureHandle::GetDeviceFormatsInUse(
- const DeviceFormatsInUseCallback& callback) {
- impl_->GetDeviceFormatsInUse(callback);
-}
-
VideoCaptureImplManager::VideoCaptureImplManager()
- : filter_(new VideoCaptureMessageFilter()),
+ : next_client_id_(0),
+ filter_(new VideoCaptureMessageFilter()),
weak_factory_(this) {
}
VideoCaptureImplManager::~VideoCaptureImplManager() {
DCHECK(thread_checker_.CalledOnValidThread());
+ if (devices_.empty())
+ return;
+ // Forcibly release all video capture resources.
+ for (VideoCaptureDeviceMap::iterator it = devices_.begin();
+ it != devices_.end(); ++it) {
+ VideoCaptureImpl* impl = it->second.second;
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::DeInit,
+ base::Unretained(impl)));
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&base::DeletePointer<VideoCaptureImpl>,
+ base::Unretained(impl)));
+ }
+ devices_.clear();
}
-scoped_ptr<VideoCaptureHandle> VideoCaptureImplManager::UseDevice(
+base::Closure VideoCaptureImplManager::UseDevice(
media::VideoCaptureSessionId id) {
DCHECK(thread_checker_.CalledOnValidThread());
- VideoCaptureImpl* video_capture_device = NULL;
+ VideoCaptureImpl* impl = NULL;
VideoCaptureDeviceMap::iterator it = devices_.find(id);
if (it == devices_.end()) {
- video_capture_device = CreateVideoCaptureImpl(id, filter_.get());
- devices_[id] =
- std::make_pair(1, linked_ptr<VideoCaptureImpl>(video_capture_device));
- video_capture_device->Init();
+ impl = CreateVideoCaptureImplForTesting(id, filter_.get());
+ if (!impl)
+ impl = new VideoCaptureImpl(id, filter_.get());
+ devices_[id] = std::make_pair(1, impl);
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::Init,
+ base::Unretained(impl)));
} else {
++it->second.first;
- video_capture_device = it->second.second.get();
}
+ return base::Bind(&VideoCaptureImplManager::UnrefDevice,
+ weak_factory_.GetWeakPtr(), id);
+}
- // This callback ensures UnrefDevice() happens on the render thread.
- return scoped_ptr<VideoCaptureHandle>(
- new VideoCaptureHandle(
- video_capture_device,
- media::BindToCurrentLoop(
- base::Bind(
- &VideoCaptureImplManager::UnrefDevice,
- weak_factory_.GetWeakPtr(),
- id))));
+base::Closure VideoCaptureImplManager::StartCapture(
+ media::VideoCaptureSessionId id,
+ const media::VideoCaptureParams& params,
+ const VideoCaptureStateUpdateCB& state_update_cb,
+ const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ VideoCaptureDeviceMap::iterator it = devices_.find(id);
+ DCHECK(it != devices_.end());
+ VideoCaptureImpl* impl = it->second.second;
+
+ // This ID is used to identify a client of VideoCaptureImpl.
+ const int client_id = ++next_client_id_;
+
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::StartCapture,
+ base::Unretained(impl),
+ client_id,
+ params,
+ state_update_cb,
+ deliver_frame_cb));
+ return base::Bind(&VideoCaptureImplManager::StopCapture,
+ weak_factory_.GetWeakPtr(),
+ client_id, id);
+}
+
+void VideoCaptureImplManager::GetDeviceSupportedFormats(
+ media::VideoCaptureSessionId id,
+ const VideoCaptureDeviceFormatsCB& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ VideoCaptureDeviceMap::iterator it = devices_.find(id);
+ DCHECK(it != devices_.end());
+ VideoCaptureImpl* impl = it->second.second;
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::GetDeviceSupportedFormats,
+ base::Unretained(impl), callback));
+}
+
+void VideoCaptureImplManager::GetDeviceFormatsInUse(
+ media::VideoCaptureSessionId id,
+ const VideoCaptureDeviceFormatsCB& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ VideoCaptureDeviceMap::iterator it = devices_.find(id);
+ DCHECK(it != devices_.end());
+ VideoCaptureImpl* impl = it->second.second;
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::GetDeviceFormatsInUse,
+ base::Unretained(impl), callback));
}
-VideoCaptureImpl* VideoCaptureImplManager::CreateVideoCaptureImpl(
+VideoCaptureImpl*
+VideoCaptureImplManager::CreateVideoCaptureImplForTesting(
media::VideoCaptureSessionId id,
VideoCaptureMessageFilter* filter) const {
- return new VideoCaptureImpl(id, filter);
+ return NULL;
+}
+
+void VideoCaptureImplManager::StopCapture(
+ int client_id, media::VideoCaptureSessionId id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ VideoCaptureDeviceMap::iterator it = devices_.find(id);
+ DCHECK(it != devices_.end());
+ VideoCaptureImpl* impl = it->second.second;
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::StopCapture,
+ base::Unretained(impl), client_id));
}
void VideoCaptureImplManager::UnrefDevice(
@@ -97,21 +157,34 @@ void VideoCaptureImplManager::UnrefDevice(
DCHECK(thread_checker_.CalledOnValidThread());
VideoCaptureDeviceMap::iterator it = devices_.find(id);
DCHECK(it != devices_.end());
+ VideoCaptureImpl* impl = it->second.second;
+ // Unref and destroy on the IO thread if there's no more client.
DCHECK(it->second.first);
--it->second.first;
if (!it->second.first) {
- VideoCaptureImpl* impl = it->second.second.release();
devices_.erase(id);
- impl->DeInit(base::Bind(&base::DeletePointer<VideoCaptureImpl>, impl));
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::DeInit,
+ base::Unretained(impl)));
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&base::DeletePointer<VideoCaptureImpl>,
+ base::Unretained(impl)));
}
}
void VideoCaptureImplManager::SuspendDevices(bool suspend) {
DCHECK(thread_checker_.CalledOnValidThread());
for (VideoCaptureDeviceMap::iterator it = devices_.begin();
- it != devices_.end(); ++it)
- it->second.second->SuspendCapture(suspend);
+ it != devices_.end(); ++it) {
+ VideoCaptureImpl* impl = it->second.second;
+ ChildProcess::current()->io_message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoCaptureImpl::SuspendCapture,
+ base::Unretained(impl), suspend));
+ }
}
} // namespace content
diff --git a/content/renderer/media/video_capture_impl_manager.h b/content/renderer/media/video_capture_impl_manager.h
index 6b02825..a03f609 100644
--- a/content/renderer/media/video_capture_impl_manager.h
+++ b/content/renderer/media/video_capture_impl_manager.h
@@ -2,17 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// VideoCaptureImplManager owns VideoCaptureImpl objects. Clients who
-// want access to a video capture device call UseDevice() to get a handle
-// to VideoCaptureImpl.
+// TODO(hclam): This class should be renamed to VideoCaptureService.
+
+// This class provides access to a video capture device in the browser
+// process through IPC. The main function is to deliver video frames
+// to a client.
//
// 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.
+// VideoFrames are delivered on the IO thread. Callbacks provided by
+// a client are also called on the IO thread.
#ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_
#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_MANAGER_H_
@@ -21,60 +23,67 @@
#include "base/callback.h"
#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
-#include "media/video/capture/video_capture.h"
+#include "content/common/media/video_capture.h"
+#include "media/video/capture/video_capture_types.h"
namespace content {
class VideoCaptureImpl;
-class VideoCaptureImplManager;
class VideoCaptureMessageFilter;
-// Thread-safe wrapper for a media::VideoCapture object. During
-// 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:
- virtual ~VideoCaptureHandle();
-
- // media::VideoCapture implementations.
- virtual void StartCapture(
- EventHandler* handler,
- const media::VideoCaptureParams& params) OVERRIDE;
- virtual void StopCapture(EventHandler* handler) OVERRIDE;
- virtual bool CaptureStarted() OVERRIDE;
- virtual int CaptureFrameRate() OVERRIDE;
- virtual void GetDeviceSupportedFormats(
- const DeviceFormatsCallback& callback) OVERRIDE;
- virtual void GetDeviceFormatsInUse(
- const DeviceFormatsInUseCallback& callback) OVERRIDE;
-
- private:
- friend class VideoCaptureImplManager;
-
- VideoCaptureHandle(media::VideoCapture* impl,
- base::Closure destruction_cb);
-
- media::VideoCapture* impl_;
- base::Closure destruction_cb_;
-
- 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);
+ // Open a device associated with the session ID.
+ // This method must be called before any methods with the same ID
+ // is used.
+ // Returns a callback that should be used to release the acquired
+ // resources.
+ base::Closure UseDevice(media::VideoCaptureSessionId id);
+
+ // Start receiving video frames for the given session ID.
+ //
+ // |state_update_cb| will be called on the IO thread when capturing
+ // state changes.
+ // States will be one of the following four:
+ // * VIDEO_CAPTURE_STATE_STARTED
+ // * VIDEO_CAPTURE_STATE_STOPPED
+ // * VIDEO_CAPTURE_STATE_PAUSED
+ // * VIDEO_CAPTURE_STATE_ERROR
+ //
+ // |deliver_frame_cb| will be called on the IO thread when a video
+ // frame is ready.
+ //
+ // Returns a callback that is used to stop capturing. Note that stopping
+ // video capture is not synchronous. Client should handle the case where
+ // callbacks are called after capturing is instructed to stop, typically
+ // by binding the passed callbacks on a WeakPtr.
+ base::Closure StartCapture(
+ media::VideoCaptureSessionId id,
+ const media::VideoCaptureParams& params,
+ const VideoCaptureStateUpdateCB& state_update_cb,
+ const VideoCaptureDeliverFrameCB& deliver_frame_cb);
+
+ // Get supported formats supported by the device for the given session
+ // ID. |callback| will be called on the IO thread.
+ void GetDeviceSupportedFormats(
+ media::VideoCaptureSessionId id,
+ const VideoCaptureDeviceFormatsCB& callback);
+
+ // Get supported formats currently in use for the given session ID.
+ // |callback| will be called on the IO thread.
+ void GetDeviceFormatsInUse(
+ media::VideoCaptureSessionId id,
+ const VideoCaptureDeviceFormatsCB& callback);
// Make all existing VideoCaptureImpl instances stop/resume delivering
// video frames to their clients, depends on flag |suspend|.
@@ -85,20 +94,27 @@ class CONTENT_EXPORT VideoCaptureImplManager {
}
protected:
- // Used in tests to inject a mock VideoCaptureImpl.
- virtual VideoCaptureImpl* CreateVideoCaptureImpl(
+ virtual VideoCaptureImpl* CreateVideoCaptureImplForTesting(
media::VideoCaptureSessionId id,
VideoCaptureMessageFilter* filter) const;
private:
+ void StopCapture(int client_id, media::VideoCaptureSessionId id);
void UnrefDevice(media::VideoCaptureSessionId id);
// The int is used to count clients of the corresponding VideoCaptureImpl.
+ // VideoCaptureImpl objects are owned by this object. But they are
+ // destroyed on the IO thread. These are raw pointers because we destroy
+ // them manually.
typedef std::map<media::VideoCaptureSessionId,
- std::pair<int, linked_ptr<VideoCaptureImpl> > >
+ std::pair<int, VideoCaptureImpl*> >
VideoCaptureDeviceMap;
VideoCaptureDeviceMap devices_;
+ // This is an internal ID for identifying clients of VideoCaptureImpl.
+ // The ID is global for the render process.
+ int next_client_id_;
+
scoped_refptr<VideoCaptureMessageFilter> filter_;
// Bound to the render thread.
diff --git a/content/renderer/media/video_capture_impl_manager_unittest.cc b/content/renderer/media/video_capture_impl_manager_unittest.cc
index 98f2b56..86cc56a 100644
--- a/content/renderer/media/video_capture_impl_manager_unittest.cc
+++ b/content/renderer/media/video_capture_impl_manager_unittest.cc
@@ -12,7 +12,6 @@
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/media/video_capture_message_filter.h"
#include "media/base/bind_to_current_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"
@@ -20,7 +19,6 @@ using ::testing::_;
using ::testing::DoAll;
using ::testing::SaveArg;
using media::BindToCurrentLoop;
-using media::MockVideoCaptureEventHandler;
namespace content {
@@ -53,9 +51,10 @@ class MockVideoCaptureImplManager : public VideoCaptureImplManager {
base::Closure destruct_video_capture_callback)
: destruct_video_capture_callback_(
destruct_video_capture_callback) {}
+ virtual ~MockVideoCaptureImplManager() {}
protected:
- virtual VideoCaptureImpl* CreateVideoCaptureImpl(
+ virtual VideoCaptureImpl* CreateVideoCaptureImplForTesting(
media::VideoCaptureSessionId id,
VideoCaptureMessageFilter* filter) const OVERRIDE {
return new MockVideoCaptureImpl(id,
@@ -72,7 +71,8 @@ class MockVideoCaptureImplManager : public VideoCaptureImplManager {
class VideoCaptureImplManagerTest : public ::testing::Test {
public:
VideoCaptureImplManagerTest()
- : manager_(BindToCurrentLoop(cleanup_run_loop_.QuitClosure())) {
+ : manager_(new MockVideoCaptureImplManager(
+ BindToCurrentLoop(cleanup_run_loop_.QuitClosure()))) {
params_.requested_format = media::VideoCaptureFormat(
gfx::Size(176, 144), 30, media::PIXEL_FORMAT_I420);
child_process_.reset(new ChildProcess());
@@ -89,15 +89,43 @@ class VideoCaptureImplManagerTest : public ::testing::Test {
base::Unretained(this)));
return;
}
- manager_.video_capture_message_filter()->OnFilterAdded(NULL);
+ manager_->video_capture_message_filter()->OnFilterAdded(NULL);
}
protected:
+ MOCK_METHOD2(OnFrameReady,
+ void(const scoped_refptr<media::VideoFrame>&,
+ const media::VideoCaptureFormat&));
+ MOCK_METHOD0(OnStarted, void());
+ MOCK_METHOD0(OnStopped, void());
+
+ void OnStateUpdate(VideoCaptureState state) {
+ switch (state) {
+ case VIDEO_CAPTURE_STATE_STARTED:
+ OnStarted();
+ break;
+ case VIDEO_CAPTURE_STATE_STOPPED:
+ OnStopped();
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ base::Closure StartCapture(const media::VideoCaptureParams& params) {
+ return manager_->StartCapture(
+ 0, params,
+ base::Bind(&VideoCaptureImplManagerTest::OnStateUpdate,
+ base::Unretained(this)),
+ base::Bind(&VideoCaptureImplManagerTest::OnFrameReady,
+ base::Unretained(this)));
+ }
+
base::MessageLoop message_loop_;
scoped_ptr<ChildProcess> child_process_;
media::VideoCaptureParams params_;
base::RunLoop cleanup_run_loop_;
- MockVideoCaptureImplManager manager_;
+ scoped_ptr<MockVideoCaptureImplManager> manager_;
private:
DISALLOW_COPY_AND_ASSIGN(VideoCaptureImplManagerTest);
@@ -106,30 +134,18 @@ class VideoCaptureImplManagerTest : public ::testing::Test {
// 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::Closure release_cb1 = manager_->UseDevice(0);
+ base::Closure release_cb2 = manager_->UseDevice(0);
+ base::Closure stop_cb1, stop_cb2;
{
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_);
+ EXPECT_CALL(*this, OnStarted()).WillOnce(
+ RunClosure(quit_closure));
+ EXPECT_CALL(*this, OnStarted()).RetiresOnSaturation();
+ stop_cb1 = StartCapture(params_);
+ stop_cb2 = StartCapture(params_);
FakeChannelSetup();
run_loop.Run();
}
@@ -138,20 +154,22 @@ TEST_F(VideoCaptureImplManagerTest, MultipleClients) {
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(
+ EXPECT_CALL(*this, OnStopped()).WillOnce(
RunClosure(quit_closure));
- handle1->StopCapture(client1.get());
- handle2->StopCapture(client2.get());
+ EXPECT_CALL(*this, OnStopped()).RetiresOnSaturation();
+ stop_cb1.Run();
+ stop_cb2.Run();
run_loop.Run();
}
- EXPECT_TRUE(device1 == device2);
- handle1.reset();
- handle2.reset();
+ release_cb1.Run();
+ release_cb2.Run();
+ cleanup_run_loop_.Run();
+}
+
+TEST_F(VideoCaptureImplManagerTest, NoLeak) {
+ manager_->UseDevice(0).Reset();
+ manager_.reset();
cleanup_run_loop_.Run();
}
diff --git a/content/renderer/media/video_capture_impl_unittest.cc b/content/renderer/media/video_capture_impl_unittest.cc
index 61893c0..13c71af 100644
--- a/content/renderer/media/video_capture_impl_unittest.cc
+++ b/content/renderer/media/video_capture_impl_unittest.cc
@@ -3,12 +3,10 @@
// found in the LICENSE file.
#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
#include "content/child/child_process.h"
#include "content/common/media/video_capture_messages.h"
#include "content/renderer/media/video_capture_impl.h"
#include "media/base/bind_to_current_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"
@@ -17,7 +15,6 @@ using ::testing::AtLeast;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using ::testing::SaveArg;
-using media::MockVideoCaptureEventHandler;
namespace content {
@@ -72,6 +69,7 @@ class VideoCaptureImplTest : public ::testing::Test {
media::VideoCaptureSessionId session_id,
const media::VideoCaptureParams& params) {
OnStateChanged(VIDEO_CAPTURE_STATE_STARTED);
+ capture_params_ = params;
}
void DevicePauseCapture(int device_id) {}
@@ -96,6 +94,13 @@ class VideoCaptureImplTest : public ::testing::Test {
media::VideoCaptureSessionId session_id) {
OnDeviceFormatsInUseReceived(media::VideoCaptureFormats());
}
+
+ const media::VideoCaptureParams& capture_params() const {
+ return capture_params_;
+ }
+
+ private:
+ media::VideoCaptureParams capture_params_;
};
VideoCaptureImplTest() {
@@ -110,23 +115,68 @@ class VideoCaptureImplTest : public ::testing::Test {
message_filter_ = new MockVideoCaptureMessageFilter;
session_id_ = 1;
- video_capture_impl_ = new MockVideoCaptureImpl(
- session_id_, message_filter_.get());
+ video_capture_impl_.reset(new MockVideoCaptureImpl(
+ session_id_, message_filter_.get()));
video_capture_impl_->device_id_ = 2;
}
virtual ~VideoCaptureImplTest() {
- delete video_capture_impl_;
}
protected:
+ MOCK_METHOD2(OnFrameReady,
+ void(const scoped_refptr<media::VideoFrame>&,
+ const media::VideoCaptureFormat&));
+ MOCK_METHOD1(OnStateUpdate, void(VideoCaptureState));
+ MOCK_METHOD1(OnDeviceFormatsInUse,
+ void(const media::VideoCaptureFormats&));
+ MOCK_METHOD1(OnDeviceSupportedFormats,
+ void(const media::VideoCaptureFormats&));
+
+ void Init() {
+ video_capture_impl_->Init();
+ }
+
+ void StartCapture(int client_id,
+ const media::VideoCaptureParams& params) {
+ video_capture_impl_->StartCapture(
+ client_id, params,
+ base::Bind(&VideoCaptureImplTest::OnStateUpdate,
+ base::Unretained(this)),
+ base::Bind(&VideoCaptureImplTest::OnFrameReady,
+ base::Unretained(this)));
+ }
+
+ void StopCapture(int client_id) {
+ video_capture_impl_->StopCapture(client_id);
+ }
+
+ void DeInit() {
+ video_capture_impl_->DeInit();
+ }
+
+ void GetDeviceSupportedFormats() {
+ const base::Callback<void(const media::VideoCaptureFormats&)>
+ callback = base::Bind(
+ &VideoCaptureImplTest::OnDeviceSupportedFormats,
+ base::Unretained(this));
+ video_capture_impl_->GetDeviceSupportedFormats(callback);
+ }
+
+ void GetDeviceFormatsInUse() {
+ const base::Callback<void(const media::VideoCaptureFormats&)>
+ callback = base::Bind(
+ &VideoCaptureImplTest::OnDeviceFormatsInUse,
+ base::Unretained(this));
+ video_capture_impl_->GetDeviceFormatsInUse(callback);
+ }
+
base::MessageLoop message_loop_;
- base::RunLoop run_loop_;
scoped_ptr<ChildProcess> child_process_;
scoped_refptr<MockVideoCaptureMessageFilter> message_filter_;
media::VideoCaptureSessionId session_id_;
- MockVideoCaptureImpl* video_capture_impl_;
+ scoped_ptr<MockVideoCaptureImpl> video_capture_impl_;
media::VideoCaptureParams params_small_;
media::VideoCaptureParams params_large_;
@@ -136,155 +186,95 @@ class VideoCaptureImplTest : public ::testing::Test {
TEST_F(VideoCaptureImplTest, Simple) {
// Execute SetCapture() and StopCapture() for one client.
- scoped_ptr<MockVideoCaptureEventHandler> client(
- new MockVideoCaptureEventHandler);
-
- EXPECT_CALL(*client, OnStarted(_));
- EXPECT_CALL(*client, OnStopped(_));
- EXPECT_CALL(*client, OnRemoved(_));
-
- video_capture_impl_->StartCapture(client.get(), params_small_);
- video_capture_impl_->StopCapture(client.get());
- video_capture_impl_->DeInit(
- media::BindToCurrentLoop(run_loop_.QuitClosure()));
- run_loop_.Run();
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+
+ Init();
+ StartCapture(0, params_small_);
+ StopCapture(0);
+ DeInit();
}
TEST_F(VideoCaptureImplTest, TwoClientsInSequence) {
- // Execute SetCapture() and StopCapture() for 2 clients in sequence.
- scoped_ptr<MockVideoCaptureEventHandler> client1(
- new MockVideoCaptureEventHandler);
- scoped_ptr<MockVideoCaptureEventHandler> client2(
- new MockVideoCaptureEventHandler);
-
- EXPECT_CALL(*client1, OnStarted(_));
- EXPECT_CALL(*client1, OnStopped(_));
- EXPECT_CALL(*client1, OnRemoved(_));
- EXPECT_CALL(*client2, OnStarted(_));
- EXPECT_CALL(*client2, OnStopped(_));
- EXPECT_CALL(*client2, OnRemoved(_));
-
- video_capture_impl_->StartCapture(client1.get(), params_small_);
- video_capture_impl_->StopCapture(client1.get());
- video_capture_impl_->StartCapture(client2.get(), params_small_);
- video_capture_impl_->StopCapture(client2.get());
- video_capture_impl_->DeInit(
- media::BindToCurrentLoop(run_loop_.QuitClosure()));
- run_loop_.Run();
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED)).Times(2);
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+
+ Init();
+ StartCapture(0, params_small_);
+ StopCapture(0);
+ StartCapture(1, params_small_);
+ StopCapture(1);
+ DeInit();
}
TEST_F(VideoCaptureImplTest, LargeAndSmall) {
- // Execute SetCapture() and StopCapture() for 2 clients simultaneously.
- // The large client starts first and stops first.
- scoped_ptr<MockVideoCaptureEventHandler> client_small(
- new MockVideoCaptureEventHandler);
- scoped_ptr<MockVideoCaptureEventHandler> client_large(
- new MockVideoCaptureEventHandler);
-
- EXPECT_CALL(*client_large, OnStarted(_));
- EXPECT_CALL(*client_small, OnStarted(_));
- EXPECT_CALL(*client_large, OnStopped(_));
- EXPECT_CALL(*client_large, OnRemoved(_));
- EXPECT_CALL(*client_small, OnStopped(_));
- EXPECT_CALL(*client_small, OnRemoved(_));
-
- video_capture_impl_->StartCapture(client_large.get(), params_large_);
- video_capture_impl_->StartCapture(client_small.get(), params_small_);
- video_capture_impl_->StopCapture(client_large.get());
- video_capture_impl_->StopCapture(client_small.get());
- video_capture_impl_->DeInit(
- media::BindToCurrentLoop(run_loop_.QuitClosure()));
- run_loop_.Run();
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED)).Times(2);
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+
+ Init();
+ StartCapture(0, params_large_);
+ StopCapture(0);
+ StartCapture(1, params_small_);
+ StopCapture(1);
+ DeInit();
}
TEST_F(VideoCaptureImplTest, SmallAndLarge) {
- // Execute SetCapture() and StopCapture() for 2 clients simultaneously.
- // The small client starts first and stops first.
- scoped_ptr<MockVideoCaptureEventHandler> client_small(
- new MockVideoCaptureEventHandler);
- scoped_ptr<MockVideoCaptureEventHandler> client_large(
- new MockVideoCaptureEventHandler);
-
- EXPECT_CALL(*client_small, OnStarted(_));
- EXPECT_CALL(*client_large, OnStarted(_));
- EXPECT_CALL(*client_small, OnStopped(_));
- EXPECT_CALL(*client_small, OnRemoved(_));
- EXPECT_CALL(*client_large, OnStopped(_));
- EXPECT_CALL(*client_large, OnRemoved(_));
-
- video_capture_impl_->StartCapture(client_small.get(), params_small_);
- video_capture_impl_->StartCapture(client_large.get(), params_large_);
- video_capture_impl_->StopCapture(client_small.get());
- video_capture_impl_->StopCapture(client_large.get());
- video_capture_impl_->DeInit(
- media::BindToCurrentLoop(run_loop_.QuitClosure()));
- run_loop_.Run();
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED)).Times(2);
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+
+ Init();
+ StartCapture(0, params_small_);
+ StopCapture(0);
+ StartCapture(1, params_large_);
+ StopCapture(1);
+ DeInit();
}
// Check that a request to GetDeviceSupportedFormats() ends up eventually in the
// provided callback.
TEST_F(VideoCaptureImplTest, GetDeviceFormats) {
- scoped_ptr<MockVideoCaptureEventHandler> client(
- new MockVideoCaptureEventHandler);
-
- EXPECT_CALL(*client, OnDeviceSupportedFormatsEnumerated(_));
-
- const base::Callback<void(const media::VideoCaptureFormats&)>
- callback = base::Bind(
- &MockVideoCaptureEventHandler::OnDeviceSupportedFormatsEnumerated,
- base::Unretained(client.get()));
- video_capture_impl_->GetDeviceSupportedFormats(callback);
- video_capture_impl_->DeInit(
- media::BindToCurrentLoop(run_loop_.QuitClosure()));
- run_loop_.Run();
+ EXPECT_CALL(*this, OnDeviceSupportedFormats(_));
+
+ Init();
+ GetDeviceSupportedFormats();
+ DeInit();
}
// Check that two requests to GetDeviceSupportedFormats() end up eventually
// calling the provided callbacks.
TEST_F(VideoCaptureImplTest, TwoClientsGetDeviceFormats) {
- scoped_ptr<MockVideoCaptureEventHandler> client1(
- new MockVideoCaptureEventHandler);
- scoped_ptr<MockVideoCaptureEventHandler> client2(
- new MockVideoCaptureEventHandler);
-
- EXPECT_CALL(*client1, OnDeviceSupportedFormatsEnumerated(_));
- EXPECT_CALL(*client2, OnDeviceSupportedFormatsEnumerated(_));
-
- const base::Callback<void(const media::VideoCaptureFormats&)>
- callback1 = base::Bind(
- &MockVideoCaptureEventHandler::OnDeviceSupportedFormatsEnumerated,
- base::Unretained(client1.get()));
- const base::Callback<void(const media::VideoCaptureFormats&)>
- callback2 = base::Bind(
- &MockVideoCaptureEventHandler::OnDeviceSupportedFormatsEnumerated,
- base::Unretained(client2.get()));
-
- video_capture_impl_->GetDeviceSupportedFormats(callback1);
- video_capture_impl_->GetDeviceSupportedFormats(callback2);
- video_capture_impl_->DeInit(
- media::BindToCurrentLoop(run_loop_.QuitClosure()));
- run_loop_.Run();
+ EXPECT_CALL(*this, OnDeviceSupportedFormats(_)).Times(2);
+
+ Init();
+ GetDeviceSupportedFormats();
+ GetDeviceSupportedFormats();
+ DeInit();
}
// Check that a request to GetDeviceFormatsInUse() ends up eventually in the
// provided callback.
TEST_F(VideoCaptureImplTest, GetDeviceFormatsInUse) {
- scoped_ptr<MockVideoCaptureEventHandler> client(
- new MockVideoCaptureEventHandler);
-
- media::VideoCaptureFormats formats_in_use;
- EXPECT_CALL(*client, OnDeviceFormatsInUseReceived(_))
- .WillOnce(SaveArg<0>(&formats_in_use));
-
- const base::Callback<void(const media::VideoCaptureFormats&)> callback =
- base::Bind(&MockVideoCaptureEventHandler::OnDeviceFormatsInUseReceived,
- base::Unretained(client.get()));
- video_capture_impl_->GetDeviceFormatsInUse(callback);
- video_capture_impl_->DeInit(
- media::BindToCurrentLoop(run_loop_.QuitClosure()));
- run_loop_.Run();
-
- EXPECT_TRUE(formats_in_use.empty());
+ EXPECT_CALL(*this, OnDeviceFormatsInUse(_));
+
+ Init();
+ GetDeviceFormatsInUse();
+ DeInit();
+}
+
+TEST_F(VideoCaptureImplTest, AlreadyStarted) {
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED)).Times(2);
+ EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+
+ Init();
+ StartCapture(0, params_small_);
+ StartCapture(1, params_large_);
+ StopCapture(0);
+ StopCapture(1);
+ DeInit();
+ DCHECK(video_capture_impl_->capture_params().requested_format
+ .frame_size ==
+ params_small_.requested_format.frame_size);
}
} // namespace content
diff --git a/content/renderer/media/video_capture_message_filter.h b/content/renderer/media/video_capture_message_filter.h
index 501eaf1..e6e4194 100644
--- a/content/renderer/media/video_capture_message_filter.h
+++ b/content/renderer/media/video_capture_message_filter.h
@@ -16,7 +16,7 @@
#include "content/common/content_export.h"
#include "content/common/media/video_capture.h"
#include "ipc/message_filter.h"
-#include "media/video/capture/video_capture.h"
+#include "media/video/capture/video_capture_types.h"
namespace gpu {
struct MailboxHolder;
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
index 39865aa..97c810a 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
@@ -149,7 +149,7 @@ void MediaStreamRemoteVideoSource::DoRenderFrameOnMainThread(
scoped_refptr<media::VideoFrame> video_frame) {
DCHECK(message_loop_proxy_->BelongsToCurrentThread());
if (state() == STARTED)
- DeliverVideoFrame(video_frame);
+ DeliverVideoFrame(video_frame, format_);
}
} // namespace content
diff --git a/content/renderer/media/webrtc/video_destination_handler.cc b/content/renderer/media/webrtc/video_destination_handler.cc
index ac16f1e..3335293 100644
--- a/content/renderer/media/webrtc/video_destination_handler.cc
+++ b/content/renderer/media/webrtc/video_destination_handler.cc
@@ -116,7 +116,7 @@ void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data,
new_frame->stride(media::VideoFrame::kVPlane),
frame_size.width(), frame_size.height());
- DeliverVideoFrame(new_frame);
+ DeliverVideoFrame(new_frame, format_);
}
// PpFrameWriterProxy is a helper class to make sure the user won't use
@@ -190,4 +190,3 @@ bool VideoDestinationHandler::Open(
}
} // namespace content
-