summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-24 22:48:44 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-24 22:48:44 +0000
commit5e1502eaadb2525b3bf963ee2d34852b68c3cb4a (patch)
treec8e735d3ba142e1ed564a3b6b2c54af4488af718
parentdeedef38e3a95a33e70a22fe64c661ee3382ccbd (diff)
downloadchromium_src-5e1502eaadb2525b3bf963ee2d34852b68c3cb4a.zip
chromium_src-5e1502eaadb2525b3bf963ee2d34852b68c3cb4a.tar.gz
chromium_src-5e1502eaadb2525b3bf963ee2d34852b68c3cb4a.tar.bz2
Refactor WebMediaPlayerImpl's multithreaded compositing code into VideoFrameCompositor.
VideoFrameCompositor notifies the compositor directly on its thread instead of invalidating via the main thread. In addition VideoFramePainter is removed as WebMediaPlayerImpl always creates a compositing layer when video is present. BUG=335345 R=xhwang@chromium.org Review URL: https://codereview.chromium.org/196133025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@259048 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/content_renderer.gypi2
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/renderer/media/video_frame_compositor.cc152
-rw-r--r--content/renderer/media/video_frame_compositor.h77
-rw-r--r--content/renderer/media/video_frame_compositor_unittest.cc174
-rw-r--r--content/renderer/media/webmediaplayer_impl.cc63
-rw-r--r--content/renderer/media/webmediaplayer_impl.h18
-rw-r--r--media/filters/video_frame_painter.cc73
-rw-r--r--media/filters/video_frame_painter.h75
-rw-r--r--media/filters/video_frame_painter_unittest.cc160
-rw-r--r--media/media.gyp3
11 files changed, 428 insertions, 370 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 40eb4e4..ff8064e 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -275,6 +275,8 @@
'renderer/media/video_capture_impl_manager.h',
'renderer/media/video_capture_message_filter.cc',
'renderer/media/video_capture_message_filter.h',
+ 'renderer/media/video_frame_compositor.cc',
+ 'renderer/media/video_frame_compositor.h',
'renderer/media/video_frame_provider.cc',
'renderer/media/video_frame_provider.h',
'renderer/media/webaudiosourceprovider_impl.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 5b19815..7e47f7c 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -570,6 +570,7 @@
'renderer/media/video_capture_impl_unittest.cc',
'renderer/media/video_capture_message_filter_unittest.cc',
'renderer/media/video_destination_handler_unittest.cc',
+ 'renderer/media/video_frame_compositor_unittest.cc',
'renderer/media/webaudiosourceprovider_impl_unittest.cc',
'renderer/npapi/webplugin_impl_unittest.cc',
'renderer/paint_aggregator_unittest.cc',
diff --git a/content/renderer/media/video_frame_compositor.cc b/content/renderer/media/video_frame_compositor.cc
new file mode 100644
index 0000000..a8d0194
--- /dev/null
+++ b/content/renderer/media/video_frame_compositor.cc
@@ -0,0 +1,152 @@
+// 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 "content/renderer/media/video_frame_compositor.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/layers/video_frame_provider.h"
+#include "content/renderer/render_thread_impl.h"
+#include "media/base/video_frame.h"
+
+namespace content {
+
+class VideoFrameCompositor::Internal : public cc::VideoFrameProvider {
+ public:
+ Internal(
+ const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+ const base::Callback<void(gfx::Size)>& natural_size_changed_cb)
+ : compositor_task_runner_(compositor_task_runner),
+ natural_size_changed_cb_(natural_size_changed_cb),
+ client_(NULL),
+ compositor_notify_finished_(true),
+ current_frame_composited_(false),
+ frames_dropped_before_composite_(0) {}
+
+ virtual ~Internal() {
+ if (client_)
+ client_->StopUsingProvider();
+ }
+
+ void DeleteSoon() {
+ compositor_task_runner_->DeleteSoon(FROM_HERE, this);
+ }
+
+ void UpdateCurrentFrame(const scoped_refptr<media::VideoFrame>& frame) {
+ base::AutoLock auto_lock(lock_);
+
+ // Count frames as dropped if and only if we updated the frame but didn't
+ // finish notifying the compositor nor managed to composite the current
+ // frame.
+ if (!current_frame_composited_ && !compositor_notify_finished_ &&
+ frames_dropped_before_composite_ < kuint32max) {
+ ++frames_dropped_before_composite_;
+ }
+
+ if (current_frame_ &&
+ current_frame_->natural_size() != frame->natural_size()) {
+ natural_size_changed_cb_.Run(frame->natural_size());
+ }
+
+ current_frame_ = frame;
+ current_frame_composited_ = false;
+
+ compositor_notify_finished_ = false;
+ compositor_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Internal::NotifyCompositorOfNewFrame,
+ base::Unretained(this)));
+ }
+
+ // If |frame_being_composited| is true the current frame will not be counted
+ // as being dropped the next time UpdateCurrentFrame() is called.
+ scoped_refptr<media::VideoFrame> GetCurrentFrame(
+ bool frame_being_composited) {
+ base::AutoLock auto_lock(lock_);
+ if (frame_being_composited)
+ current_frame_composited_ = false;
+ return current_frame_;
+ }
+
+ uint32 GetFramesDroppedBeforeComposite() {
+ base::AutoLock auto_lock(lock_);
+ return frames_dropped_before_composite_;
+ }
+
+ void SetFramesDroppedBeforeCompositeForTesting(uint32 dropped_frames) {
+ base::AutoLock auto_lock(lock_);
+ frames_dropped_before_composite_ = dropped_frames;
+ }
+
+ private:
+ void NotifyCompositorOfNewFrame() {
+ base::AutoLock auto_lock(lock_);
+ compositor_notify_finished_ = true;
+ if (client_)
+ client_->DidReceiveFrame();
+ }
+
+ // cc::VideoFrameProvider implementation.
+ virtual void SetVideoFrameProviderClient(
+ cc::VideoFrameProvider::Client* client) OVERRIDE {
+ if (client_)
+ client_->StopUsingProvider();
+ client_ = client;
+ }
+
+ virtual scoped_refptr<media::VideoFrame> GetCurrentFrame() OVERRIDE {
+ return GetCurrentFrame(true);
+ }
+
+ virtual void PutCurrentFrame(const scoped_refptr<media::VideoFrame>& frame)
+ OVERRIDE {}
+
+ scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+ base::Callback<void(gfx::Size)>natural_size_changed_cb_;
+
+ cc::VideoFrameProvider::Client* client_;
+
+ base::Lock lock_;
+ scoped_refptr<media::VideoFrame> current_frame_;
+ bool compositor_notify_finished_;
+ bool current_frame_composited_;
+ uint32 frames_dropped_before_composite_;
+
+ DISALLOW_COPY_AND_ASSIGN(Internal);
+};
+
+VideoFrameCompositor::VideoFrameCompositor(
+ const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+ const base::Callback<void(gfx::Size)>& natural_size_changed_cb)
+ : internal_(new Internal(compositor_task_runner, natural_size_changed_cb)) {
+}
+
+VideoFrameCompositor::~VideoFrameCompositor() {
+ internal_->DeleteSoon();
+}
+
+cc::VideoFrameProvider* VideoFrameCompositor::GetVideoFrameProvider() {
+ return internal_;
+}
+
+void VideoFrameCompositor::UpdateCurrentFrame(
+ const scoped_refptr<media::VideoFrame>& frame) {
+ internal_->UpdateCurrentFrame(frame);
+}
+
+scoped_refptr<media::VideoFrame> VideoFrameCompositor::GetCurrentFrame() {
+ return internal_->GetCurrentFrame(false);
+}
+
+uint32 VideoFrameCompositor::GetFramesDroppedBeforeComposite() {
+ return internal_->GetFramesDroppedBeforeComposite();
+}
+
+void VideoFrameCompositor::SetFramesDroppedBeforeCompositeForTesting(
+ uint32 dropped_frames) {
+ internal_->SetFramesDroppedBeforeCompositeForTesting(dropped_frames);
+}
+
+} // namespace content
diff --git a/content/renderer/media/video_frame_compositor.h b/content/renderer/media/video_frame_compositor.h
new file mode 100644
index 0000000..47e6ad1
--- /dev/null
+++ b/content/renderer/media/video_frame_compositor.h
@@ -0,0 +1,77 @@
+// 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 CONTENT_RENDERER_MEDIA_VIDEO_FRAME_COMPOSITOR_H_
+#define CONTENT_RENDERER_MEDIA_VIDEO_FRAME_COMPOSITOR_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
+#include "ui/gfx/size.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace cc {
+class VideoFrameProvider;
+}
+
+namespace media {
+class VideoFrame;
+}
+
+namespace content {
+
+// VideoFrameCompositor handles incoming frames by notifying the compositor in a
+// thread-safe manner.
+//
+// Typical usage is to deliver the output of VideoRendererImpl to
+// UpdateCurrentFrame() so that VideoFrameCompositor can take care of tracking
+// dropped frames and firing callbacks as needed.
+//
+// All APIs are callable from any thread.
+class CONTENT_EXPORT VideoFrameCompositor {
+ public:
+ // |compositor_task_runner| is the task runner of the compositor.
+ //
+ // |natural_size_changed_cb| is run with the new natural size of the video
+ // frame whenever a change in natural size is detected. It is not called the
+ // first time UpdateCurrentFrame() is called. Run on the same thread as the
+ // caller of UpdateCurrentFrame().
+ VideoFrameCompositor(
+ const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
+ const base::Callback<void(gfx::Size)>& natural_size_changed_cb);
+ ~VideoFrameCompositor();
+
+ cc::VideoFrameProvider* GetVideoFrameProvider();
+
+ // Updates the current frame and notifies the compositor.
+ void UpdateCurrentFrame(const scoped_refptr<media::VideoFrame>& frame);
+
+ // Retrieves the last frame set via UpdateCurrentFrame() for non-compositing
+ // purposes (e.g., painting to a canvas). Frames retrieved in this manner will
+ // not be counted as being composited for frame drop counting.
+ //
+ // Note that the compositor retrieves frames via the cc::VideoFrameProvider
+ // interface instead of using this method.
+ scoped_refptr<media::VideoFrame> GetCurrentFrame();
+
+ // Returns the number of frames dropped before the compositor was notified as
+ // well as being able to composite the previous frame.
+ uint32 GetFramesDroppedBeforeComposite();
+
+ void SetFramesDroppedBeforeCompositeForTesting(uint32 dropped_frames);
+
+ private:
+ class Internal;
+ Internal* internal_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameCompositor);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_MEDIA_VIDEO_FRAME_COMPOSITOR_H_
diff --git a/content/renderer/media/video_frame_compositor_unittest.cc b/content/renderer/media/video_frame_compositor_unittest.cc
new file mode 100644
index 0000000..2f4163c
--- /dev/null
+++ b/content/renderer/media/video_frame_compositor_unittest.cc
@@ -0,0 +1,174 @@
+// 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/message_loop/message_loop.h"
+#include "cc/layers/video_frame_provider.h"
+#include "content/renderer/media/video_frame_compositor.h"
+#include "media/base/video_frame.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+using media::VideoFrame;
+
+class VideoFrameCompositorTest : public testing::Test,
+ public cc::VideoFrameProvider::Client {
+ public:
+ VideoFrameCompositorTest()
+ : compositor_(new VideoFrameCompositor(
+ message_loop_.message_loop_proxy(),
+ base::Bind(&VideoFrameCompositorTest::NaturalSizeChanged,
+ base::Unretained(this)))),
+ did_receive_frame_count_(0),
+ natural_size_changed_count_(0) {
+ provider()->SetVideoFrameProviderClient(this);
+ }
+
+ virtual ~VideoFrameCompositorTest() {
+ provider()->SetVideoFrameProviderClient(NULL);
+ compositor_.reset();
+ message_loop_.RunUntilIdle();
+ }
+
+ base::MessageLoop* message_loop() { return &message_loop_; }
+ VideoFrameCompositor* compositor() { return compositor_.get(); }
+ cc::VideoFrameProvider* provider() {
+ return compositor_->GetVideoFrameProvider();
+ }
+ int did_receive_frame_count() { return did_receive_frame_count_; }
+ int natural_size_changed_count() { return natural_size_changed_count_; }
+ gfx::Size natural_size() { return natural_size_; }
+
+ private:
+ // cc::VideoFrameProvider::Client implementation.
+ virtual void StopUsingProvider() OVERRIDE {}
+ virtual void DidReceiveFrame() OVERRIDE {
+ ++did_receive_frame_count_;
+ }
+ virtual void DidUpdateMatrix(const float* matrix) OVERRIDE {}
+
+ void NaturalSizeChanged(gfx::Size natural_size) {
+ ++natural_size_changed_count_;
+ natural_size_ = natural_size;
+ }
+
+ base::MessageLoop message_loop_;
+ scoped_ptr<VideoFrameCompositor> compositor_;
+ int did_receive_frame_count_;
+ int natural_size_changed_count_;
+ gfx::Size natural_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameCompositorTest);
+};
+
+TEST_F(VideoFrameCompositorTest, InitialValues) {
+ EXPECT_TRUE(compositor()->GetVideoFrameProvider());
+ EXPECT_FALSE(compositor()->GetCurrentFrame());
+ EXPECT_EQ(0u, compositor()->GetFramesDroppedBeforeComposite());
+}
+
+TEST_F(VideoFrameCompositorTest, UpdateCurrentFrame) {
+ scoped_refptr<VideoFrame> expected = VideoFrame::CreateEOSFrame();
+
+ compositor()->UpdateCurrentFrame(expected);
+ scoped_refptr<VideoFrame> actual = compositor()->GetCurrentFrame();
+ EXPECT_EQ(expected, actual);
+
+ // Should notify compositor asynchronously.
+ EXPECT_EQ(0, did_receive_frame_count());
+ message_loop()->RunUntilIdle();
+ EXPECT_EQ(1, did_receive_frame_count());
+}
+
+TEST_F(VideoFrameCompositorTest, NaturalSizeChanged) {
+ gfx::Size initial_size(8, 8);
+ scoped_refptr<VideoFrame> initial_frame =
+ VideoFrame::CreateBlackFrame(initial_size);
+
+ gfx::Size larger_size(16, 16);
+ scoped_refptr<VideoFrame> larger_frame =
+ VideoFrame::CreateBlackFrame(larger_size);
+
+ // Initial expectations.
+ EXPECT_EQ(0, natural_size().width());
+ EXPECT_EQ(0, natural_size().height());
+ EXPECT_EQ(0, natural_size_changed_count());
+
+ // Callback isn't fired for the first frame.
+ compositor()->UpdateCurrentFrame(initial_frame);
+ EXPECT_EQ(0, natural_size().width());
+ EXPECT_EQ(0, natural_size().height());
+ EXPECT_EQ(0, natural_size_changed_count());
+
+ // Callback should be fired once.
+ compositor()->UpdateCurrentFrame(larger_frame);
+ EXPECT_EQ(larger_size.width(), natural_size().width());
+ EXPECT_EQ(larger_size.height(), natural_size().height());
+ EXPECT_EQ(1, natural_size_changed_count());
+
+ compositor()->UpdateCurrentFrame(larger_frame);
+ EXPECT_EQ(larger_size.width(), natural_size().width());
+ EXPECT_EQ(larger_size.height(), natural_size().height());
+ EXPECT_EQ(1, natural_size_changed_count());
+
+ // Callback is fired once more when switching back to initial size.
+ compositor()->UpdateCurrentFrame(initial_frame);
+ EXPECT_EQ(initial_size.width(), natural_size().width());
+ EXPECT_EQ(initial_size.height(), natural_size().height());
+ EXPECT_EQ(2, natural_size_changed_count());
+
+ compositor()->UpdateCurrentFrame(initial_frame);
+ EXPECT_EQ(initial_size.width(), natural_size().width());
+ EXPECT_EQ(initial_size, natural_size());
+ EXPECT_EQ(2, natural_size_changed_count());
+}
+
+TEST_F(VideoFrameCompositorTest, GetFramesDroppedBeforeComposite) {
+ scoped_refptr<VideoFrame> frame = VideoFrame::CreateEOSFrame();
+
+ compositor()->UpdateCurrentFrame(frame);
+ EXPECT_EQ(0, did_receive_frame_count());
+ EXPECT_EQ(0u, compositor()->GetFramesDroppedBeforeComposite());
+
+ // Should not increment if we finished notifying the compositor but didn't
+ // composite the frame.
+ //
+ // This covers the scenario where the region we're rendering isn't
+ // visible so there wasn't a need to composite.
+ message_loop()->RunUntilIdle();
+ compositor()->UpdateCurrentFrame(frame);
+ EXPECT_EQ(1, did_receive_frame_count());
+ EXPECT_EQ(0u, compositor()->GetFramesDroppedBeforeComposite());
+
+ // Should not increment if we didn't finish notifying the compositor but still
+ // managed to composite the frame.
+ //
+ // This covers the scenario where something else may have notified the
+ // compositor and managed to composite the current frame.
+ message_loop()->RunUntilIdle();
+ provider()->GetCurrentFrame();
+ provider()->PutCurrentFrame(NULL);
+ compositor()->UpdateCurrentFrame(frame);
+ EXPECT_EQ(2, did_receive_frame_count());
+ EXPECT_EQ(0u, compositor()->GetFramesDroppedBeforeComposite());
+
+ // Should increment if we didn't notify the compositor and didn't composite
+ // the frame.
+ //
+ // This covers the scenario where we didn't even finish notifying nor
+ // compositing the current frame before updating. Consider it dropped.
+ message_loop()->RunUntilIdle();
+ compositor()->UpdateCurrentFrame(frame);
+ compositor()->UpdateCurrentFrame(frame);
+ EXPECT_EQ(3, did_receive_frame_count());
+ EXPECT_EQ(1u, compositor()->GetFramesDroppedBeforeComposite());
+
+ // Shouldn't overflow.
+ compositor()->SetFramesDroppedBeforeCompositeForTesting(kuint32max);
+ compositor()->UpdateCurrentFrame(frame);
+ EXPECT_EQ(kuint32max, compositor()->GetFramesDroppedBeforeComposite());
+}
+
+} // namespace content
diff --git a/content/renderer/media/webmediaplayer_impl.cc b/content/renderer/media/webmediaplayer_impl.cc
index 139ef02..48b001b 100644
--- a/content/renderer/media/webmediaplayer_impl.cc
+++ b/content/renderer/media/webmediaplayer_impl.cc
@@ -165,10 +165,11 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
supports_save_(true),
starting_(false),
chunk_demuxer_(NULL),
- painter_(
- BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::InvalidateOnMainThread),
+ compositor_( // Threaded compositing isn't enabled universally yet.
+ (RenderThreadImpl::current()->compositor_message_loop_proxy()
+ ? RenderThreadImpl::current()->compositor_message_loop_proxy()
+ : base::MessageLoopProxy::current()),
BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChange)),
- video_frame_provider_client_(NULL),
text_track_index_(0),
web_cdm_(NULL) {
media_log_->AddEvent(
@@ -198,7 +199,6 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
}
WebMediaPlayerImpl::~WebMediaPlayerImpl() {
- SetVideoFrameProviderClient(NULL);
client_->setWebLayer(NULL);
DCHECK(main_loop_->BelongsToCurrentThread());
@@ -533,9 +533,14 @@ void WebMediaPlayerImpl::paint(WebCanvas* canvas,
frame_->view()->isAcceleratedCompositingActive());
}
+ // TODO(scherkus): Clarify paint() API contract to better understand when and
+ // why it's being called. For example, today paint() is called when:
+ // - We haven't reached HAVE_CURRENT_DATA and need to paint black
+ // - We're painting to a canvas
+ // See http://crbug.com/341225 http://crbug.com/342621 for details.
+ scoped_refptr<media::VideoFrame> video_frame = compositor_.GetCurrentFrame();
TRACE_EVENT0("media", "WebMediaPlayerImpl:paint");
- scoped_refptr<media::VideoFrame> video_frame = painter_.GetCurrentFrame(true);
gfx::Rect gfx_rect(rect);
skcanvas_video_renderer_.Paint(video_frame.get(), canvas, gfx_rect, alpha);
}
@@ -568,9 +573,11 @@ unsigned WebMediaPlayerImpl::droppedFrameCount() const {
media::PipelineStatistics stats = pipeline_.GetStatistics();
- unsigned frames_dropped = stats.video_frames_dropped +
- const_cast<media::VideoFramePainter*>(&painter_)
- ->GetFramesDroppedBeforePaint();
+ unsigned frames_dropped = stats.video_frames_dropped;
+
+ frames_dropped += const_cast<VideoFrameCompositor&>(compositor_)
+ .GetFramesDroppedBeforeComposite();
+
DCHECK_LE(frames_dropped, stats.video_frames_decoded);
return frames_dropped;
}
@@ -589,33 +596,6 @@ unsigned WebMediaPlayerImpl::videoDecodedByteCount() const {
return stats.video_bytes_decoded;
}
-void WebMediaPlayerImpl::SetVideoFrameProviderClient(
- cc::VideoFrameProvider::Client* client) {
- // This is called from both the main renderer thread and the compositor
- // thread (when the main thread is blocked).
- if (video_frame_provider_client_)
- video_frame_provider_client_->StopUsingProvider();
- video_frame_provider_client_ = client;
-}
-
-scoped_refptr<media::VideoFrame> WebMediaPlayerImpl::GetCurrentFrame() {
- scoped_refptr<media::VideoFrame> current_frame =
- painter_.GetCurrentFrame(true);
- TRACE_EVENT_ASYNC_BEGIN0(
- "media", "WebMediaPlayerImpl:compositing", this);
- return current_frame;
-}
-
-void WebMediaPlayerImpl::PutCurrentFrame(
- const scoped_refptr<media::VideoFrame>& frame) {
- if (!accelerated_compositing_reported_) {
- accelerated_compositing_reported_ = true;
- DCHECK(frame_->view()->isAcceleratedCompositingActive());
- UMA_HISTOGRAM_BOOLEAN("Media.AcceleratedCompositingActive", true);
- }
- TRACE_EVENT_ASYNC_END0("media", "WebMediaPlayerImpl:compositing", this);
-}
-
bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture(
blink::WebGraphicsContext3D* web_graphics_context,
unsigned int texture,
@@ -624,8 +604,7 @@ bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture(
unsigned int type,
bool premultiply_alpha,
bool flip_y) {
- scoped_refptr<media::VideoFrame> video_frame =
- painter_.GetCurrentFrame(false);
+ scoped_refptr<media::VideoFrame> video_frame = compositor_.GetCurrentFrame();
TRACE_EVENT0("media", "WebMediaPlayerImpl:copyVideoTextureToPlatformTexture");
@@ -909,7 +888,6 @@ void WebMediaPlayerImpl::InvalidateOnMainThread() {
DCHECK(main_loop_->BelongsToCurrentThread());
TRACE_EVENT0("media", "WebMediaPlayerImpl::InvalidateOnMainThread");
- painter_.DidFinishInvalidating();
client_->repaint();
}
@@ -980,8 +958,8 @@ void WebMediaPlayerImpl::OnPipelineBufferingState(
if (hasVideo()) {
DCHECK(!video_weblayer_);
- video_weblayer_.reset(
- new webkit::WebLayerImpl(cc::VideoLayer::Create(this)));
+ video_weblayer_.reset(new webkit::WebLayerImpl(
+ cc::VideoLayer::Create(compositor_.GetVideoFrameProvider())));
client_->setWebLayer(video_weblayer_.get());
}
break;
@@ -1286,10 +1264,7 @@ void WebMediaPlayerImpl::OnNaturalSizeChange(gfx::Size size) {
void WebMediaPlayerImpl::FrameReady(
const scoped_refptr<media::VideoFrame>& frame) {
- // TODO(scherkus): Today we always invalidate on the main thread even when
- // compositing is available, which is less efficient and involves more
- // thread hops. Refer to http://crbug.com/335345 for details.
- painter_.UpdateCurrentFrame(frame);
+ compositor_.UpdateCurrentFrame(frame);
}
void WebMediaPlayerImpl::SetDecryptorReadyCB(
diff --git a/content/renderer/media/webmediaplayer_impl.h b/content/renderer/media/webmediaplayer_impl.h
index 7f765a8..b3516c5 100644
--- a/content/renderer/media/webmediaplayer_impl.h
+++ b/content/renderer/media/webmediaplayer_impl.h
@@ -13,15 +13,14 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread.h"
-#include "cc/layers/video_frame_provider.h"
#include "content/renderer/media/crypto/proxy_decryptor.h"
+#include "content/renderer/media/video_frame_compositor.h"
#include "media/base/audio_renderer_sink.h"
#include "media/base/decryptor.h"
#include "media/base/media_keys.h"
#include "media/base/pipeline.h"
#include "media/base/text_track.h"
#include "media/filters/skcanvas_video_renderer.h"
-#include "media/filters/video_frame_painter.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/public/platform/WebAudioSourceProvider.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
@@ -52,6 +51,7 @@ class WebLayerImpl;
namespace content {
class BufferedDataSource;
+class VideoFrameCompositor;
class WebAudioSourceProviderImpl;
class WebContentDecryptionModuleImpl;
class WebMediaPlayerDelegate;
@@ -63,7 +63,6 @@ class WebTextTrackImpl;
// Encrypted Media.
class WebMediaPlayerImpl
: public blink::WebMediaPlayer,
- public cc::VideoFrameProvider,
public base::SupportsWeakPtr<WebMediaPlayerImpl> {
public:
// Constructs a WebMediaPlayer implementation using Chromium's media stack.
@@ -126,13 +125,6 @@ class WebMediaPlayerImpl
virtual unsigned audioDecodedByteCount() const;
virtual unsigned videoDecodedByteCount() const;
- // cc::VideoFrameProvider implementation.
- virtual void SetVideoFrameProviderClient(
- cc::VideoFrameProvider::Client* client) OVERRIDE;
- virtual scoped_refptr<media::VideoFrame> GetCurrentFrame() OVERRIDE;
- virtual void PutCurrentFrame(const scoped_refptr<media::VideoFrame>& frame)
- OVERRIDE;
-
virtual bool copyVideoTextureToPlatformTexture(
blink::WebGraphicsContext3D* web_graphics_context,
unsigned int texture,
@@ -329,7 +321,7 @@ class WebMediaPlayerImpl
std::string init_data_type_;
// Video rendering members.
- media::VideoFramePainter painter_;
+ VideoFrameCompositor compositor_;
media::SkCanvasVideoRenderer skcanvas_video_renderer_;
gfx::Size natural_size_;
@@ -337,10 +329,6 @@ class WebMediaPlayerImpl
// playback.
scoped_ptr<webkit::WebLayerImpl> video_weblayer_;
- // A pointer back to the compositor to inform it about state changes. This is
- // not NULL while the compositor is actively using this webmediaplayer.
- cc::VideoFrameProvider::Client* video_frame_provider_client_;
-
// Text track objects get a unique index value when they're created.
int text_track_index_;
diff --git a/media/filters/video_frame_painter.cc b/media/filters/video_frame_painter.cc
deleted file mode 100644
index 6da2521..0000000
--- a/media/filters/video_frame_painter.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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/filters/video_frame_painter.h"
-
-#include "media/base/video_frame.h"
-
-namespace media {
-
-VideoFramePainter::VideoFramePainter(
- const base::Closure& invalidate_cb,
- const base::Callback<void(gfx::Size)>& natural_size_changed_cb)
- : invalidate_cb_(invalidate_cb),
- natural_size_changed_cb_(natural_size_changed_cb),
- invalidation_finished_(true),
- current_frame_painted_(false),
- frames_dropped_before_paint_(0) {}
-
-VideoFramePainter::~VideoFramePainter() {}
-
-void VideoFramePainter::UpdateCurrentFrame(
- const scoped_refptr<VideoFrame>& frame) {
- base::AutoLock auto_lock(lock_);
-
- // Count frames as dropped if and only if we updated the frame but didn't
- // finish invalidating nor managed to paint the current frame.
- if (!current_frame_painted_ && !invalidation_finished_ &&
- frames_dropped_before_paint_ < kuint32max) {
- ++frames_dropped_before_paint_;
- }
-
- if (current_frame_ &&
- current_frame_->natural_size() != frame->natural_size()) {
- natural_size_changed_cb_.Run(frame->natural_size());
- }
-
- current_frame_ = frame;
- current_frame_painted_ = false;
-
- if (!invalidation_finished_)
- return;
-
- invalidation_finished_ = false;
- invalidate_cb_.Run();
-}
-
-scoped_refptr<VideoFrame> VideoFramePainter::GetCurrentFrame(
- bool frame_being_painted) {
- base::AutoLock auto_lock(lock_);
- if (frame_being_painted)
- current_frame_painted_ = true;
-
- return current_frame_;
-}
-
-void VideoFramePainter::DidFinishInvalidating() {
- base::AutoLock auto_lock(lock_);
- invalidation_finished_ = true;
-}
-
-uint32 VideoFramePainter::GetFramesDroppedBeforePaint() {
- base::AutoLock auto_lock(lock_);
- return frames_dropped_before_paint_;
-}
-
-void VideoFramePainter::SetFramesDroppedBeforePaintForTesting(
- uint32 dropped_frames) {
- base::AutoLock auto_lock(lock_);
- frames_dropped_before_paint_ = dropped_frames;
-}
-
-} // namespace media
diff --git a/media/filters/video_frame_painter.h b/media/filters/video_frame_painter.h
deleted file mode 100644
index b7bb1ff..0000000
--- a/media/filters/video_frame_painter.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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_FILTERS_VIDEO_FRAME_PAINTER_H_
-#define MEDIA_FILTERS_VIDEO_FRAME_PAINTER_H_
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
-#include "media/base/media_export.h"
-#include "ui/gfx/size.h"
-
-namespace media {
-
-class VideoFrame;
-
-// VideoFramePainter handles incoming frames by managing painting-related
-// information and rate-limiting paint invalidations in a thread-safe manner.
-//
-// Typical usage is to deliver the output of VideoRendererImpl to
-// UpdateCurrentFrame() so that VideoFramePainter can take care of tracking
-// dropped frames and firing callbacks as needed.
-//
-// All APIs are callable from any thread.
-class MEDIA_EXPORT VideoFramePainter {
- public:
- // |invalidate_cb| is run whenever the current frame has been updated and
- // the previous invalidation is no longer pending.
- //
- // |natural_size_changed_cb| is run with the new natural size of the video
- // frame whenever a change in natural size is detected.
- VideoFramePainter(
- const base::Closure& invalidate_cb,
- const base::Callback<void(gfx::Size)>& natural_size_changed_cb);
- ~VideoFramePainter();
-
- // Updates the current frame and runs |invalidate_cb| if there isn't an
- // outstanding invalidation.
- //
- // Clients are expected to run DidFinishInvalidating() to inform the painter
- // that invalidation has completed.
- void UpdateCurrentFrame(const scoped_refptr<VideoFrame>& frame);
-
- // Retrieves the last frame set via SetCurrentFrame().
- //
- // Clients must specify whether the frame will be painted to allow proper
- // dropped frame count.
- scoped_refptr<VideoFrame> GetCurrentFrame(bool frame_being_painted);
-
- // Informs the painter that the client has finished invalidating.
- void DidFinishInvalidating();
-
- // Returns the number of frames dropped before the painter was notified of
- // painting via DidPaintCurrentFrame().
- uint32 GetFramesDroppedBeforePaint();
-
- void SetFramesDroppedBeforePaintForTesting(uint32 dropped_frames);
-
- private:
- base::Closure invalidate_cb_;
- base::Callback<void(gfx::Size)> natural_size_changed_cb_;
-
- base::Lock lock_;
- scoped_refptr<VideoFrame> current_frame_;
- bool invalidation_finished_;
- bool current_frame_painted_;
- uint32 frames_dropped_before_paint_;
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(VideoFramePainter);
-};
-
-} // namespace media
-
-#endif // MEDIA_FILTERS_VIDEO_FRAME_PAINTER_H_
diff --git a/media/filters/video_frame_painter_unittest.cc b/media/filters/video_frame_painter_unittest.cc
deleted file mode 100644
index 71534ff..0000000
--- a/media/filters/video_frame_painter_unittest.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// 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 "media/base/video_frame.h"
-#include "media/filters/video_frame_painter.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace media {
-
-class VideoFramePainterTest : public testing::Test {
- public:
- VideoFramePainterTest()
- : painter_(base::Bind(&VideoFramePainterTest::Invalidate,
- base::Unretained(this)),
- base::Bind(&VideoFramePainterTest::NaturalSizeChanged,
- base::Unretained(this))),
- invalidate_count_(0),
- natural_size_changed_count_(0) {}
- virtual ~VideoFramePainterTest() {}
-
- VideoFramePainter* painter() { return &painter_; }
- int invalidate_count() { return invalidate_count_; }
- int natural_size_changed_count() { return natural_size_changed_count_; }
- gfx::Size natural_size() { return natural_size_; }
-
- private:
- void Invalidate() {
- ++invalidate_count_;
- }
-
- void NaturalSizeChanged(gfx::Size natural_size) {
- ++natural_size_changed_count_;
- natural_size_ = natural_size;
- }
-
- VideoFramePainter painter_;
- int invalidate_count_;
- int natural_size_changed_count_;
- gfx::Size natural_size_;
-
- DISALLOW_COPY_AND_ASSIGN(VideoFramePainterTest);
-};
-
-TEST_F(VideoFramePainterTest, InitialValues) {
- EXPECT_FALSE(painter()->GetCurrentFrame(false));
- EXPECT_EQ(0u, painter()->GetFramesDroppedBeforePaint());
-}
-
-TEST_F(VideoFramePainterTest, UpdateCurrentFrame) {
- scoped_refptr<VideoFrame> expected = VideoFrame::CreateEOSFrame();
-
- painter()->UpdateCurrentFrame(expected);
- scoped_refptr<VideoFrame> actual = painter()->GetCurrentFrame(false);
-
- EXPECT_EQ(1, invalidate_count());
- EXPECT_EQ(expected, actual);
-}
-
-TEST_F(VideoFramePainterTest, NaturalSizeChanged) {
- gfx::Size initial_size(8, 8);
- scoped_refptr<VideoFrame> initial_frame =
- VideoFrame::CreateBlackFrame(initial_size);
-
- gfx::Size larger_size(16, 16);
- scoped_refptr<VideoFrame> larger_frame =
- VideoFrame::CreateBlackFrame(larger_size);
-
- // Initial expectations.
- EXPECT_EQ(0, natural_size().width());
- EXPECT_EQ(0, natural_size().height());
- EXPECT_EQ(0, natural_size_changed_count());
-
- // Callback isn't fired for the first frame.
- painter()->UpdateCurrentFrame(initial_frame);
- EXPECT_EQ(0, natural_size().width());
- EXPECT_EQ(0, natural_size().height());
- EXPECT_EQ(0, natural_size_changed_count());
-
- // Callback should be fired once.
- painter()->UpdateCurrentFrame(larger_frame);
- EXPECT_EQ(larger_size.width(), natural_size().width());
- EXPECT_EQ(larger_size.height(), natural_size().height());
- EXPECT_EQ(1, natural_size_changed_count());
-
- painter()->UpdateCurrentFrame(larger_frame);
- EXPECT_EQ(larger_size.width(), natural_size().width());
- EXPECT_EQ(larger_size.height(), natural_size().height());
- EXPECT_EQ(1, natural_size_changed_count());
-
- // Callback is fired once more when switching back to initial size.
- painter()->UpdateCurrentFrame(initial_frame);
- EXPECT_EQ(initial_size.width(), natural_size().width());
- EXPECT_EQ(initial_size.height(), natural_size().height());
- EXPECT_EQ(2, natural_size_changed_count());
-
- painter()->UpdateCurrentFrame(initial_frame);
- EXPECT_EQ(initial_size.width(), natural_size().width());
- EXPECT_EQ(initial_size, natural_size());
- EXPECT_EQ(2, natural_size_changed_count());
-}
-
-TEST_F(VideoFramePainterTest, DidFinishInvalidating) {
- scoped_refptr<VideoFrame> frame = VideoFrame::CreateEOSFrame();
-
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(1, invalidate_count());
-
- // Shouldn't increment until DidFinishInvalidating() is called.
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(1, invalidate_count());
-
- painter()->DidFinishInvalidating();
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(2, invalidate_count());
-}
-
-TEST_F(VideoFramePainterTest, GetFramesDroppedBeforePaint) {
- scoped_refptr<VideoFrame> frame = VideoFrame::CreateEOSFrame();
-
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(1, invalidate_count());
- EXPECT_EQ(0u, painter()->GetFramesDroppedBeforePaint());
-
- // Should not increment if we finished invalidating but didn't paint the
- // frame.
- //
- // This covers the scenario where the region we're invalidating isn't
- // visible so there wasn't a need to paint.
- painter()->DidFinishInvalidating();
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(2, invalidate_count());
- EXPECT_EQ(0u, painter()->GetFramesDroppedBeforePaint());
-
- // Should not increment if we didn't finish invalidating but still managed
- // to paint the frame.
- //
- // This covers the scenario where something else may have invalidated the
- // video's region and managed to paint the current frame.
- painter()->GetCurrentFrame(true);
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(2, invalidate_count());
- EXPECT_EQ(0u, painter()->GetFramesDroppedBeforePaint());
-
- // Should increment if we didn't invalidate and didn't paint the frame.
- //
- // This covers the scenario where we didn't even finish invalidating nor
- // painting the current frame before updating. Consider it dropped.
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(2, invalidate_count());
- EXPECT_EQ(1u, painter()->GetFramesDroppedBeforePaint());
-
- // Shouldn't overflow.
- painter()->SetFramesDroppedBeforePaintForTesting(kuint32max);
- painter()->UpdateCurrentFrame(frame);
- EXPECT_EQ(kuint32max, painter()->GetFramesDroppedBeforePaint());
-}
-
-} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index e12c04e..88df38a 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -410,8 +410,6 @@
'filters/source_buffer_stream.h',
'filters/stream_parser_factory.cc',
'filters/stream_parser_factory.h',
- 'filters/video_frame_painter.cc',
- 'filters/video_frame_painter.h',
'filters/video_renderer_impl.cc',
'filters/video_renderer_impl.h',
'filters/vpx_video_decoder.cc',
@@ -1028,7 +1026,6 @@
'filters/skcanvas_video_renderer_unittest.cc',
'filters/source_buffer_stream_unittest.cc',
'filters/video_decoder_selector_unittest.cc',
- 'filters/video_frame_painter_unittest.cc',
'filters/video_frame_stream_unittest.cc',
'filters/video_renderer_impl_unittest.cc',
'midi/midi_manager_usb_unittest.cc',