summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordalecurtis <dalecurtis@chromium.org>2015-05-11 11:42:07 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-11 18:43:05 +0000
commit44ce4de962fd1c456383a4ee2f39b9599edd42de (patch)
treed217c0c385e84bf6951f06f1df3a9a413953b0b4
parentd5fcf984458e420e9f2c943b0bacb49a8178de73 (diff)
downloadchromium_src-44ce4de962fd1c456383a4ee2f39b9599edd42de.zip
chromium_src-44ce4de962fd1c456383a4ee2f39b9599edd42de.tar.gz
chromium_src-44ce4de962fd1c456383a4ee2f39b9599edd42de.tar.bz2
Allow WMPI to drive compositor updates for invisible video tags.
Invisible tags don't get compositor updates, so we need to let them drive those updates (within reason). This allows VideoFrameCompositor instances without a VideoFrameProvider::Client to update at up to 250Hz, which should be enough for everyone. This is done by introducing GetCurrentFrameAndMaybeBackgroundRender() which is called by WebMediaPlayerImpl for grabbing frames. If we have no client, have a callback, and are background rendering, this allows WebMediaPlayerImpl::paint() calls to drive frame acquisition. Also fixes bug with media_blink_unittests which isn't caught by the try bots for some reason... BUG=485783 TEST=new unittest, ro.me plays great. Review URL: https://codereview.chromium.org/1132213002 Cr-Commit-Position: refs/heads/master@{#329201}
-rw-r--r--media/blink/run_all_unittests.cc7
-rw-r--r--media/blink/video_frame_compositor.cc24
-rw-r--r--media/blink/video_frame_compositor.h14
-rw-r--r--media/blink/video_frame_compositor_unittest.cc78
-rw-r--r--media/blink/webmediaplayer_impl.cc4
5 files changed, 112 insertions, 15 deletions
diff --git a/media/blink/run_all_unittests.cc b/media/blink/run_all_unittests.cc
index f85144b..0261dc2 100644
--- a/media/blink/run_all_unittests.cc
+++ b/media/blink/run_all_unittests.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "build/build_config.h"
@@ -81,6 +82,12 @@ void BlinkMediaTestSuite::Initialize() {
gin::V8Initializer::LoadV8Snapshot();
#endif
+ // Dummy task runner is initialized here because the blink::initialize creates
+ // IsolateHolder which needs the current task runner handle. There should be
+ // no task posted to this task runner.
+ scoped_ptr<base::MessageLoop> message_loop;
+ if (!base::MessageLoop::current())
+ message_loop.reset(new base::MessageLoop());
blink::initialize(blink_platform_support_.get());
}
diff --git a/media/blink/video_frame_compositor.cc b/media/blink/video_frame_compositor.cc
index 634eb4f..3e19254 100644
--- a/media/blink/video_frame_compositor.cc
+++ b/media/blink/video_frame_compositor.cc
@@ -165,6 +165,29 @@ void VideoFrameCompositor::PaintFrameUsingOldRenderingPath(
client_->DidReceiveFrame();
}
+scoped_refptr<VideoFrame>
+VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() {
+ DCHECK(compositor_task_runner_->BelongsToCurrentThread());
+ if (client_ || !rendering_ || !is_background_rendering_)
+ return current_frame_;
+
+ DCHECK(!last_background_render_.is_null());
+
+ const base::TimeTicks now = tick_clock_->NowTicks();
+ const base::TimeDelta interval = now - last_background_render_;
+
+ // Cap updates to 250Hz which should be more than enough for everyone.
+ if (interval < base::TimeDelta::FromMilliseconds(4))
+ return current_frame_;
+
+ // Update the interval based on the time between calls and call background
+ // render which will give this information to the client.
+ last_interval_ = interval;
+ BackgroundRender();
+
+ return current_frame_;
+}
+
bool VideoFrameCompositor::ProcessNewFrame(
const scoped_refptr<VideoFrame>& frame) {
DCHECK(compositor_task_runner_->BelongsToCurrentThread());
@@ -191,6 +214,7 @@ bool VideoFrameCompositor::ProcessNewFrame(
void VideoFrameCompositor::BackgroundRender() {
DCHECK(compositor_task_runner_->BelongsToCurrentThread());
const base::TimeTicks now = tick_clock_->NowTicks();
+ last_background_render_ = now;
CallRender(now, now + last_interval_, true);
}
diff --git a/media/blink/video_frame_compositor.h b/media/blink/video_frame_compositor.h
index 4eb2e7e..6b34b11 100644
--- a/media/blink/video_frame_compositor.h
+++ b/media/blink/video_frame_compositor.h
@@ -88,6 +88,19 @@ class MEDIA_EXPORT VideoFrameCompositor
void PaintFrameUsingOldRenderingPath(
const scoped_refptr<VideoFrame>& frame) override;
+ // Returns |current_frame_| if |client_| is set. If no |client_| is set,
+ // |is_background_rendering_| is true, and |callback_| is set, it requests a
+ // new frame from |callback_|, using the elapsed time between calls to this
+ // function as the render interval; defaulting to 16.6ms if no prior calls
+ // have been made. A cap of 250Hz (4ms) is in place to prevent clients from
+ // accidentally (or intentionally) spamming the rendering pipeline.
+ //
+ // This method is primarily to facilitate canvas and WebGL based applications
+ // where the <video> tag is invisible (possibly not even in the DOM) and thus
+ // does not receive a |client_|. In this case, frame acquisition is driven by
+ // the frequency of canvas or WebGL paints requested via JavaScript.
+ scoped_refptr<VideoFrame> GetCurrentFrameAndUpdateIfStale();
+
void set_tick_clock_for_testing(scoped_ptr<base::TickClock> tick_clock) {
tick_clock_ = tick_clock.Pass();
}
@@ -146,6 +159,7 @@ class MEDIA_EXPORT VideoFrameCompositor
bool rendered_last_frame_;
bool is_background_rendering_;
base::TimeDelta last_interval_;
+ base::TimeTicks last_background_render_;
// These values are updated and read from the media and compositor threads.
base::Lock lock_;
diff --git a/media/blink/video_frame_compositor_unittest.cc b/media/blink/video_frame_compositor_unittest.cc
index 9f73cb4..9394edf 100644
--- a/media/blink/video_frame_compositor_unittest.cc
+++ b/media/blink/video_frame_compositor_unittest.cc
@@ -49,6 +49,12 @@ class VideoFrameCompositorTest : public testing::Test,
compositor_->SetVideoFrameProviderClient(nullptr);
}
+ scoped_refptr<VideoFrame> CreateOpaqueFrame() {
+ gfx::Size size(8, 8);
+ return VideoFrame::CreateFrame(VideoFrame::YV12, size, gfx::Rect(size),
+ size, base::TimeDelta());
+ }
+
VideoFrameCompositor* compositor() { return compositor_.get(); }
int did_receive_frame_count() { return did_receive_frame_count_; }
int natural_size_changed_count() { return natural_size_changed_count_; }
@@ -212,8 +218,7 @@ TEST_F(VideoFrameCompositorTest, NaturalSizeChanged) {
TEST_F(VideoFrameCompositorTest, OpacityChanged) {
gfx::Size size(8, 8);
- scoped_refptr<VideoFrame> opaque_frame = VideoFrame::CreateFrame(
- VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta());
+ scoped_refptr<VideoFrame> opaque_frame = CreateOpaqueFrame();
scoped_refptr<VideoFrame> not_opaque_frame = VideoFrame::CreateFrame(
VideoFrame::YV12A, size, gfx::Rect(size), size, base::TimeDelta());
@@ -275,9 +280,7 @@ TEST_F(VideoFrameCompositorTest, OpacityChanged) {
}
TEST_F(VideoFrameCompositorTest, VideoRendererSinkFrameDropped) {
- gfx::Size size(8, 8);
- scoped_refptr<VideoFrame> opaque_frame = VideoFrame::CreateFrame(
- VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta());
+ scoped_refptr<VideoFrame> opaque_frame = CreateOpaqueFrame();
EXPECT_CALL(*this, Render(_, _, _)).WillRepeatedly(Return(opaque_frame));
StartVideoRendererSink();
@@ -320,20 +323,14 @@ TEST_F(VideoFrameCompositorTest, VideoLayerShutdownWhileRendering) {
}
TEST_F(VideoFrameCompositorTest, StartFiresBackgroundRender) {
- gfx::Size size(8, 8);
- scoped_refptr<VideoFrame> opaque_frame = VideoFrame::CreateFrame(
- VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta());
-
+ scoped_refptr<VideoFrame> opaque_frame = CreateOpaqueFrame();
EXPECT_CALL(*this, Render(_, _, true)).WillRepeatedly(Return(opaque_frame));
StartVideoRendererSink();
StopVideoRendererSink(true);
}
TEST_F(VideoFrameCompositorTest, BackgroundRenderTicks) {
- gfx::Size size(8, 8);
- scoped_refptr<VideoFrame> opaque_frame = VideoFrame::CreateFrame(
- VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta());
-
+ scoped_refptr<VideoFrame> opaque_frame = CreateOpaqueFrame();
compositor_->set_background_rendering_for_testing(true);
base::RunLoop run_loop;
@@ -353,4 +350,59 @@ TEST_F(VideoFrameCompositorTest, BackgroundRenderTicks) {
StopVideoRendererSink(true);
}
+TEST_F(VideoFrameCompositorTest, GetCurrentFrameAndUpdateIfStale) {
+ scoped_refptr<VideoFrame> opaque_frame_1 = CreateOpaqueFrame();
+ scoped_refptr<VideoFrame> opaque_frame_2 = CreateOpaqueFrame();
+ compositor_->set_background_rendering_for_testing(true);
+
+ // |current_frame_| should be null at this point since we don't have a client
+ // or a callback.
+ ASSERT_FALSE(compositor()->GetCurrentFrameAndUpdateIfStale());
+
+ // Starting the video renderer should return a single frame.
+ EXPECT_CALL(*this, Render(_, _, true)).WillOnce(Return(opaque_frame_1));
+ StartVideoRendererSink();
+
+ // Since we have a client, this call should not call background render, even
+ // if a lot of time has elapsed between calls.
+ tick_clock_->Advance(base::TimeDelta::FromSeconds(1));
+ ASSERT_EQ(opaque_frame_1, compositor()->GetCurrentFrameAndUpdateIfStale());
+
+ // An update current frame call should stop background rendering.
+ EXPECT_CALL(*this, Render(_, _, false)).WillOnce(Return(opaque_frame_2));
+ EXPECT_TRUE(
+ compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
+
+ // This call should still not call background render.
+ ASSERT_EQ(opaque_frame_2, compositor()->GetCurrentFrameAndUpdateIfStale());
+
+ testing::Mock::VerifyAndClearExpectations(this);
+
+ // Clear our client, which means no mock function calls for Client.
+ compositor()->SetVideoFrameProviderClient(nullptr);
+
+ // This call should still not call background render, because we aren't in the
+ // background rendering state yet.
+ ASSERT_EQ(opaque_frame_2, compositor()->GetCurrentFrameAndUpdateIfStale());
+
+ // Wait for background rendering to tick again.
+ base::RunLoop run_loop;
+ EXPECT_CALL(*this, Render(_, _, true))
+ .WillOnce(
+ DoAll(RunClosure(run_loop.QuitClosure()), Return(opaque_frame_1)))
+ .WillOnce(Return(opaque_frame_2));
+ run_loop.Run();
+
+ // This call should still not call background render, because not enough time
+ // has elapsed since the last background render call.
+ ASSERT_EQ(opaque_frame_1, compositor()->GetCurrentFrameAndUpdateIfStale());
+
+ // Advancing the tick clock should allow a new frame to be requested.
+ tick_clock_->Advance(base::TimeDelta::FromMilliseconds(10));
+ ASSERT_EQ(opaque_frame_2, compositor()->GetCurrentFrameAndUpdateIfStale());
+
+ // Background rendering should tick another render callback.
+ StopVideoRendererSink(false);
+}
+
} // namespace media
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 201ab17..814a932 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -987,7 +987,7 @@ static void GetCurrentFrameAndSignal(
scoped_refptr<VideoFrame>* video_frame_out,
base::WaitableEvent* event) {
TRACE_EVENT0("media", "GetCurrentFrameAndSignal");
- *video_frame_out = compositor->GetCurrentFrame();
+ *video_frame_out = compositor->GetCurrentFrameAndUpdateIfStale();
event->Signal();
}
@@ -995,7 +995,7 @@ scoped_refptr<VideoFrame>
WebMediaPlayerImpl::GetCurrentFrameFromCompositor() {
TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor");
if (compositor_task_runner_->BelongsToCurrentThread())
- return compositor_->GetCurrentFrame();
+ return compositor_->GetCurrentFrameAndUpdateIfStale();
// Use a posted task and waitable event instead of a lock otherwise
// WebGL/Canvas can see different content than what the compositor is seeing.