summaryrefslogtreecommitdiffstats
path: root/media/filters
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-18 18:57:48 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-18 18:57:48 +0000
commitda7abbb92e3cb6774f91ab37571bca6aaaa7b41a (patch)
tree9e2cb9c3a607c06cb97673108756e66a8001f720 /media/filters
parente940fc1133b038f3cabbe2e67d0c643839b681b8 (diff)
downloadchromium_src-da7abbb92e3cb6774f91ab37571bca6aaaa7b41a.zip
chromium_src-da7abbb92e3cb6774f91ab37571bca6aaaa7b41a.tar.gz
chromium_src-da7abbb92e3cb6774f91ab37571bca6aaaa7b41a.tar.bz2
Use midpoint calculation for frame-exact seeking.
Previously we would use a "good enough" frame without fully taking duration into account. BUG=69499 TEST=media_unittests Review URL: http://codereview.chromium.org/6321005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71676 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/filters')
-rw-r--r--media/filters/video_renderer_base.cc2
-rw-r--r--media/filters/video_renderer_base_unittest.cc160
2 files changed, 112 insertions, 50 deletions
diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc
index 8aee560..c870df5 100644
--- a/media/filters/video_renderer_base.cc
+++ b/media/filters/video_renderer_base.cc
@@ -408,7 +408,7 @@ void VideoRendererBase::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) {
// Discard frames until we reach our desired seek timestamp.
if (state_ == kSeeking && !frame->IsEndOfStream() &&
- (frame->GetTimestamp() + frame->GetDuration()) < seek_timestamp_) {
+ (frame->GetTimestamp() + frame->GetDuration() / 2) < seek_timestamp_) {
frames_queue_done_.push_back(frame);
ScheduleRead_Locked();
} else {
diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc
index fae500e..8ee5b14 100644
--- a/media/filters/video_renderer_base_unittest.cc
+++ b/media/filters/video_renderer_base_unittest.cc
@@ -49,7 +49,8 @@ class VideoRendererBaseTest : public ::testing::Test {
public:
VideoRendererBaseTest()
: renderer_(new MockVideoRendererBase()),
- decoder_(new MockVideoDecoder()) {
+ decoder_(new MockVideoDecoder()),
+ seeking_(false) {
renderer_->set_host(&host_);
// Queue all reads from the decoder.
@@ -80,9 +81,81 @@ class VideoRendererBaseTest : public ::testing::Test {
renderer_->Stop(NewExpectedCallback());
}
+ void Initialize() {
+ // Who knows how many times ThreadMain() will execute!
+ //
+ // TODO(scherkus): really, really, really need to inject a thread into
+ // VideoRendererBase... it makes mocking much harder.
+ EXPECT_CALL(host_, GetTime()).WillRepeatedly(Return(base::TimeDelta()));
+
+ // Expects the video renderer to get duration from the host.
+ EXPECT_CALL(host_, GetDuration())
+ .WillRepeatedly(Return(base::TimeDelta()));
+
+ InSequence s;
+
+ // We expect the video size to be set.
+ EXPECT_CALL(host_, SetVideoSize(kWidth, kHeight));
+
+ // Then our subclass will be asked to initialize.
+ EXPECT_CALL(*renderer_, OnInitialize(_))
+ .WillOnce(Return(true));
+
+ // Initialize, we shouldn't have any reads.
+ renderer_->Initialize(decoder_, NewExpectedCallback());
+ EXPECT_EQ(0u, read_queue_.size());
+
+ // Now seek to trigger prerolling.
+ Seek(0);
+ }
+
+ void Seek(int64 timestamp) {
+ EXPECT_CALL(*renderer_, OnFrameAvailable());
+ EXPECT_FALSE(seeking_);
+
+ // Now seek to trigger prerolling.
+ seeking_ = true;
+ renderer_->Seek(base::TimeDelta::FromMicroseconds(timestamp),
+ NewCallback(this, &VideoRendererBaseTest::OnSeekComplete));
+
+ // Now satisfy the read requests. The callback must be executed in order
+ // to exit the loop since VideoRendererBase can read a few extra frames
+ // after |timestamp| in order to preroll.
+ for (int64 i = 0; seeking_; ++i) {
+ CreateFrame(i * kDuration, kDuration);
+ }
+ }
+
+ void Flush() {
+ renderer_->Pause(NewExpectedCallback());
+
+ EXPECT_CALL(*decoder_, ProvidesBuffer())
+ .WillRepeatedly(Return(true));
+
+ renderer_->Flush(NewExpectedCallback());
+ }
+
+ void CreateFrame(int64 timestamp, int64 duration) {
+ const base::TimeDelta kZero;
+ scoped_refptr<VideoFrame> frame;
+ VideoFrame::CreateFrame(VideoFrame::RGB32, kWidth, kHeight,
+ base::TimeDelta::FromMicroseconds(timestamp),
+ base::TimeDelta::FromMicroseconds(duration),
+ &frame);
+ decoder_->VideoFrameReady(frame);
+ }
+
+ void ExpectCurrentTimestamp(int64 timestamp) {
+ scoped_refptr<VideoFrame> frame;
+ renderer_->GetCurrentFrame(&frame);
+ EXPECT_EQ(timestamp, frame->GetTimestamp().InMicroseconds());
+ renderer_->PutCurrentFrame(frame);
+ }
+
protected:
static const size_t kWidth;
static const size_t kHeight;
+ static const int64 kDuration;
// Fixture members.
scoped_refptr<MockVideoRendererBase> renderer_;
@@ -98,11 +171,19 @@ class VideoRendererBaseTest : public ::testing::Test {
read_queue_.push_back(frame);
}
+ void OnSeekComplete() {
+ EXPECT_TRUE(seeking_);
+ seeking_ = false;
+ }
+
+ bool seeking_;
+
DISALLOW_COPY_AND_ASSIGN(VideoRendererBaseTest);
};
const size_t VideoRendererBaseTest::kWidth = 16u;
const size_t VideoRendererBaseTest::kHeight = 16u;
+const int64 VideoRendererBaseTest::kDuration = 10;
// Test initialization where the decoder's media format is malformed.
TEST_F(VideoRendererBaseTest, Initialize_BadMediaFormat) {
@@ -143,58 +224,39 @@ TEST_F(VideoRendererBaseTest, Initialize_Failed) {
// Test successful initialization and preroll.
TEST_F(VideoRendererBaseTest, Initialize_Successful) {
- // Who knows how many times ThreadMain() will execute!
- //
- // TODO(scherkus): really, really, really need to inject a thread into
- // VideoRendererBase... it makes mocking much harder.
- EXPECT_CALL(host_, GetTime()).WillRepeatedly(Return(base::TimeDelta()));
-
- // Expects the video renderer to get duration from the host.
- EXPECT_CALL(host_, GetDuration())
- .WillRepeatedly(Return(base::TimeDelta()));
-
- InSequence s;
-
- // We expect the video size to be set.
- EXPECT_CALL(host_, SetVideoSize(kWidth, kHeight));
-
- // Then our subclass will be asked to initialize.
- EXPECT_CALL(*renderer_, OnInitialize(_))
- .WillOnce(Return(true));
-
- // Initialize, we shouldn't have any reads.
- renderer_->Initialize(decoder_, NewExpectedCallback());
- EXPECT_EQ(0u, read_queue_.size());
-
- // Verify the following expectations haven't run until we complete the reads.
- EXPECT_CALL(*renderer_, CheckPoint(0));
-
- // We'll expect to get notified once due preroll completing.
- EXPECT_CALL(*renderer_, OnFrameAvailable());
-
- // Now seek to trigger prerolling.
- renderer_->Seek(base::TimeDelta(), NewExpectedCallback());
-
- // Verify our seek callback hasn't been executed yet.
- renderer_->CheckPoint(0);
-
- // Now satisfy the read requests. Our callback should be executed after
- // exiting this loop.
- for (unsigned int i = 0; i < Limits::kMaxVideoFrames; i++) {
- const base::TimeDelta kZero;
- scoped_refptr<VideoFrame> frame;
- VideoFrame::CreateFrame(VideoFrame::RGB32, kWidth, kHeight, kZero,
- kZero, &frame);
- decoder_->VideoFrameReady(frame);
- }
+ Initialize();
+ ExpectCurrentTimestamp(0);
+ Flush();
+}
+TEST_F(VideoRendererBaseTest, Play) {
+ Initialize();
renderer_->Play(NewExpectedCallback());
- renderer_->Pause(NewExpectedCallback());
+ Flush();
+}
- EXPECT_CALL(*decoder_, ProvidesBuffer())
- .WillRepeatedly(Return(true));
+TEST_F(VideoRendererBaseTest, Seek_Exact) {
+ Initialize();
+ Flush();
+ Seek(kDuration * 6);
+ ExpectCurrentTimestamp(kDuration * 6);
+ Flush();
+}
+
+TEST_F(VideoRendererBaseTest, Seek_RoundUp) {
+ Initialize();
+ Flush();
+ Seek(kDuration * 6 - 1);
+ ExpectCurrentTimestamp(kDuration * 6);
+ Flush();
+}
- renderer_->Flush(NewExpectedCallback());
+TEST_F(VideoRendererBaseTest, Seek_RoundDown) {
+ Initialize();
+ Flush();
+ Seek(kDuration * 6 + 1);
+ ExpectCurrentTimestamp(kDuration * 6);
+ Flush();
}
} // namespace media