summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-16 02:03:43 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-16 02:03:43 +0000
commit5981c66c7b9f8d84471f9f44d3b8a88a5fdd7c2c (patch)
tree0c9934080de2e90972b07f7b7abe18f95e936dcf /media
parent21cef747bba35a718dedc88106a7664df89f67b2 (diff)
downloadchromium_src-5981c66c7b9f8d84471f9f44d3b8a88a5fdd7c2c.zip
chromium_src-5981c66c7b9f8d84471f9f44d3b8a88a5fdd7c2c.tar.gz
chromium_src-5981c66c7b9f8d84471f9f44d3b8a88a5fdd7c2c.tar.bz2
Introduce VideoFrameScheduler{Impl,Proxy}.
VideoFrameScheduler defines an interface for scheduling video frames to be displayed at specific a wall clock time. A single-threaded implementation that uses delayed tasks on a message loop is provided as well as a thread-safe proxy for coordinating scheduling between threads. BUG=110814 Review URL: https://codereview.chromium.org/237093007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@264066 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/video_frame_scheduler.h43
-rw-r--r--media/filters/video_frame_scheduler_impl.cc108
-rw-r--r--media/filters/video_frame_scheduler_impl.h74
-rw-r--r--media/filters/video_frame_scheduler_impl_unittest.cc148
-rw-r--r--media/filters/video_frame_scheduler_proxy.cc47
-rw-r--r--media/filters/video_frame_scheduler_proxy.h51
-rw-r--r--media/media.gyp6
7 files changed, 477 insertions, 0 deletions
diff --git a/media/filters/video_frame_scheduler.h b/media/filters/video_frame_scheduler.h
new file mode 100644
index 0000000..6ccebe7
--- /dev/null
+++ b/media/filters/video_frame_scheduler.h
@@ -0,0 +1,43 @@
+// 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_SCHEDULER_H_
+#define MEDIA_FILTERS_VIDEO_FRAME_SCHEDULER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+class VideoFrame;
+
+// Defines an abstract video frame scheduler that is capable of managing the
+// display of video frames at explicit times.
+class MEDIA_EXPORT VideoFrameScheduler {
+ public:
+ VideoFrameScheduler() {}
+ virtual ~VideoFrameScheduler() {}
+
+ enum Reason {
+ DISPLAYED, // Frame was displayed.
+ DROPPED, // Frame was dropped.
+ RESET, // Scheduler was reset before frame was scheduled for display.
+ };
+ typedef base::Callback<void(const scoped_refptr<VideoFrame>&, Reason)> DoneCB;
+
+ // Schedule |frame| to be displayed at |wall_ticks|, firing |done_cb| when
+ // the scheduler has finished with the frame.
+ virtual void ScheduleVideoFrame(const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks wall_ticks,
+ const DoneCB& done_cb) = 0;
+
+ // Causes the scheduler to release all previously scheduled frames. Frames
+ // will be returned as RESET.
+ virtual void Reset() = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_VIDEO_FRAME_SCHEDULER_H_
diff --git a/media/filters/video_frame_scheduler_impl.cc b/media/filters/video_frame_scheduler_impl.cc
new file mode 100644
index 0000000..a505164
--- /dev/null
+++ b/media/filters/video_frame_scheduler_impl.cc
@@ -0,0 +1,108 @@
+// 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_scheduler_impl.h"
+
+#include <list>
+
+#include "base/single_thread_task_runner.h"
+#include "base/time/default_tick_clock.h"
+#include "media/base/video_frame.h"
+
+namespace media {
+
+VideoFrameSchedulerImpl::VideoFrameSchedulerImpl(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const DisplayCB& display_cb)
+ : task_runner_(task_runner),
+ display_cb_(display_cb),
+ tick_clock_(new base::DefaultTickClock()) {
+}
+
+VideoFrameSchedulerImpl::~VideoFrameSchedulerImpl() {
+}
+
+void VideoFrameSchedulerImpl::ScheduleVideoFrame(
+ const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks wall_ticks,
+ const DoneCB& done_cb) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK(!frame->end_of_stream());
+ pending_frames_.push(PendingFrame(frame, wall_ticks, done_cb));
+ ResetTimerIfNecessary();
+}
+
+void VideoFrameSchedulerImpl::Reset() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ while (!pending_frames_.empty()) {
+ pending_frames_.top().done_cb.Run(pending_frames_.top().frame, RESET);
+ pending_frames_.pop();
+ }
+}
+
+void VideoFrameSchedulerImpl::SetTickClockForTesting(
+ scoped_ptr<base::TickClock> tick_clock) {
+ tick_clock_.swap(tick_clock);
+}
+
+void VideoFrameSchedulerImpl::ResetTimerIfNecessary() {
+ if (pending_frames_.empty()) {
+ DCHECK(!timer_.IsRunning());
+ return;
+ }
+
+ // Negative times will schedule the callback to run immediately.
+ timer_.Stop();
+ timer_.Start(FROM_HERE,
+ pending_frames_.top().wall_ticks - tick_clock_->NowTicks(),
+ base::Bind(&VideoFrameSchedulerImpl::OnTimerFired,
+ base::Unretained(this)));
+}
+
+void VideoFrameSchedulerImpl::OnTimerFired() {
+ base::TimeTicks now = tick_clock_->NowTicks();
+
+ // Move all frames that have reached their deadline into a separate queue.
+ std::list<PendingFrame> expired_frames;
+ while (!pending_frames_.empty() && pending_frames_.top().wall_ticks <= now) {
+ expired_frames.push_back(pending_frames_.top());
+ pending_frames_.pop();
+ }
+
+ // Signal that all frames except for the last one as dropped.
+ while (expired_frames.size() > 1) {
+ expired_frames.front().done_cb.Run(expired_frames.front().frame, DROPPED);
+ expired_frames.pop_front();
+ }
+
+ // Display the last expired frame.
+ if (!expired_frames.empty()) {
+ display_cb_.Run(expired_frames.front().frame);
+ expired_frames.front().done_cb.Run(expired_frames.front().frame, DISPLAYED);
+ expired_frames.pop_front();
+ }
+
+ ResetTimerIfNecessary();
+}
+
+VideoFrameSchedulerImpl::PendingFrame::PendingFrame(
+ const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks wall_ticks,
+ const DoneCB& done_cb)
+ : frame(frame), wall_ticks(wall_ticks), done_cb(done_cb) {
+}
+
+VideoFrameSchedulerImpl::PendingFrame::~PendingFrame() {
+}
+
+bool VideoFrameSchedulerImpl::PendingFrame::operator<(
+ const PendingFrame& other) const {
+ // Flip the comparison as std::priority_queue<T>::top() returns the largest
+ // element.
+ //
+ // Assume video frames with identical timestamps contain identical content.
+ return wall_ticks > other.wall_ticks;
+}
+
+} // namespace media
diff --git a/media/filters/video_frame_scheduler_impl.h b/media/filters/video_frame_scheduler_impl.h
new file mode 100644
index 0000000..f6bc78d
--- /dev/null
+++ b/media/filters/video_frame_scheduler_impl.h
@@ -0,0 +1,74 @@
+// 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_SCHEDULER_IMPL_H_
+#define MEDIA_FILTERS_VIDEO_FRAME_SCHEDULER_IMPL_H_
+
+#include <queue>
+
+#include "base/memory/ref_counted.h"
+#include "base/timer/timer.h"
+#include "media/filters/video_frame_scheduler.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class TickClock;
+}
+
+namespace media {
+
+// A scheduler that uses delayed tasks on a task runner for timing the display
+// of video frames.
+//
+// Single threaded. Calls must be on |task_runner|.
+class MEDIA_EXPORT VideoFrameSchedulerImpl : public VideoFrameScheduler {
+ public:
+ typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> DisplayCB;
+
+ // |task_runner| is used for scheduling the delayed tasks.
+ // |display_cb| is run when a frame is to be displayed.
+ VideoFrameSchedulerImpl(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const DisplayCB& display_cb);
+ virtual ~VideoFrameSchedulerImpl();
+
+ // VideoFrameScheduler implementation.
+ virtual void ScheduleVideoFrame(const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks wall_ticks,
+ const DoneCB& done_cb) OVERRIDE;
+ virtual void Reset() OVERRIDE;
+
+ void SetTickClockForTesting(scoped_ptr<base::TickClock> tick_clock);
+
+ private:
+ void ResetTimerIfNecessary();
+ void OnTimerFired();
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ DisplayCB display_cb_;
+ scoped_ptr<base::TickClock> tick_clock_;
+ base::OneShotTimer<VideoFrameScheduler> timer_;
+
+ struct PendingFrame {
+ PendingFrame(const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks wall_ticks,
+ const DoneCB& done_cb);
+ ~PendingFrame();
+
+ // For use with std::priority_queue<T>.
+ bool operator<(const PendingFrame& other) const;
+
+ scoped_refptr<VideoFrame> frame;
+ base::TimeTicks wall_ticks;
+ DoneCB done_cb;
+ };
+ typedef std::priority_queue<PendingFrame> PendingFrameQueue;
+ PendingFrameQueue pending_frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameSchedulerImpl);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_VIDEO_FRAME_SCHEDULER_IMPL_H_
diff --git a/media/filters/video_frame_scheduler_impl_unittest.cc b/media/filters/video_frame_scheduler_impl_unittest.cc
new file mode 100644
index 0000000..b423776
--- /dev/null
+++ b/media/filters/video_frame_scheduler_impl_unittest.cc
@@ -0,0 +1,148 @@
+// 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/message_loop/message_loop.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "media/base/test_helpers.h"
+#include "media/base/video_frame.h"
+#include "media/filters/video_frame_scheduler_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// NOTE: millisecond-level resolution is used for times as real delayed tasks
+// are posted. Don't use large values if you want to keep tests running fast.
+class VideoFrameSchedulerImplTest : public testing::Test {
+ public:
+ VideoFrameSchedulerImplTest()
+ : scheduler_(message_loop_.message_loop_proxy(),
+ base::Bind(&VideoFrameSchedulerImplTest::OnDisplay,
+ base::Unretained(this))),
+ tick_clock_(new base::SimpleTestTickClock()) {
+ scheduler_.SetTickClockForTesting(scoped_ptr<base::TickClock>(tick_clock_));
+ }
+
+ virtual ~VideoFrameSchedulerImplTest() {}
+
+ MOCK_METHOD1(OnDisplay, void(const scoped_refptr<VideoFrame>&));
+ MOCK_METHOD2(OnFrameDone,
+ void(const scoped_refptr<VideoFrame>&,
+ VideoFrameScheduler::Reason));
+
+ void Schedule(const scoped_refptr<VideoFrame>& frame, int64 target_ms) {
+ scheduler_.ScheduleVideoFrame(
+ frame,
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(target_ms),
+ base::Bind(&VideoFrameSchedulerImplTest::OnFrameDone,
+ base::Unretained(this)));
+ }
+
+ void RunUntilTimeHasElapsed(int64 ms) {
+ WaitableMessageLoopEvent waiter;
+ message_loop_.PostDelayedTask(
+ FROM_HERE, waiter.GetClosure(), base::TimeDelta::FromMilliseconds(ms));
+ waiter.RunAndWait();
+ }
+
+ void AdvanceTime(int64 ms) {
+ tick_clock_->Advance(base::TimeDelta::FromMilliseconds(ms));
+ }
+
+ void Reset() {
+ scheduler_.Reset();
+ }
+
+ private:
+ base::MessageLoop message_loop_;
+ VideoFrameSchedulerImpl scheduler_;
+ base::SimpleTestTickClock* tick_clock_; // Owned by |scheduler_|.
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameSchedulerImplTest);
+};
+
+TEST_F(VideoFrameSchedulerImplTest, ImmediateDisplay) {
+ scoped_refptr<VideoFrame> frame =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ Schedule(frame, 0);
+
+ EXPECT_CALL(*this, OnDisplay(frame));
+ EXPECT_CALL(*this, OnFrameDone(frame, VideoFrameScheduler::DISPLAYED));
+ RunUntilTimeHasElapsed(0);
+}
+
+TEST_F(VideoFrameSchedulerImplTest, EventualDisplay) {
+ scoped_refptr<VideoFrame> frame =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ Schedule(frame, 10);
+
+ // Nothing should happen.
+ RunUntilTimeHasElapsed(10);
+
+ // Now we should get the frame.
+ EXPECT_CALL(*this, OnDisplay(frame));
+ EXPECT_CALL(*this, OnFrameDone(frame, VideoFrameScheduler::DISPLAYED));
+ AdvanceTime(10);
+ RunUntilTimeHasElapsed(10);
+}
+
+TEST_F(VideoFrameSchedulerImplTest, DroppedFrame) {
+ scoped_refptr<VideoFrame> dropped =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ scoped_refptr<VideoFrame> displayed =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ Schedule(dropped, 10);
+ Schedule(displayed, 20);
+
+ // The frame past its deadline will get dropped.
+ EXPECT_CALL(*this, OnDisplay(displayed));
+ EXPECT_CALL(*this, OnFrameDone(dropped, VideoFrameScheduler::DROPPED));
+ EXPECT_CALL(*this, OnFrameDone(displayed, VideoFrameScheduler::DISPLAYED));
+ AdvanceTime(20);
+ RunUntilTimeHasElapsed(20);
+}
+
+TEST_F(VideoFrameSchedulerImplTest, SingleFrameLate) {
+ scoped_refptr<VideoFrame> frame =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ Schedule(frame, 10);
+
+ // Despite frame being late it should still get displayed as it's the only
+ // one.
+ EXPECT_CALL(*this, OnDisplay(frame));
+ EXPECT_CALL(*this, OnFrameDone(frame, VideoFrameScheduler::DISPLAYED));
+ AdvanceTime(20);
+ RunUntilTimeHasElapsed(20);
+}
+
+TEST_F(VideoFrameSchedulerImplTest, ManyFramesLate) {
+ scoped_refptr<VideoFrame> dropped =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ scoped_refptr<VideoFrame> displayed =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ Schedule(dropped, 10);
+ Schedule(displayed, 20);
+
+ // Despite both being late, the scheduler should always displays the latest
+ // expired frame.
+ EXPECT_CALL(*this, OnDisplay(displayed));
+ EXPECT_CALL(*this, OnFrameDone(dropped, VideoFrameScheduler::DROPPED));
+ EXPECT_CALL(*this, OnFrameDone(displayed, VideoFrameScheduler::DISPLAYED));
+ AdvanceTime(30);
+ RunUntilTimeHasElapsed(30);
+}
+
+TEST_F(VideoFrameSchedulerImplTest, Reset) {
+ scoped_refptr<VideoFrame> frame =
+ VideoFrame::CreateBlackFrame(gfx::Size(8, 8));
+ Schedule(frame, 10);
+
+ // Despite being on time, frames are returned immediately.
+ EXPECT_CALL(*this, OnFrameDone(frame, VideoFrameScheduler::RESET));
+ AdvanceTime(10);
+ Reset();
+ RunUntilTimeHasElapsed(10);
+}
+
+} // namespace media
diff --git a/media/filters/video_frame_scheduler_proxy.cc b/media/filters/video_frame_scheduler_proxy.cc
new file mode 100644
index 0000000..7499dc3
--- /dev/null
+++ b/media/filters/video_frame_scheduler_proxy.cc
@@ -0,0 +1,47 @@
+// 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_scheduler_proxy.h"
+
+#include "base/single_thread_task_runner.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/video_frame.h"
+
+namespace media {
+
+VideoFrameSchedulerProxy::VideoFrameSchedulerProxy(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& scheduler_runner,
+ VideoFrameScheduler* scheduler)
+ : task_runner_(task_runner),
+ scheduler_runner_(scheduler_runner),
+ scheduler_(scheduler),
+ weak_factory_(this) {
+}
+
+VideoFrameSchedulerProxy::~VideoFrameSchedulerProxy() {
+}
+
+void VideoFrameSchedulerProxy::ScheduleVideoFrame(
+ const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks wall_ticks,
+ const DoneCB& done_cb) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ scheduler_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoFrameScheduler::ScheduleVideoFrame,
+ base::Unretained(scheduler_),
+ frame,
+ wall_ticks,
+ BindToCurrentLoop(done_cb)));
+}
+
+void VideoFrameSchedulerProxy::Reset() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ scheduler_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&VideoFrameScheduler::Reset, base::Unretained(scheduler_)));
+}
+
+} // namespace media
diff --git a/media/filters/video_frame_scheduler_proxy.h b/media/filters/video_frame_scheduler_proxy.h
new file mode 100644
index 0000000..d17fb56
--- /dev/null
+++ b/media/filters/video_frame_scheduler_proxy.h
@@ -0,0 +1,51 @@
+// 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_SCHEDULER_PROXY_H_
+#define MEDIA_FILTERS_VIDEO_FRAME_SCHEDULER_PROXY_H_
+
+#include "base/memory/weak_ptr.h"
+#include "media/filters/video_frame_scheduler.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+
+// Provides a thread-safe proxy for a VideoFrameScheduler. Typical use is to
+// use a real VideoFrameScheduler on the task runner responsible for graphics
+// display and provide a proxy on the task runner responsible for background
+// video decoding.
+class MEDIA_EXPORT VideoFrameSchedulerProxy : public VideoFrameScheduler {
+ public:
+ // |task_runner| is the runner that this object will be called on.
+ // |scheduler_runner| is the runner that |scheduler| will be called on.
+ // |scheduler| must out-live the lifetime of this object.
+ VideoFrameSchedulerProxy(
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ const scoped_refptr<base::SingleThreadTaskRunner>& scheduler_runner,
+ VideoFrameScheduler* scheduler);
+ virtual ~VideoFrameSchedulerProxy();
+
+ // VideoFrameScheduler implementation.
+ virtual void ScheduleVideoFrame(const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks wall_ticks,
+ const DoneCB& done_cb) OVERRIDE;
+ virtual void Reset() OVERRIDE;
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> scheduler_runner_;
+ VideoFrameScheduler* scheduler_; // Not owned.
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<VideoFrameSchedulerProxy> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameSchedulerProxy);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_VIDEO_FRAME_SCHEDULER_PROXY_H_
diff --git a/media/media.gyp b/media/media.gyp
index 3b03664..2a7d66b 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -413,6 +413,11 @@
'filters/source_buffer_stream.h',
'filters/stream_parser_factory.cc',
'filters/stream_parser_factory.h',
+ 'filters/video_frame_scheduler.h',
+ 'filters/video_frame_scheduler_impl.cc',
+ 'filters/video_frame_scheduler_impl.h',
+ 'filters/video_frame_scheduler_proxy.cc',
+ 'filters/video_frame_scheduler_proxy.h',
'filters/video_renderer_impl.cc',
'filters/video_renderer_impl.h',
'filters/vpx_video_decoder.cc',
@@ -1039,6 +1044,7 @@
'filters/skcanvas_video_renderer_unittest.cc',
'filters/source_buffer_stream_unittest.cc',
'filters/video_decoder_selector_unittest.cc',
+ 'filters/video_frame_scheduler_impl_unittest.cc',
'filters/video_frame_stream_unittest.cc',
'filters/video_renderer_impl_unittest.cc',
'midi/midi_manager_usb_unittest.cc',