summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authordalecurtis <dalecurtis@chromium.org>2015-05-05 00:23:15 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-05 07:23:49 +0000
commit3e8bda613486ae81f53428ab9f41a7dbb7f9f0c3 (patch)
tree515411b1d64ba6bb8c318d7d4ac31ca7c8a55f2a /media
parentc4a4946256cd0abd02b187bd9efbd8d9b3c99b2a (diff)
downloadchromium_src-3e8bda613486ae81f53428ab9f41a7dbb7f9f0c3.zip
chromium_src-3e8bda613486ae81f53428ab9f41a7dbb7f9f0c3.tar.gz
chromium_src-3e8bda613486ae81f53428ab9f41a7dbb7f9f0c3.tar.bz2
Move background rendering pump to VideoFrameCompositor. Fixes tests.
Moving the pump to VFC cleans up a lot of code around dealing with stale frames and makes it possible to ensure we always have a frame ready for testing and upon ended without having to make an extra call during Stop. Doing this required / exposed: - Issues with firing the transition to HAVE_NOTHING, tests added. - Adding a |background_rendering| flag to the render callback. - NullAudioSink being built with non-test code. BUG=439548 TEST=new tests, all layout tests pass. Review URL: https://codereview.chromium.org/1121103002 Cr-Commit-Position: refs/heads/master@{#328295}
Diffstat (limited to 'media')
-rw-r--r--media/base/BUILD.gn4
-rw-r--r--media/base/null_video_sink.cc21
-rw-r--r--media/base/null_video_sink.h16
-rw-r--r--media/base/null_video_sink_unittest.cc20
-rw-r--r--media/base/video_renderer_sink.h7
-rw-r--r--media/blink/video_frame_compositor.cc129
-rw-r--r--media/blink/video_frame_compositor.h59
-rw-r--r--media/blink/video_frame_compositor_unittest.cc125
-rw-r--r--media/blink/webmediaplayer_impl.cc2
-rw-r--r--media/media.gyp4
-rw-r--r--media/renderers/video_renderer_impl.cc149
-rw-r--r--media/renderers/video_renderer_impl.h42
-rw-r--r--media/renderers/video_renderer_impl_unittest.cc139
13 files changed, 314 insertions, 403 deletions
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 0602408..a728cfa 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -123,8 +123,6 @@ source_set("base") {
"moving_average.h",
"multi_channel_resampler.cc",
"multi_channel_resampler.h",
- "null_video_sink.cc",
- "null_video_sink.h",
"pipeline.cc",
"pipeline.h",
"pipeline_status.h",
@@ -335,6 +333,8 @@ source_set("test_support") {
"mock_demuxer_host.h",
"mock_filters.cc",
"mock_filters.h",
+ "null_video_sink.cc",
+ "null_video_sink.h",
"test_data_util.cc",
"test_data_util.h",
"test_helpers.cc",
diff --git a/media/base/null_video_sink.cc b/media/base/null_video_sink.cc
index 214c499..ea38d06 100644
--- a/media/base/null_video_sink.cc
+++ b/media/base/null_video_sink.cc
@@ -22,7 +22,8 @@ NullVideoSink::NullVideoSink(
task_runner_(task_runner),
started_(false),
callback_(nullptr),
- tick_clock_(&default_tick_clock_) {
+ tick_clock_(&default_tick_clock_),
+ background_render_(false) {
}
NullVideoSink::~NullVideoSink() {
@@ -53,14 +54,12 @@ void NullVideoSink::CallRender() {
DCHECK(started_);
const base::TimeTicks end_of_interval = current_render_time_ + interval_;
- if (current_render_time_ > pause_end_time_) {
- scoped_refptr<VideoFrame> new_frame =
- callback_->Render(current_render_time_, end_of_interval);
- const bool is_new_frame = new_frame != last_frame_;
- last_frame_ = new_frame;
- if (is_new_frame)
- new_frame_cb_.Run(new_frame);
- }
+ scoped_refptr<VideoFrame> new_frame = callback_->Render(
+ current_render_time_, end_of_interval, background_render_);
+ const bool is_new_frame = new_frame != last_frame_;
+ last_frame_ = new_frame;
+ if (is_new_frame)
+ new_frame_cb_.Run(new_frame);
current_render_time_ += interval_;
@@ -94,8 +93,4 @@ void NullVideoSink::PaintFrameUsingOldRenderingPath(
new_frame_cb_.Run(frame);
}
-void NullVideoSink::PauseRenderCallbacks(base::TimeTicks pause_until) {
- pause_end_time_ = pause_until;
-}
-
} // namespace media
diff --git a/media/base/null_video_sink.h b/media/base/null_video_sink.h
index f1e076c..66ff79f 100644
--- a/media/base/null_video_sink.h
+++ b/media/base/null_video_sink.h
@@ -6,7 +6,6 @@
#define MEDIA_AUDIO_NULL_VIDEO_SINK_H_
#include "base/cancelable_callback.h"
-#include "base/md5.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
@@ -18,7 +17,7 @@ class SingleThreadTaskRunner;
namespace media {
-class MEDIA_EXPORT NullVideoSink : NON_EXPORTED_BASE(public VideoRendererSink) {
+class NullVideoSink : public VideoRendererSink {
public:
using NewFrameCB = base::Callback<void(const scoped_refptr<VideoFrame>&)>;
@@ -38,9 +37,6 @@ class MEDIA_EXPORT NullVideoSink : NON_EXPORTED_BASE(public VideoRendererSink) {
void PaintFrameUsingOldRenderingPath(
const scoped_refptr<VideoFrame>& frame) override;
- // Allows tests to simulate suspension of Render() callbacks.
- void PauseRenderCallbacks(base::TimeTicks pause_until);
-
void set_tick_clock_for_testing(base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
}
@@ -52,6 +48,10 @@ class MEDIA_EXPORT NullVideoSink : NON_EXPORTED_BASE(public VideoRendererSink) {
bool is_started() const { return started_; }
+ void set_background_render(bool is_background_rendering) {
+ background_render_ = is_background_rendering;
+ }
+
private:
// Task that periodically calls Render() to consume video data.
void CallRender();
@@ -74,9 +74,6 @@ class MEDIA_EXPORT NullVideoSink : NON_EXPORTED_BASE(public VideoRendererSink) {
// to maintain stable periodicity of callbacks.
base::TimeTicks current_render_time_;
- // Used to suspend Render() callbacks to |callback_| for some time.
- base::TimeTicks pause_end_time_;
-
// Allow for an injectable tick clock for testing.
base::DefaultTickClock default_tick_clock_;
base::TimeTicks last_now_;
@@ -87,6 +84,9 @@ class MEDIA_EXPORT NullVideoSink : NON_EXPORTED_BASE(public VideoRendererSink) {
// If set, called when Stop() is called.
base::Closure stop_cb_;
+ // Value passed to RenderCallback::Render().
+ bool background_render_;
+
DISALLOW_COPY_AND_ASSIGN(NullVideoSink);
};
diff --git a/media/base/null_video_sink_unittest.cc b/media/base/null_video_sink_unittest.cc
index aebe328..0279695 100644
--- a/media/base/null_video_sink_unittest.cc
+++ b/media/base/null_video_sink_unittest.cc
@@ -48,8 +48,10 @@ class NullVideoSinkTest : public testing::Test,
}
// VideoRendererSink::RenderCallback implementation.
- MOCK_METHOD2(Render,
- scoped_refptr<VideoFrame>(base::TimeTicks, base::TimeTicks));
+ MOCK_METHOD3(Render,
+ scoped_refptr<VideoFrame>(base::TimeTicks,
+ base::TimeTicks,
+ bool));
MOCK_METHOD0(OnFrameDropped, void());
MOCK_METHOD1(FrameReceived, void(const scoped_refptr<VideoFrame>&));
@@ -76,7 +78,7 @@ TEST_F(NullVideoSinkTest, BasicFunctionality) {
sink->Start(this);
const base::TimeTicks current_time = tick_clock_.NowTicks();
const base::TimeTicks current_interval_end = current_time + kInterval;
- EXPECT_CALL(*this, Render(current_time, current_interval_end))
+ EXPECT_CALL(*this, Render(current_time, current_interval_end, false))
.WillOnce(Return(test_frame));
WaitableMessageLoopEvent event;
EXPECT_CALL(*this, FrameReceived(test_frame))
@@ -84,12 +86,16 @@ TEST_F(NullVideoSinkTest, BasicFunctionality) {
event.RunAndWait();
}
+ // Verify that toggling background rendering mode issues the right bit to
+ // each Render() call.
+ sink->set_background_render(true);
+
// A second call returning the same frame should not result in a new call to
// FrameReceived().
{
SCOPED_TRACE("Waiting for second render call.");
WaitableMessageLoopEvent event;
- EXPECT_CALL(*this, Render(_, _))
+ EXPECT_CALL(*this, Render(_, _, true))
.WillOnce(Return(test_frame))
.WillOnce(Return(nullptr));
EXPECT_CALL(*this, FrameReceived(test_frame)).Times(0);
@@ -127,11 +133,11 @@ TEST_F(NullVideoSinkTest, ClocklessFunctionality) {
for (int i = 0; i < kTestRuns; ++i) {
if (i < kTestRuns - 1) {
EXPECT_CALL(*this, Render(current_time + i * interval,
- current_time + (i + 1) * interval))
+ current_time + (i + 1) * interval, false))
.WillOnce(Return(test_frame));
} else {
EXPECT_CALL(*this, Render(current_time + i * interval,
- current_time + (i + 1) * interval))
+ current_time + (i + 1) * interval, false))
.WillOnce(DoAll(RunClosure(event.GetClosure()), Return(nullptr)));
}
}
@@ -140,4 +146,4 @@ TEST_F(NullVideoSinkTest, ClocklessFunctionality) {
sink->Stop();
}
-}
+} // namespace media
diff --git a/media/base/video_renderer_sink.h b/media/base/video_renderer_sink.h
index 3f89856..5a51a73 100644
--- a/media/base/video_renderer_sink.h
+++ b/media/base/video_renderer_sink.h
@@ -27,8 +27,13 @@ class MEDIA_EXPORT VideoRendererSink {
// rendering within the requested interval. Intervals are expected to be
// regular, contiguous, and monotonically increasing. Irregular intervals
// may affect the rendering decisions made by the underlying callback.
+ //
+ // If |background_rendering| is true, the VideoRenderSink is pumping
+ // callbacks at a lower frequency than normal and the results of the
+ // Render() call may not be used.
virtual scoped_refptr<VideoFrame> Render(base::TimeTicks deadline_min,
- base::TimeTicks deadline_max) = 0;
+ base::TimeTicks deadline_max,
+ bool background_rendering) = 0;
// Called by the sink when a VideoFrame previously returned via Render() was
// not actually rendered. Must be called before the next Render() call.
diff --git a/media/blink/video_frame_compositor.cc b/media/blink/video_frame_compositor.cc
index 8064d21..634eb4f 100644
--- a/media/blink/video_frame_compositor.cc
+++ b/media/blink/video_frame_compositor.cc
@@ -12,10 +12,9 @@
namespace media {
-// The maximum time we'll allow to elapse between Render() callbacks if there is
-// an external caller requesting frames via GetCurrentFrame(); i.e. there is a
-// canvas which frames are being copied into.
-const int kStaleFrameThresholdMs = 250;
+// Amount of time to wait between UpdateCurrentFrame() callbacks before starting
+// background rendering to keep the Render() callbacks moving.
+const int kBackgroundRenderingTimeoutMs = 250;
static bool IsOpaque(const scoped_refptr<VideoFrame>& frame) {
switch (frame->format()) {
@@ -48,14 +47,21 @@ VideoFrameCompositor::VideoFrameCompositor(
tick_clock_(new base::DefaultTickClock()),
natural_size_changed_cb_(natural_size_changed_cb),
opacity_changed_cb_(opacity_changed_cb),
- stale_frame_threshold_(
- base::TimeDelta::FromMilliseconds(kStaleFrameThresholdMs)),
+ background_rendering_enabled_(true),
+ background_rendering_timer_(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs),
+ base::Bind(&VideoFrameCompositor::BackgroundRender,
+ base::Unretained(this)),
+ // Task is not repeating, CallRender() will reset the task as needed.
+ false),
client_(nullptr),
rendering_(false),
rendered_last_frame_(false),
- callback_(nullptr),
// Assume 60Hz before the first UpdateCurrentFrame() call.
- last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)) {
+ last_interval_(base::TimeDelta::FromSecondsD(1.0 / 60)),
+ callback_(nullptr) {
+ background_rendering_timer_.SetTaskRunner(compositor_task_runner_);
}
VideoFrameCompositor::~VideoFrameCompositor() {
@@ -70,7 +76,16 @@ void VideoFrameCompositor::OnRendererStateUpdate(bool new_state) {
DCHECK(compositor_task_runner_->BelongsToCurrentThread());
DCHECK_NE(rendering_, new_state);
rendering_ = new_state;
- last_frame_update_time_ = base::TimeTicks();
+
+ if (rendering_) {
+ // Always start playback in background rendering mode, if |client_| kicks
+ // in right away it's okay.
+ BackgroundRender();
+ } else if (background_rendering_enabled_) {
+ background_rendering_timer_.Stop();
+ } else {
+ DCHECK(!background_rendering_timer_.IsRunning());
+ }
if (!client_)
return;
@@ -81,18 +96,6 @@ void VideoFrameCompositor::OnRendererStateUpdate(bool new_state) {
client_->StopRendering();
}
-scoped_refptr<VideoFrame>
-VideoFrameCompositor::GetCurrentFrameAndUpdateIfStale() {
- DCHECK(compositor_task_runner_->BelongsToCurrentThread());
- if (rendering_) {
- const base::TimeTicks now = tick_clock_->NowTicks();
- if (now - last_frame_update_time_ > stale_frame_threshold_)
- UpdateCurrentFrame(now, now + last_interval_);
- }
-
- return GetCurrentFrame();
-}
-
void VideoFrameCompositor::SetVideoFrameProviderClient(
cc::VideoFrameProvider::Client* client) {
DCHECK(compositor_task_runner_->BelongsToCurrentThread());
@@ -100,7 +103,8 @@ void VideoFrameCompositor::SetVideoFrameProviderClient(
client_->StopUsingProvider();
client_ = client;
- if (rendering_)
+ // |client_| may now be null, so verify before calling it.
+ if (rendering_ && client_)
client_->StartRendering();
}
@@ -117,23 +121,11 @@ void VideoFrameCompositor::PutCurrentFrame() {
bool VideoFrameCompositor::UpdateCurrentFrame(base::TimeTicks deadline_min,
base::TimeTicks deadline_max) {
DCHECK(compositor_task_runner_->BelongsToCurrentThread());
- base::AutoLock lock(lock_);
- if (!callback_)
- return false;
-
- DCHECK(rendering_);
-
- // If the previous frame was never rendered, let the client know.
- if (!rendered_last_frame_ && current_frame_)
- callback_->OnFrameDropped();
-
- last_frame_update_time_ = tick_clock_->NowTicks();
- last_interval_ = deadline_max - deadline_min;
- return ProcessNewFrame(callback_->Render(deadline_min, deadline_max), false);
+ return CallRender(deadline_min, deadline_max, false);
}
void VideoFrameCompositor::Start(RenderCallback* callback) {
- TRACE_EVENT0("media", __FUNCTION__);
+ TRACE_EVENT0("media", "VideoFrameCompositor::Start");
// Called from the media thread, so acquire the callback under lock before
// returning in case a Stop() call comes in before the PostTask is processed.
@@ -146,27 +138,13 @@ void VideoFrameCompositor::Start(RenderCallback* callback) {
}
void VideoFrameCompositor::Stop() {
- TRACE_EVENT0("media", __FUNCTION__);
+ TRACE_EVENT0("media", "VideoFrameCompositor::Stop");
// Called from the media thread, so release the callback under lock before
// returning to avoid a pending UpdateCurrentFrame() call occurring before
// the PostTask is processed.
base::AutoLock lock(lock_);
DCHECK(callback_);
-
- // Fire one more Render() callback if we're more than one render interval away
- // to ensure that we have a good frame to display if Render() has never been
- // called, or was called long enough ago that the frame is stale. We must
- // always have a |current_frame_| to recover from "damage" to the video layer.
- const base::TimeTicks now = tick_clock_->NowTicks();
- if (now - last_frame_update_time_ > last_interval_) {
- compositor_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(base::IgnoreResult(&VideoFrameCompositor::ProcessNewFrame),
- base::Unretained(this),
- callback_->Render(now, now + last_interval_), true));
- }
-
callback_ = nullptr;
compositor_task_runner_->PostTask(
FROM_HERE, base::Bind(&VideoFrameCompositor::OnRendererStateUpdate,
@@ -183,12 +161,12 @@ void VideoFrameCompositor::PaintFrameUsingOldRenderingPath(
return;
}
- ProcessNewFrame(frame, true);
+ if (ProcessNewFrame(frame) && client_)
+ client_->DidReceiveFrame();
}
bool VideoFrameCompositor::ProcessNewFrame(
- const scoped_refptr<VideoFrame>& frame,
- bool notify_client_of_new_frames) {
+ const scoped_refptr<VideoFrame>& frame) {
DCHECK(compositor_task_runner_->BelongsToCurrentThread());
if (frame == current_frame_)
@@ -207,10 +185,47 @@ bool VideoFrameCompositor::ProcessNewFrame(
opacity_changed_cb_.Run(IsOpaque(frame));
current_frame_ = frame;
- if (notify_client_of_new_frames && client_)
- client_->DidReceiveFrame();
-
return true;
}
+void VideoFrameCompositor::BackgroundRender() {
+ DCHECK(compositor_task_runner_->BelongsToCurrentThread());
+ const base::TimeTicks now = tick_clock_->NowTicks();
+ CallRender(now, now + last_interval_, true);
+}
+
+bool VideoFrameCompositor::CallRender(base::TimeTicks deadline_min,
+ base::TimeTicks deadline_max,
+ bool background_rendering) {
+ DCHECK(compositor_task_runner_->BelongsToCurrentThread());
+
+ base::AutoLock lock(lock_);
+ if (!callback_) {
+ // Even if we no longer have a callback, return true if we have a frame
+ // which |client_| hasn't seen before.
+ return !rendered_last_frame_ && current_frame_;
+ }
+
+ DCHECK(rendering_);
+
+ // If the previous frame was never rendered and we're not in background
+ // rendering mode (nor have just exited it), let the client know.
+ if (!rendered_last_frame_ && current_frame_ && !background_rendering &&
+ !is_background_rendering_) {
+ callback_->OnFrameDropped();
+ }
+
+ const bool new_frame = ProcessNewFrame(
+ callback_->Render(deadline_min, deadline_max, background_rendering));
+
+ is_background_rendering_ = background_rendering;
+ last_interval_ = deadline_max - deadline_min;
+
+ // Restart the background rendering timer whether we're background rendering
+ // or not; in either case we should wait for |kBackgroundRenderingTimeoutMs|.
+ if (background_rendering_enabled_)
+ background_rendering_timer_.Reset();
+ return new_frame;
+}
+
} // namespace media
diff --git a/media/blink/video_frame_compositor.h b/media/blink/video_frame_compositor.h
index 2e8c2ccc..4eb2e7e 100644
--- a/media/blink/video_frame_compositor.h
+++ b/media/blink/video_frame_compositor.h
@@ -10,6 +10,7 @@
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/time/tick_clock.h"
+#include "base/timer/timer.h"
#include "cc/layers/video_frame_provider.h"
#include "media/base/media_export.h"
#include "media/base/video_renderer_sink.h"
@@ -38,6 +39,9 @@ class VideoFrame;
// once new frames are expected or are no longer expected to be ready; this data
// is relayed to the compositor to avoid extraneous callbacks.
//
+// VideoFrameCompositor is also responsible for pumping UpdateCurrentFrame()
+// callbacks in the background when |client_| has decided to suspend them.
+//
// VideoFrameCompositor must live on the same thread as the compositor, though
// it may be constructed on any thread.
class MEDIA_EXPORT VideoFrameCompositor
@@ -68,14 +72,6 @@ class MEDIA_EXPORT VideoFrameCompositor
// called before destruction starts.
~VideoFrameCompositor() override;
- // Returns |current_frame_| if it was refreshed recently; otherwise, if
- // |callback_| is available, requests a new frame and returns that one.
- //
- // This is required for programmatic frame requests where the compositor may
- // have stopped issuing UpdateCurrentFrame() callbacks in response to
- // visibility changes in the output layer.
- scoped_refptr<VideoFrame> GetCurrentFrameAndUpdateIfStale();
-
// cc::VideoFrameProvider implementation. These methods must be called on the
// |compositor_task_runner_|.
void SetVideoFrameProviderClient(
@@ -96,23 +92,37 @@ class MEDIA_EXPORT VideoFrameCompositor
tick_clock_ = tick_clock.Pass();
}
- base::TimeDelta get_stale_frame_threshold_for_testing() const {
- return stale_frame_threshold_;
- }
-
void clear_current_frame_for_testing() { current_frame_ = nullptr; }
+ // Enables or disables background rendering. If |enabled|, |timeout| is the
+ // amount of time to wait after the last Render() call before starting the
+ // background rendering mode. Note, this can not disable the background
+ // rendering call issues when a sink is started.
+ void set_background_rendering_for_testing(bool enabled) {
+ background_rendering_enabled_ = enabled;
+ }
+
private:
// Called on the compositor thread in response to Start() or Stop() calls;
// must be used to change |rendering_| state.
void OnRendererStateUpdate(bool new_state);
// Handles setting of |current_frame_| and fires |natural_size_changed_cb_|
- // and |opacity_changed_cb_| when the frame properties changes. Will also
- // call DidReceiveFrame() on |client_| if |notify_client_of_new_frames| is
- // true and a new frame is encountered.
- bool ProcessNewFrame(const scoped_refptr<VideoFrame>& frame,
- bool notify_client_of_new_frames);
+ // and |opacity_changed_cb_| when the frame properties changes.
+ bool ProcessNewFrame(const scoped_refptr<VideoFrame>& frame);
+
+ // Called by |background_rendering_timer_| when enough time elapses where we
+ // haven't seen a Render() call.
+ void BackgroundRender();
+
+ // If |callback_| is available, calls Render() with the provided properties.
+ // Updates |is_background_rendering_|, |last_interval_|, and resets
+ // |background_rendering_timer_|. Ensures that natural size and opacity
+ // changes are correctly fired. Returns true if there's a new frame available
+ // via GetCurrentFrame().
+ bool CallRender(base::TimeTicks deadline_min,
+ base::TimeTicks deadline_max,
+ bool background_rendering);
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
scoped_ptr<base::TickClock> tick_clock_;
@@ -121,23 +131,26 @@ class MEDIA_EXPORT VideoFrameCompositor
const base::Callback<void(gfx::Size)> natural_size_changed_cb_;
const base::Callback<void(bool)> opacity_changed_cb_;
- base::TimeDelta stale_frame_threshold_;
+ // Allows tests to disable the background rendering task.
+ bool background_rendering_enabled_;
+
+ // Manages UpdateCurrentFrame() callbacks if |client_| has stopped sending
+ // them for various reasons. Runs on |compositor_task_runner_| and is reset
+ // after each successful UpdateCurrentFrame() call.
+ base::Timer background_rendering_timer_;
// These values are only set and read on the compositor thread.
cc::VideoFrameProvider::Client* client_;
scoped_refptr<VideoFrame> current_frame_;
bool rendering_;
bool rendered_last_frame_;
+ bool is_background_rendering_;
+ base::TimeDelta last_interval_;
// These values are updated and read from the media and compositor threads.
base::Lock lock_;
VideoRendererSink::RenderCallback* callback_;
- // These values are set on the compositor thread under lock and may be read
- // from the media thread under lock.
- base::TimeTicks last_frame_update_time_;
- base::TimeDelta last_interval_;
-
DISALLOW_COPY_AND_ASSIGN(VideoFrameCompositor);
};
diff --git a/media/blink/video_frame_compositor_unittest.cc b/media/blink/video_frame_compositor_unittest.cc
index 553222c..9f73cb4 100644
--- a/media/blink/video_frame_compositor_unittest.cc
+++ b/media/blink/video_frame_compositor_unittest.cc
@@ -4,6 +4,7 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
#include "base/test/simple_test_tick_clock.h"
#include "cc/layers/video_frame_provider.h"
#include "media/base/video_frame.h"
@@ -12,10 +13,15 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::DoAll;
using testing::Return;
namespace media {
+ACTION_P(RunClosure, closure) {
+ closure.Run();
+}
+
class VideoFrameCompositorTest : public testing::Test,
public cc::VideoFrameProvider::Client,
public VideoRendererSink::RenderCallback {
@@ -35,10 +41,12 @@ class VideoFrameCompositorTest : public testing::Test,
compositor_->SetVideoFrameProviderClient(this);
compositor_->set_tick_clock_for_testing(
scoped_ptr<base::TickClock>(tick_clock_));
+ // Disable background rendering by default.
+ compositor_->set_background_rendering_for_testing(false);
}
~VideoFrameCompositorTest() override {
- compositor_->SetVideoFrameProviderClient(NULL);
+ compositor_->SetVideoFrameProviderClient(nullptr);
}
VideoFrameCompositor* compositor() { return compositor_.get(); }
@@ -58,8 +66,10 @@ class VideoFrameCompositorTest : public testing::Test,
void DidUpdateMatrix(const float* matrix) override {}
// VideoRendererSink::RenderCallback implementation.
- MOCK_METHOD2(Render,
- scoped_refptr<VideoFrame>(base::TimeTicks, base::TimeTicks));
+ MOCK_METHOD3(Render,
+ scoped_refptr<VideoFrame>(base::TimeTicks,
+ base::TimeTicks,
+ bool));
MOCK_METHOD0(OnFrameDropped, void());
void NaturalSizeChanged(gfx::Size natural_size) {
@@ -81,13 +91,14 @@ class VideoFrameCompositorTest : public testing::Test,
message_loop.RunUntilIdle();
}
- void StopVideoRendererSink() {
- EXPECT_CALL(*this, StopRendering());
+ void StopVideoRendererSink(bool have_client) {
+ if (have_client)
+ EXPECT_CALL(*this, StopRendering());
+ const bool had_current_frame = !!compositor_->GetCurrentFrame();
compositor()->Stop();
+ // If we previously had a frame, we should still have one now.
+ EXPECT_EQ(had_current_frame, !!compositor_->GetCurrentFrame());
message_loop.RunUntilIdle();
-
- // We should still have a frame after stop is called.
- EXPECT_TRUE(compositor_->GetCurrentFrame());
}
void RenderFrame() {
@@ -165,20 +176,19 @@ TEST_F(VideoFrameCompositorTest, NaturalSizeChanged) {
natural_size_ = empty_size;
compositor()->clear_current_frame_for_testing();
- StartVideoRendererSink();
- EXPECT_CALL(*this, Render(_, _))
+ EXPECT_CALL(*this, Render(_, _, _))
.WillOnce(Return(initial_frame))
.WillOnce(Return(larger_frame))
.WillOnce(Return(initial_frame))
.WillOnce(Return(initial_frame));
- // Callback isn't fired for the first frame.
+ StartVideoRendererSink();
+
+ // Starting the sink will issue one Render() call, ensure the callback isn't
+ // fired for the first frame.
EXPECT_EQ(0, natural_size_changed_count());
- EXPECT_TRUE(
- compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
- RenderFrame();
EXPECT_EQ(empty_size, natural_size());
- EXPECT_EQ(0, natural_size_changed_count());
+ // Once another frame is received with a different size it should fire.
EXPECT_TRUE(
compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
RenderFrame();
@@ -197,7 +207,7 @@ TEST_F(VideoFrameCompositorTest, NaturalSizeChanged) {
EXPECT_EQ(2, natural_size_changed_count());
RenderFrame();
- StopVideoRendererSink();
+ StopVideoRendererSink(true);
}
TEST_F(VideoFrameCompositorTest, OpacityChanged) {
@@ -234,16 +244,12 @@ TEST_F(VideoFrameCompositorTest, OpacityChanged) {
opacity_changed_count_ = 0;
compositor()->clear_current_frame_for_testing();
- StartVideoRendererSink();
- EXPECT_CALL(*this, Render(_, _))
+ EXPECT_CALL(*this, Render(_, _, _))
.WillOnce(Return(not_opaque_frame))
.WillOnce(Return(not_opaque_frame))
.WillOnce(Return(opaque_frame))
.WillOnce(Return(opaque_frame));
- EXPECT_EQ(0, opacity_changed_count());
- EXPECT_TRUE(
- compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
- RenderFrame();
+ StartVideoRendererSink();
EXPECT_FALSE(opaque());
EXPECT_EQ(1, opacity_changed_count());
@@ -265,7 +271,7 @@ TEST_F(VideoFrameCompositorTest, OpacityChanged) {
EXPECT_EQ(2, opacity_changed_count());
RenderFrame();
- StopVideoRendererSink();
+ StopVideoRendererSink(true);
}
TEST_F(VideoFrameCompositorTest, VideoRendererSinkFrameDropped) {
@@ -273,12 +279,15 @@ TEST_F(VideoFrameCompositorTest, VideoRendererSinkFrameDropped) {
scoped_refptr<VideoFrame> opaque_frame = VideoFrame::CreateFrame(
VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta());
+ EXPECT_CALL(*this, Render(_, _, _)).WillRepeatedly(Return(opaque_frame));
StartVideoRendererSink();
- EXPECT_CALL(*this, Render(_, _)).WillRepeatedly(Return(opaque_frame));
- EXPECT_TRUE(
+
+ // The first UpdateCurrentFrame() after a background render, which starting
+ // the sink does automatically, won't report a dropped frame.
+ EXPECT_FALSE(
compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
- // If we don't call RenderFrame() the frame should be reported as dropped.
+ // Another call should trigger a dropped frame callback.
EXPECT_CALL(*this, OnFrameDropped());
EXPECT_FALSE(
compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
@@ -300,60 +309,48 @@ TEST_F(VideoFrameCompositorTest, VideoRendererSinkFrameDropped) {
EXPECT_FALSE(
compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
- StopVideoRendererSink();
+ StopVideoRendererSink(true);
+}
+
+TEST_F(VideoFrameCompositorTest, VideoLayerShutdownWhileRendering) {
+ EXPECT_CALL(*this, Render(_, _, true)).WillOnce(Return(nullptr));
+ StartVideoRendererSink();
+ compositor_->SetVideoFrameProviderClient(nullptr);
+ StopVideoRendererSink(false);
}
-TEST_F(VideoFrameCompositorTest, GetCurrentFrameAndUpdateIfStale) {
+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_2 = VideoFrame::CreateFrame(
- VideoFrame::YV12, size, gfx::Rect(size), size, base::TimeDelta());
+ EXPECT_CALL(*this, Render(_, _, true)).WillRepeatedly(Return(opaque_frame));
StartVideoRendererSink();
- EXPECT_CALL(*this, Render(_, _))
- .WillOnce(Return(opaque_frame))
- .WillOnce(Return(opaque_frame_2));
- EXPECT_TRUE(
- compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
-
- base::TimeDelta stale_frame_threshold =
- compositor()->get_stale_frame_threshold_for_testing();
-
- // Advancing time a little bit shouldn't cause the frame to be stale.
- tick_clock_->Advance(stale_frame_threshold / 2);
- EXPECT_EQ(opaque_frame, compositor()->GetCurrentFrameAndUpdateIfStale());
-
- // Since rendering of frames is likely not happening, this will trigger a
- // dropped frame call.
- EXPECT_CALL(*this, OnFrameDropped());
-
- // Advancing the clock over the threshold should cause a new frame request.
- tick_clock_->Advance(stale_frame_threshold / 2 +
- base::TimeDelta::FromMicroseconds(1));
- EXPECT_EQ(opaque_frame_2, compositor()->GetCurrentFrameAndUpdateIfStale());
-
- StopVideoRendererSink();
+ StopVideoRendererSink(true);
}
-TEST_F(VideoFrameCompositorTest, StopUpdatesCurrentFrameIfStale) {
+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());
- const base::TimeDelta interval = base::TimeDelta::FromSecondsD(1.0 / 60);
+ compositor_->set_background_rendering_for_testing(true);
+ base::RunLoop run_loop;
+ EXPECT_CALL(*this, Render(_, _, true))
+ .WillOnce(Return(opaque_frame))
+ .WillOnce(
+ DoAll(RunClosure(run_loop.QuitClosure()), Return(opaque_frame)));
StartVideoRendererSink();
+ run_loop.Run();
- // Expect two calls to Render(), one from UpdateCurrentFrame() and one from
- // Stop() because the frame is too old.
- EXPECT_CALL(*this, Render(_, _))
- .WillOnce(Return(opaque_frame))
- .WillOnce(Return(opaque_frame));
- EXPECT_TRUE(compositor()->UpdateCurrentFrame(base::TimeTicks(),
- base::TimeTicks() + interval));
- tick_clock_->Advance(interval * 2);
- StopVideoRendererSink();
+ // UpdateCurrentFrame() calls should indicate they are not synthetic.
+ EXPECT_CALL(*this, Render(_, _, false)).WillOnce(Return(opaque_frame));
+ EXPECT_FALSE(
+ compositor()->UpdateCurrentFrame(base::TimeTicks(), base::TimeTicks()));
+
+ // Background rendering should tick another render callback.
+ StopVideoRendererSink(true);
}
} // namespace media
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 79c134f..bf938c2 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -985,7 +985,7 @@ static void GetCurrentFrameAndSignal(
scoped_refptr<VideoFrame>* video_frame_out,
base::WaitableEvent* event) {
TRACE_EVENT0("media", "GetCurrentFrameAndSignal");
- *video_frame_out = compositor->GetCurrentFrameAndUpdateIfStale();
+ *video_frame_out = compositor->GetCurrentFrame();
event->Signal();
}
diff --git a/media/media.gyp b/media/media.gyp
index 02d425b..758166b 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -334,8 +334,6 @@
'base/moving_average.h',
'base/multi_channel_resampler.cc',
'base/multi_channel_resampler.h',
- 'base/null_video_sink.cc',
- 'base/null_video_sink.h',
'base/pipeline.cc',
'base/pipeline.h',
'base/pipeline_status.h',
@@ -1506,6 +1504,8 @@
'base/mock_demuxer_host.h',
'base/mock_filters.cc',
'base/mock_filters.h',
+ 'base/null_video_sink.cc',
+ 'base/null_video_sink.h',
'base/test_data_util.cc',
'base/test_data_util.h',
'base/test_helpers.cc',
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index 558c8e6..f493f45 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -22,10 +22,6 @@
namespace media {
-// Wait a quarter of a second for Render() callbacks before starting background
-// rendering to keep the decode pump moving.
-const int kBackgroundRenderingTimeoutMs = 250;
-
VideoRendererImpl::VideoRendererImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
VideoRendererSink* sink,
@@ -53,12 +49,9 @@ VideoRendererImpl::VideoRendererImpl(
frames_dropped_(0),
is_shutting_down_(false),
tick_clock_(new base::DefaultTickClock()),
- is_background_rendering_(false),
- should_use_background_renderering_(true),
+ was_background_rendering_(false),
time_progressing_(false),
render_first_frame_and_stop_(false),
- background_rendering_timeout_(
- base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs)),
weak_factory_(this) {
}
@@ -149,7 +142,7 @@ void VideoRendererImpl::Initialize(
DCHECK(!wall_clock_time_cb.is_null());
DCHECK_EQ(kUninitialized, state_);
DCHECK(!render_first_frame_and_stop_);
- DCHECK(!is_background_rendering_);
+ DCHECK(!was_background_rendering_);
DCHECK(!time_progressing_);
low_delay_ = (stream->liveness() == DemuxerStream::LIVENESS_LIVE);
@@ -175,11 +168,11 @@ void VideoRendererImpl::Initialize(
scoped_refptr<VideoFrame> VideoRendererImpl::Render(
base::TimeTicks deadline_min,
- base::TimeTicks deadline_max) {
+ base::TimeTicks deadline_max,
+ bool background_rendering) {
base::AutoLock auto_lock(lock_);
DCHECK(use_new_video_renderering_path_);
DCHECK_EQ(state_, kPlaying);
- last_render_time_ = tick_clock_->NowTicks();
size_t frames_dropped = 0;
scoped_refptr<VideoFrame> result =
@@ -189,24 +182,31 @@ scoped_refptr<VideoFrame> VideoRendererImpl::Render(
// we've had a proper startup sequence.
DCHECK(result);
- // See if it's time to fire the ended callback.
+ // Declare HAVE_NOTHING if we reach a state where we can't progress playback
+ // any further. We don't want to do this if we've already done so, reached
+ // end of stream, or have frames available. We also don't want to do this in
+ // background rendering mode unless this isn't the first background render
+ // tick and we haven't seen any decoded frames since the last one.
const size_t effective_frames = MaybeFireEndedCallback();
-
- // Declare HAVE_NOTHING if we have no more effective frames.
- if (!effective_frames && !rendered_end_of_stream_ &&
- buffering_state_ == BUFFERING_HAVE_ENOUGH) {
- buffering_state_ = BUFFERING_HAVE_NOTHING;
+ if (buffering_state_ == BUFFERING_HAVE_ENOUGH && !received_end_of_stream_ &&
+ !effective_frames && (!background_rendering ||
+ (!frames_decoded_ && was_background_rendering_))) {
+ // Do not set |buffering_state_| here as the lock in FrameReady() may be
+ // held already and it fire the state changes in the wrong order.
task_runner_->PostTask(
- FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING));
+ FROM_HERE, base::Bind(&VideoRendererImpl::TransitionToHaveNothing,
+ weak_factory_.GetWeakPtr()));
}
- // As we resume from background rendering, don't count the initial batch of
+ // We don't count dropped frames in the background to avoid skewing the count
+ // and impacting JavaScript visible metrics used by web developers.
+ //
+ // Just after resuming from background rendering, we also don't count the
// dropped frames since they are likely just dropped due to being too old.
- if (!is_background_rendering_) {
+ if (!background_rendering && !was_background_rendering_)
frames_dropped_ += frames_dropped;
- UpdateStatsAndWait_Locked(base::TimeDelta());
- }
- is_background_rendering_ = false;
+ UpdateStatsAndWait_Locked(base::TimeDelta());
+ was_background_rendering_ = background_rendering;
// After painting the first frame, if playback hasn't started, we request that
// the sink be stopped. OnTimeStateChanged() will clear this flag if time
@@ -232,13 +232,6 @@ void VideoRendererImpl::OnFrameDropped() {
algorithm_->OnLastFrameDropped();
}
-void VideoRendererImpl::SetBackgroundRenderingForTesting(
- bool enabled,
- base::TimeDelta timeout) {
- should_use_background_renderering_ = enabled;
- background_rendering_timeout_ = timeout;
-}
-
void VideoRendererImpl::CreateVideoThread() {
// This may fail and cause a crash if there are too many threads created in
// the current process. See http://crbug.com/443291
@@ -468,8 +461,8 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
received_end_of_stream_ = true;
// See if we can fire EOS immediately instead of waiting for Render() or
- // BackgroundRender() to tick. We also want to fire EOS if we have no
- // frames and received EOS.
+ // to tick. We also want to fire EOS if we have no frames and received
+ // EOS.
if (use_new_video_renderering_path_ &&
(time_progressing_ || !algorithm_->frames_queued())) {
MaybeFireEndedCallback();
@@ -496,12 +489,12 @@ void VideoRendererImpl::FrameReady(VideoFrameStream::Status status,
}
}
- // BackgroundRender may not be ticking fast enough by itself to remove
- // expired frames, so give it a boost here by ensuring we don't exit the
- // decoding cycle too early.
- if (is_background_rendering_) {
+ // Background rendering updates may not be ticking fast enough by itself to
+ // remove expired frames, so give it a boost here by ensuring we don't exit
+ // the decoding cycle too early.
+ if (was_background_rendering_) {
DCHECK(use_new_video_renderering_path_);
- BackgroundRender_Locked();
+ algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks());
}
// Always request more decoded video if we have capacity. This serves two
@@ -528,6 +521,11 @@ bool VideoRendererImpl::HaveEnoughData_Locked() {
if (HaveReachedBufferingCap())
return true;
+ if (use_new_video_renderering_path_ && was_background_rendering_ &&
+ frames_decoded_) {
+ return true;
+ }
+
if (!low_delay_)
return false;
@@ -550,6 +548,17 @@ void VideoRendererImpl::TransitionToHaveEnough_Locked() {
buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH);
}
+void VideoRendererImpl::TransitionToHaveNothing() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ base::AutoLock auto_lock(lock_);
+ if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked())
+ return;
+
+ buffering_state_ = BUFFERING_HAVE_NOTHING;
+ buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
+}
+
void VideoRendererImpl::AddReadyFrame_Locked(
const scoped_refptr<VideoFrame>& frame) {
DCHECK(task_runner_->BelongsToCurrentThread());
@@ -649,67 +658,6 @@ void VideoRendererImpl::MaybeStopSinkAfterFirstPaint() {
StopSink();
}
-void VideoRendererImpl::StartBackgroundRenderTimer() {
- DCHECK(use_new_video_renderering_path_);
- if (!drop_frames_ || !should_use_background_renderering_)
- return;
-
- // |last_render_time_| and |background_rendering_timer_| work in conjunction
- // to ensure we don't exceed |kBackgroundRenderingTimeoutMs| between calls to
- // Render(). BackgroundRender() will tick repeatedly and verify that Render()
- // has fired within the allotted time, by checking |last_render_time_|.
- last_render_time_ = tick_clock_->NowTicks();
- background_rendering_timer_.Start(
- FROM_HERE, background_rendering_timeout_,
- base::Bind(&VideoRendererImpl::BackgroundRender,
- weak_factory_.GetWeakPtr()));
-}
-
-void VideoRendererImpl::BackgroundRender() {
- base::AutoLock auto_lock(lock_);
- BackgroundRender_Locked();
-}
-
-void VideoRendererImpl::BackgroundRender_Locked() {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK_EQ(state_, kPlaying);
- lock_.AssertAcquired();
-
- // If a Render() call never occurs after starting playback for the first frame
- // we need to carry out the duties of Render() and stop the sink. We don't
- // call MaybeStopSinkAfterFirstPaint() since it may need to mutate |sink_|,
- // which can't be done under lock.
- if (render_first_frame_and_stop_) {
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&VideoRendererImpl::MaybeStopSinkAfterFirstPaint,
- weak_factory_.GetWeakPtr()));
-
- // MaybeStopSinkAfterFirstPaint isn't going to stop the sink if time is
- // currently progressing, so only bail out if necessary.
- if (!time_progressing_)
- return;
- }
-
- // Do nothing if Render() calls are progressing normally.
- if (tick_clock_->NowTicks() - last_render_time_ <
- background_rendering_timeout_) {
- return;
- }
-
- // First clear as many expired frames as we can.
- algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks());
- is_background_rendering_ = true;
-
- // See if we've run out of frames and need to fire the ended callback.
- MaybeFireEndedCallback();
- if (rendered_end_of_stream_)
- return;
-
- // Start queuing new frames and scheduled this process again otherwise.
- AttemptRead_Locked();
- UpdateStatsAndWait_Locked(base::TimeDelta());
-}
-
bool VideoRendererImpl::HaveReachedBufferingCap() {
DCHECK(task_runner_->BelongsToCurrentThread());
if (use_new_video_renderering_path_) {
@@ -729,15 +677,14 @@ void VideoRendererImpl::StartSink() {
DCHECK(task_runner_->BelongsToCurrentThread());
sink_->Start(this);
sink_started_ = true;
- StartBackgroundRenderTimer();
+ was_background_rendering_ = false;
}
void VideoRendererImpl::StopSink() {
DCHECK(task_runner_->BelongsToCurrentThread());
sink_->Stop();
sink_started_ = false;
- background_rendering_timer_.Stop();
- is_background_rendering_ = false;
+ was_background_rendering_ = false;
}
size_t VideoRendererImpl::MaybeFireEndedCallback() {
diff --git a/media/renderers/video_renderer_impl.h b/media/renderers/video_renderer_impl.h
index 52a15fd..43ffd86 100644
--- a/media/renderers/video_renderer_impl.h
+++ b/media/renderers/video_renderer_impl.h
@@ -78,18 +78,14 @@ class MEDIA_EXPORT VideoRendererImpl
// VideoRendererSink::RenderCallback implementation.
scoped_refptr<VideoFrame> Render(base::TimeTicks deadline_min,
- base::TimeTicks deadline_max) override;
+ base::TimeTicks deadline_max,
+ bool background_rendering) override;
void OnFrameDropped() override;
void enable_new_video_renderer_for_testing() {
use_new_video_renderering_path_ = true;
}
- // Enables or disables background rendering. If |enabled|, |timeout| is the
- // amount of time to wait after the last Render() call before starting the
- // background rendering mode.
- void SetBackgroundRenderingForTesting(bool enabled, base::TimeDelta timeout);
-
private:
// Creates a dedicated |thread_| for video rendering.
void CreateVideoThread();
@@ -128,6 +124,7 @@ class MEDIA_EXPORT VideoRendererImpl
// Note that having enough data may be due to reaching end of stream.
bool HaveEnoughData_Locked();
void TransitionToHaveEnough_Locked();
+ void TransitionToHaveNothing();
// Runs |statistics_cb_| with |frames_decoded_| and |frames_dropped_|, resets
// them to 0, and then waits on |frame_available_| for up to the
@@ -138,15 +135,6 @@ class MEDIA_EXPORT VideoRendererImpl
// false it Stop() on |sink_|.
void MaybeStopSinkAfterFirstPaint();
- // Resets and primes the |background_rendering_timer_|, when the timer fires
- // it calls the BackgroundRender() method below.
- void StartBackgroundRenderTimer();
-
- // Called by |background_rendering_timer_| when enough time elapses where we
- // haven't seen a Render() call.
- void BackgroundRender();
- void BackgroundRender_Locked();
-
// Returns true if there is no more room for additional buffered frames.
bool HaveReachedBufferingCap();
@@ -183,7 +171,7 @@ class MEDIA_EXPORT VideoRendererImpl
bool low_delay_;
// Queue of incoming frames yet to be painted.
- typedef std::deque<scoped_refptr<VideoFrame> > VideoFrameQueue;
+ typedef std::deque<scoped_refptr<VideoFrame>> VideoFrameQueue;
VideoFrameQueue ready_frames_;
// Keeps track of whether we received the end of stream buffer and finished
@@ -269,17 +257,10 @@ class MEDIA_EXPORT VideoRendererImpl
// timing related information.
scoped_ptr<VideoRendererAlgorithm> algorithm_;
- // Indicates that Render() callbacks from |sink_| have timed out, so we've
- // entered a background rendering mode where dropped frames are not counted.
- bool is_background_rendering_;
-
- // Allows tests to disable the background rendering task.
- bool should_use_background_renderering_;
-
- // Manages expiration of stale video frames if Render() callbacks timeout. Do
- // not access from the thread Render() is called on. Must only be used on
- // |task_runner_|.
- base::RepeatingTimer<VideoRendererImpl> background_rendering_timer_;
+ // Indicates that Render() was called with |background_rendering| set to true,
+ // so we've entered a background rendering mode where dropped frames are not
+ // counted.
+ bool was_background_rendering_;
// Indicates whether or not media time is currently progressing or not.
bool time_progressing_;
@@ -288,13 +269,6 @@ class MEDIA_EXPORT VideoRendererImpl
// that the sink be stopped.
bool render_first_frame_and_stop_;
- // The time to wait for Render() before firing BackgroundRender().
- base::TimeDelta background_rendering_timeout_;
-
- // Updated on every Render() call, checked by |background_rendering_timer_| to
- // determine if background rendering should start.
- base::TimeTicks last_render_time_;
-
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<VideoRendererImpl> weak_factory_;
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc
index 573ff33..f0df681 100644
--- a/media/renderers/video_renderer_impl_unittest.cc
+++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -36,9 +36,6 @@ using ::testing::StrictMock;
namespace media {
-// Threshold for Render() callbacks used for testing; should not be zero.
-static const int kTestBackgroundRenderTimeoutMs = 25;
-
ACTION_P(RunClosure, closure) {
closure.Run();
}
@@ -72,9 +69,6 @@ class VideoRendererImplTest : public testing::TestWithParam<bool> {
renderer_->SetTickClockForTesting(scoped_ptr<base::TickClock>(tick_clock_));
null_video_sink_->set_tick_clock_for_testing(tick_clock_);
- // Disable background rendering for tests by default.
- renderer_->SetBackgroundRenderingForTesting(false, base::TimeDelta());
-
// Start wallclock time at a non-zero value.
AdvanceWallclockTimeInMs(12345);
@@ -152,11 +146,6 @@ class VideoRendererImplTest : public testing::TestWithParam<bool> {
message_loop_.RunUntilIdle();
}
- void SuspendRenderCallbacks() {
- null_video_sink_->PauseRenderCallbacks(base::TimeTicks() +
- base::TimeDelta::Max());
- }
-
// Parses a string representation of video frames and generates corresponding
// VideoFrame objects in |decode_results_|.
//
@@ -300,6 +289,8 @@ class VideoRendererImplTest : public testing::TestWithParam<bool> {
// Must be destroyed before |renderer_| since they share |tick_clock_|.
scoped_ptr<NullVideoSink> null_video_sink_;
+ PipelineStatistics last_pipeline_statistics_;
+
private:
base::TimeTicks GetWallClockTime(base::TimeDelta time) {
base::AutoLock l(lock_);
@@ -333,7 +324,9 @@ class VideoRendererImplTest : public testing::TestWithParam<bool> {
message_loop_.PostTask(FROM_HERE, callback);
}
- void OnStatisticsUpdate(const PipelineStatistics& stats) {}
+ void OnStatisticsUpdate(const PipelineStatistics& stats) {
+ last_pipeline_statistics_ = stats;
+ }
MOCK_METHOD0(OnWaitingForDecryptionKey, void(void));
@@ -588,34 +581,30 @@ TEST_P(VideoRendererImplTest, Underflow) {
Destroy();
}
-// Tests the case where the video started in the background and never received
-// any Render() calls and time never started progressing (so the sink should be
-// stopped immediately).
-TEST_P(VideoRendererImplTest, BackgroundRenderingStopsAfterFirstFrame) {
+// Verifies that the sink is stopped after rendering the first frame if
+// playback hasn't started.
+TEST_P(VideoRendererImplTest, RenderingStopsAfterFirstFrame) {
// This test is only for the new rendering path.
if (!GetParam())
return;
- // By default, tests disable background rendering, so enable it now and give
- // a short, but non-zero, timeout.
- renderer_->SetBackgroundRenderingForTesting(
- true, base::TimeDelta::FromMilliseconds(kTestBackgroundRenderTimeoutMs));
-
InitializeWithLowDelay(true);
QueueFrames("0");
+ EXPECT_CALL(mock_cb_, FrameReceived(HasTimestamp(0)));
EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
- // Pause callbacks forever, but don't start the sink. Ended should not fire.
- SuspendRenderCallbacks();
- StartPlayingFrom(0);
- EXPECT_TRUE(IsReadPending());
- SatisfyPendingReadWithEndOfStream();
-
{
SCOPED_TRACE("Waiting for sink to stop.");
WaitableMessageLoopEvent event;
+
+ null_video_sink_->set_background_render(true);
null_video_sink_->set_stop_cb(event.GetClosure());
+ StartPlayingFrom(0);
+
+ EXPECT_TRUE(IsReadPending());
+ SatisfyPendingReadWithEndOfStream();
+
event.RunAndWait();
}
@@ -623,87 +612,50 @@ TEST_P(VideoRendererImplTest, BackgroundRenderingStopsAfterFirstFrame) {
Destroy();
}
-// Tests the case where the video started in the background and never received
-// any Render() calls, but time is progressing.
-TEST_P(VideoRendererImplTest, BackgroundRenderingNeverStarted) {
+// Verifies that the sink is stopped after rendering the first frame if
+// playback ha started.
+TEST_P(VideoRendererImplTest, RenderingStopsAfterOneFrameWithEOS) {
// This test is only for the new rendering path.
if (!GetParam())
return;
- // By default, tests disable background rendering, so enable it now and give
- // a short, but non-zero, timeout.
- renderer_->SetBackgroundRenderingForTesting(
- true, base::TimeDelta::FromMilliseconds(kTestBackgroundRenderTimeoutMs));
-
- Initialize();
- QueueFrames("0 10 20 30");
+ InitializeWithLowDelay(true);
+ QueueFrames("0");
+ EXPECT_CALL(mock_cb_, FrameReceived(HasTimestamp(0)));
EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
- // Start the sink and pause callbacks forever.
- renderer_->OnTimeStateChanged(true);
- SuspendRenderCallbacks();
- StartPlayingFrom(0);
- AdvanceTimeInMs(41);
- AdvanceWallclockTimeInMs(kTestBackgroundRenderTimeoutMs);
-
- // Eventually background rendering should request new buffers and at that
- // point fire the ended event if rendering has completed.
- WaitForPendingRead();
- SatisfyPendingReadWithEndOfStream();
- WaitForEnded();
- Destroy();
-}
-
-// Tests the case where the video started in the background and never received
-// any Render() calls, but time is progressing and there's only a single frame
-// in the video.
-TEST_P(VideoRendererImplTest, BackgroundRenderingNeverStartedSingleFrame) {
- // This test is only for the new rendering path.
- if (!GetParam())
- return;
-
- // By default, tests disable background rendering, so enable it now and give
- // a short, but non-zero, timeout.
- renderer_->SetBackgroundRenderingForTesting(
- true, base::TimeDelta::FromMilliseconds(kTestBackgroundRenderTimeoutMs));
-
- Initialize();
- QueueFrames("0");
+ {
+ SCOPED_TRACE("Waiting for sink to stop.");
+ WaitableMessageLoopEvent event;
- EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH));
+ null_video_sink_->set_stop_cb(event.GetClosure());
+ StartPlayingFrom(0);
+ renderer_->OnTimeStateChanged(true);
- // Start the sink and pause callbacks forever.
- renderer_->OnTimeStateChanged(true);
- SuspendRenderCallbacks();
- StartPlayingFrom(0);
+ EXPECT_TRUE(IsReadPending());
+ SatisfyPendingReadWithEndOfStream();
- AdvanceWallclockTimeInMs(kTestBackgroundRenderTimeoutMs);
+ event.RunAndWait();
+ }
- // Eventually background rendering should request new buffers and at that
- // point fire the ended event if rendering has completed.
- WaitForPendingRead();
- SatisfyPendingReadWithEndOfStream();
WaitForEnded();
Destroy();
}
// Tests the case where the video started and received a single Render() call,
// then the video was put into the background.
-TEST_P(VideoRendererImplTest, BackgroundRenderingRenderStartedThenStopped) {
+TEST_P(VideoRendererImplTest, RenderingStartedThenStopped) {
// This test is only for the new rendering path.
if (!GetParam())
return;
- // By default, tests disable background rendering, so enable it now and give
- // a short, but non-zero, timeout.
- renderer_->SetBackgroundRenderingForTesting(
- true, base::TimeDelta::FromMilliseconds(kTestBackgroundRenderTimeoutMs));
-
Initialize();
QueueFrames("0 10 20 30");
- // Start the sink and wait for the first callback.
+ // Start the sink and wait for the first callback. Set statistics to a non
+ // zero value, once we have some decoded frames they should be overwritten.
+ last_pipeline_statistics_.video_frames_dropped = 1;
{
WaitableMessageLoopEvent event;
EXPECT_CALL(mock_cb_, FrameReceived(HasTimestamp(0)));
@@ -712,19 +664,26 @@ TEST_P(VideoRendererImplTest, BackgroundRenderingRenderStartedThenStopped) {
StartPlayingFrom(0);
event.RunAndWait();
Mock::VerifyAndClearExpectations(&mock_cb_);
+ EXPECT_EQ(0u, last_pipeline_statistics_.video_frames_dropped);
}
renderer_->OnTimeStateChanged(true);
- // Suspend all future callbacks and synthetically advance the media time.
- SuspendRenderCallbacks();
+ // Suspend all future callbacks and synthetically advance the media time,
+ // because this is a background render, we won't underflow by waiting until
+ // a pending read is ready.
+ null_video_sink_->set_background_render(true);
AdvanceTimeInMs(41);
- AdvanceWallclockTimeInMs(kTestBackgroundRenderTimeoutMs);
-
- // Eventually background rendering should request new buffers and at that
- // point fire the ended event if rendering has completed.
+ EXPECT_CALL(mock_cb_, FrameReceived(HasTimestamp(30)));
WaitForPendingRead();
SatisfyPendingReadWithEndOfStream();
+
+ // If this wasn't background rendering mode, this would result in two frames
+ // being dropped, but since we set background render to true, none should be
+ // reported
+ EXPECT_EQ(0u, last_pipeline_statistics_.video_frames_dropped);
+ EXPECT_EQ(4u, last_pipeline_statistics_.video_frames_decoded);
+
WaitForEnded();
Destroy();
}