summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-09 18:58:07 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-09 18:58:07 +0000
commit8ff8c5de539874d02a0614d3630de2e59d6ca1b7 (patch)
treecb139450234b9840ff97d0517fbfa26eaa606447 /media
parent127ea5b4c481048ad00af9e09617cc0d4c0ed0ba (diff)
downloadchromium_src-8ff8c5de539874d02a0614d3630de2e59d6ca1b7.zip
chromium_src-8ff8c5de539874d02a0614d3630de2e59d6ca1b7.tar.gz
chromium_src-8ff8c5de539874d02a0614d3630de2e59d6ca1b7.tar.bz2
Creating integration tests for media pipeline that use real decoders and demuxers.
TEST=PipelineIntegrationTest Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=116349 Review URL: http://codereview.chromium.org/8968035 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116889 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/clock.cc6
-rw-r--r--media/base/clock.h5
-rw-r--r--media/base/pipeline_impl.cc50
-rw-r--r--media/base/pipeline_impl.h4
-rw-r--r--media/base/test_data_util.cc13
-rw-r--r--media/base/test_data_util.h5
-rw-r--r--media/filters/audio_renderer_base.cc4
-rw-r--r--media/filters/null_audio_renderer.cc73
-rw-r--r--media/filters/null_audio_renderer.h20
-rw-r--r--media/filters/pipeline_integration_test.cc218
-rw-r--r--media/media.gyp3
11 files changed, 325 insertions, 76 deletions
diff --git a/media/base/clock.cc b/media/base/clock.cc
index 61096177..aaa0c13 100644
--- a/media/base/clock.cc
+++ b/media/base/clock.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -17,6 +17,10 @@ Clock::Clock(TimeProvider* time_provider)
Clock::~Clock() {}
+bool Clock::IsPlaying() const {
+ return playing_;
+}
+
base::TimeDelta Clock::Play() {
DCHECK(!playing_);
reference_ = GetTimeFromProvider();
diff --git a/media/base/clock.h b/media/base/clock.h
index 0acf71b..fc1be6d 100644
--- a/media/base/clock.h
+++ b/media/base/clock.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -33,6 +33,9 @@ class MEDIA_EXPORT Clock {
Clock(TimeProvider* time_provider);
~Clock();
+ // Returns true if the clock is running.
+ bool IsPlaying() const;
+
// Starts the clock and returns the current media time, which will increase
// with respect to the current playback rate.
base::TimeDelta Play();
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 5ea444c..eec4351 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
//
@@ -232,9 +232,6 @@ void PipelineImpl::SetPreload(Preload preload) {
}
base::TimeDelta PipelineImpl::GetCurrentTime() const {
- // TODO(scherkus): perhaps replace checking state_ == kEnded with a bool that
- // is set/get under the lock, because this is breaching the contract that
- // |state_| is only accessed on |message_loop_|.
base::AutoLock auto_lock(lock_);
return GetCurrentTime_Locked();
}
@@ -242,9 +239,9 @@ base::TimeDelta PipelineImpl::GetCurrentTime() const {
base::TimeDelta PipelineImpl::GetCurrentTime_Locked() const {
lock_.AssertAcquired();
base::TimeDelta elapsed = clock_->Elapsed();
- if (state_ == kEnded || elapsed > duration_) {
+ if (elapsed > duration_)
return duration_;
- }
+
return elapsed;
}
@@ -487,9 +484,8 @@ void PipelineImpl::SetTime(base::TimeDelta time) {
if (waiting_for_clock_update_) {
if (time < clock_->Elapsed())
return;
- waiting_for_clock_update_ = false;
clock_->SetTime(time);
- clock_->Play();
+ StartClockIfWaitingForTimeUpdate_Locked();
return;
}
clock_->SetTime(time);
@@ -900,8 +896,7 @@ void PipelineImpl::SeekTask(base::TimeDelta time,
// Kick off seeking!
{
base::AutoLock auto_lock(lock_);
- // If we are waiting for a clock update, the clock hasn't been played yet.
- if (!waiting_for_clock_update_)
+ if (clock_->IsPlaying())
clock_->Pause();
}
pipeline_filter_->Pause(
@@ -924,12 +919,10 @@ void PipelineImpl::NotifyEndedTask() {
return;
}
- if (waiting_for_clock_update_) {
- // Start clock since there is no more audio to
- // trigger clock updates.
- waiting_for_clock_update_ = false;
- clock_->Play();
- }
+ // Start clock since there is no more audio to
+ // trigger clock updates.
+ base::AutoLock auto_lock(lock_);
+ StartClockIfWaitingForTimeUpdate_Locked();
}
if (video_renderer_ && !video_renderer_->HasEnded()) {
@@ -938,6 +931,12 @@ void PipelineImpl::NotifyEndedTask() {
// Transition to ended, executing the callback if present.
SetState(kEnded);
+ {
+ base::AutoLock auto_lock(lock_);
+ clock_->Pause();
+ clock_->SetTime(duration_);
+ }
+
if (!ended_callback_.is_null()) {
ended_callback_.Run(status_);
}
@@ -964,6 +963,10 @@ void PipelineImpl::DisableAudioRendererTask() {
demuxer_->OnAudioRendererDisabled();
pipeline_filter_->OnAudioRendererDisabled();
}
+
+ // Start clock since there is no more audio to
+ // trigger clock updates.
+ StartClockIfWaitingForTimeUpdate_Locked();
}
void PipelineImpl::FilterStateTransitionTask() {
@@ -1029,9 +1032,9 @@ void PipelineImpl::FilterStateTransitionTask() {
base::AutoLock auto_lock(lock_);
// We use audio stream to update the clock. So if there is such a stream,
// we pause the clock until we receive a valid timestamp.
- waiting_for_clock_update_ = has_audio_;
- if (!waiting_for_clock_update_)
- clock_->Play();
+ waiting_for_clock_update_ = true;
+ if (!has_audio_)
+ StartClockIfWaitingForTimeUpdate_Locked();
// Start monitoring rate of downloading.
int bitrate = 0;
@@ -1430,4 +1433,13 @@ void PipelineImpl::NotifyCanPlayThrough() {
NotifyNetworkEventTask(CAN_PLAY_THROUGH);
}
+void PipelineImpl::StartClockIfWaitingForTimeUpdate_Locked() {
+ lock_.AssertAcquired();
+ if (!waiting_for_clock_update_)
+ return;
+
+ waiting_for_clock_update_ = false;
+ clock_->Play();
+}
+
} // namespace media
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index be55291..e5652f0 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -349,6 +349,8 @@ class MEDIA_EXPORT PipelineImpl
// needing to pause to buffer.
void NotifyCanPlayThrough();
+ void StartClockIfWaitingForTimeUpdate_Locked();
+
// Message loop used to execute pipeline tasks.
MessageLoop* message_loop_;
diff --git a/media/base/test_data_util.cc b/media/base/test_data_util.cc
index 45a3735..3e6f3fd 100644
--- a/media/base/test_data_util.cc
+++ b/media/base/test_data_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -11,6 +11,17 @@
namespace media {
+std::string GetTestDataURL(const std::string& name) {
+ FilePath file_path;
+ CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
+
+ file_path = file_path.Append(FILE_PATH_LITERAL("media"))
+ .Append(FILE_PATH_LITERAL("test"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .AppendASCII(name);
+ return file_path.MaybeAsASCII();
+}
+
void ReadTestDataFile(const std::string& name, scoped_array<uint8>* buffer,
int* size) {
FilePath file_path;
diff --git a/media/base/test_data_util.h b/media/base/test_data_util.h
index 19f8e7f..142780b 100644
--- a/media/base/test_data_util.h
+++ b/media/base/test_data_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -13,6 +13,9 @@
namespace media {
+// Returns a URL path for a file in the media/test/data directory.
+std::string GetTestDataURL(const std::string& name);
+
// Reads a test file from media/test/data directory and stores it in
// a scoped_array.
//
diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc
index bca260a..049a39a 100644
--- a/media/filters/audio_renderer_base.cc
+++ b/media/filters/audio_renderer_base.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -278,10 +278,12 @@ void AudioRendererBase::ScheduleRead_Locked() {
}
void AudioRendererBase::SetPlaybackRate(float playback_rate) {
+ base::AutoLock auto_lock(lock_);
algorithm_->SetPlaybackRate(playback_rate);
}
float AudioRendererBase::GetPlaybackRate() {
+ base::AutoLock auto_lock(lock_);
return algorithm_->playback_rate();
}
diff --git a/media/filters/null_audio_renderer.cc b/media/filters/null_audio_renderer.cc
index 0d5daf5..d4689aa 100644
--- a/media/filters/null_audio_renderer.cc
+++ b/media/filters/null_audio_renderer.cc
@@ -1,10 +1,11 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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 <algorithm>
#include <cmath>
+#include "base/bind.h"
#include "base/logging.h"
#include "base/threading/platform_thread.h"
#include "media/base/filter_host.h"
@@ -21,45 +22,17 @@ NullAudioRenderer::NullAudioRenderer()
: AudioRendererBase(),
bytes_per_millisecond_(0),
buffer_size_(0),
- thread_(base::kNullThreadHandle),
- shutdown_(false) {
+ thread_("AudioThread") {
}
NullAudioRenderer::~NullAudioRenderer() {
- DCHECK_EQ(base::kNullThreadHandle, thread_);
+ DCHECK(!thread_.IsRunning());
}
void NullAudioRenderer::SetVolume(float volume) {
// Do nothing.
}
-void NullAudioRenderer::ThreadMain() {
- // Loop until we're signaled to stop.
- while (!shutdown_) {
- float sleep_in_milliseconds = 0.0f;
-
- // Only consume buffers when actually playing.
- if (GetPlaybackRate() > 0.0f) {
- size_t bytes = FillBuffer(buffer_.get(),
- buffer_size_,
- base::TimeDelta(),
- true);
-
- // Calculate our sleep duration, taking playback rate into consideration.
- sleep_in_milliseconds =
- floor(bytes / static_cast<float>(bytes_per_millisecond_));
- sleep_in_milliseconds /= GetPlaybackRate();
- } else {
- // If paused, sleep for 10 milliseconds before polling again.
- sleep_in_milliseconds = 10.0f;
- }
-
- // Sleep for at least one millisecond so we don't spin the CPU.
- base::PlatformThread::Sleep(
- std::max(1, static_cast<int>(sleep_in_milliseconds)));
- }
-}
-
bool NullAudioRenderer::OnInitialize(int bits_per_channel,
ChannelLayout channel_layout,
int sample_rate) {
@@ -71,17 +44,41 @@ bool NullAudioRenderer::OnInitialize(int bits_per_channel,
buffer_.reset(new uint8[buffer_size_]);
DCHECK(buffer_.get());
- // It's safe to start the thread now because it simply sleeps when playback
- // rate is 0.0f.
- return base::PlatformThread::Create(0, this, &thread_);
+ if (!thread_.Start())
+ return false;
+
+ thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
+ &NullAudioRenderer::FillBufferTask, this));
+ return true;
}
void NullAudioRenderer::OnStop() {
- shutdown_ = true;
- if (thread_) {
- base::PlatformThread::Join(thread_);
- thread_ = base::kNullThreadHandle;
+ thread_.Stop();
+}
+
+void NullAudioRenderer::FillBufferTask() {
+ int64 sleep_in_milliseconds = 0;
+
+ // Only consume buffers when actually playing.
+ if (GetPlaybackRate() > 0.0f) {
+ size_t bytes = FillBuffer(buffer_.get(),
+ buffer_size_,
+ base::TimeDelta(),
+ true);
+
+ // Calculate our sleep duration, taking playback rate into consideration.
+ sleep_in_milliseconds =
+ bytes / (bytes_per_millisecond_ * GetPlaybackRate());
+ } else {
+ // If paused, sleep for 10 milliseconds before polling again.
+ sleep_in_milliseconds = 10;
}
+
+ // Sleep for at least one millisecond so we don't spin the CPU.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&NullAudioRenderer::FillBufferTask, this),
+ std::max(sleep_in_milliseconds, static_cast<int64>(1)));
}
} // namespace media
diff --git a/media/filters/null_audio_renderer.h b/media/filters/null_audio_renderer.h
index 9f4ed94..9832367 100644
--- a/media/filters/null_audio_renderer.h
+++ b/media/filters/null_audio_renderer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -16,16 +16,14 @@
#include <deque>
#include "base/memory/scoped_ptr.h"
-#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
#include "media/base/buffers.h"
#include "media/base/filters.h"
#include "media/filters/audio_renderer_base.h"
namespace media {
-class MEDIA_EXPORT NullAudioRenderer
- : public AudioRendererBase,
- public base::PlatformThread::Delegate {
+class MEDIA_EXPORT NullAudioRenderer : public AudioRendererBase {
public:
NullAudioRenderer();
virtual ~NullAudioRenderer();
@@ -33,9 +31,6 @@ class MEDIA_EXPORT NullAudioRenderer
// AudioRenderer implementation.
virtual void SetVolume(float volume) OVERRIDE;
- // PlatformThread::Delegate implementation.
- virtual void ThreadMain() OVERRIDE;
-
protected:
// AudioRendererBase implementation.
virtual bool OnInitialize(int bits_per_channel,
@@ -44,6 +39,10 @@ class MEDIA_EXPORT NullAudioRenderer
virtual void OnStop() OVERRIDE;
private:
+ // Audio thread task that periodically calls FillBuffer() to consume
+ // audio data.
+ void FillBufferTask();
+
// A number to convert bytes written in FillBuffer to milliseconds based on
// the audio format.
size_t bytes_per_millisecond_;
@@ -53,10 +52,7 @@ class MEDIA_EXPORT NullAudioRenderer
size_t buffer_size_;
// Separate thread used to throw away data.
- base::PlatformThreadHandle thread_;
-
- // Shutdown flag.
- bool shutdown_;
+ base::Thread thread_;
DISALLOW_COPY_AND_ASSIGN(NullAudioRenderer);
};
diff --git a/media/filters/pipeline_integration_test.cc b/media/filters/pipeline_integration_test.cc
new file mode 100644
index 0000000..6f99590
--- /dev/null
+++ b/media/filters/pipeline_integration_test.cc
@@ -0,0 +1,218 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "media/base/filter_collection.h"
+#include "media/base/media_log.h"
+#include "media/base/message_loop_factory_impl.h"
+#include "media/base/pipeline_impl.h"
+#include "media/base/test_data_util.h"
+#include "media/filters/ffmpeg_audio_decoder.h"
+#include "media/filters/ffmpeg_demuxer_factory.h"
+#include "media/filters/ffmpeg_video_decoder.h"
+#include "media/filters/file_data_source_factory.h"
+#include "media/filters/null_audio_renderer.h"
+#include "media/filters/video_renderer_base.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AnyNumber;
+
+namespace media {
+
+// Integration tests for PipelineImpl. Real demuxers, real decoders, and
+// base renderer implementations are used to verify pipeline functionality. The
+// renderers used in these tests rely heavily on the AudioRendererBase &
+// VideoRendererBase implementations which contain a majority of the code used
+// in the real AudioRendererImpl & VideoRendererImpl implementations used in the
+// browser. The renderers in this test don't actually write data to a display or
+// audio device. Both of these devices are simulated since they have little
+// effect on verifying pipeline behavior and allow tests to run faster than
+// real-time.
+class PipelineIntegrationTest : public testing::Test {
+ public:
+ PipelineIntegrationTest()
+ : message_loop_factory_(new MessageLoopFactoryImpl()),
+ pipeline_(new PipelineImpl(&message_loop_, new MediaLog())),
+ ended_(false) {
+ pipeline_->Init(
+ base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)),
+ base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)),
+ Pipeline::NetworkEventCB());
+
+ EXPECT_CALL(*this, OnVideoRendererPaint()).Times(AnyNumber());
+ EXPECT_CALL(*this, OnSetOpaque(true));
+ }
+
+ virtual ~PipelineIntegrationTest() {
+ if (!pipeline_->IsRunning())
+ return;
+
+ Stop();
+ }
+
+ void OnStatusCallback(PipelineStatus expected_status,
+ PipelineStatus status) {
+ DCHECK_EQ(status, expected_status);
+ message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+
+ PipelineStatusCB QuitOnStatusCB(PipelineStatus expected_status) {
+ return base::Bind(&PipelineIntegrationTest::OnStatusCallback,
+ base::Unretained(this),
+ expected_status);
+ }
+
+ void OnEnded(PipelineStatus status) {
+ DCHECK_EQ(status, PIPELINE_OK);
+ DCHECK(!ended_);
+ ended_ = true;
+ message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ }
+
+ void WaitUntilOnEnded() {
+ if (!ended_) {
+ message_loop_.Run();
+ DCHECK(ended_);
+ }
+ }
+
+ MOCK_METHOD1(OnError, void(PipelineStatus));
+
+ void Start(const std::string& url, PipelineStatus expected_status) {
+ pipeline_->Start(CreateFilterCollection(), url,
+ QuitOnStatusCB(expected_status));
+ message_loop_.Run();
+ }
+
+ void Play() {
+ pipeline_->SetPlaybackRate(1);
+ }
+
+ void Pause() {
+ pipeline_->SetPlaybackRate(0);
+ }
+
+ void Seek(base::TimeDelta seek_time) {
+ ended_ = false;
+
+ pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK));
+ message_loop_.Run();
+ }
+
+ void Stop() {
+ DCHECK(pipeline_->IsRunning());
+ pipeline_->Stop(QuitOnStatusCB(PIPELINE_OK));
+ message_loop_.Run();
+ }
+
+ void QuitAfterCurrentTimeTask(const base::TimeDelta& quit_time) {
+ if (pipeline_->GetCurrentTime() >= quit_time) {
+ message_loop_.Quit();
+ return;
+ }
+
+ message_loop_.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&PipelineIntegrationTest::QuitAfterCurrentTimeTask,
+ base::Unretained(this), quit_time),
+ 10);
+ }
+
+ void WaitUntilCurrentTimeIsAfter(const base::TimeDelta& wait_time) {
+ DCHECK(pipeline_->IsRunning());
+ DCHECK_GT(pipeline_->GetPlaybackRate(), 0);
+ DCHECK(wait_time <= pipeline_->GetMediaDuration());
+
+ message_loop_.PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&PipelineIntegrationTest::QuitAfterCurrentTimeTask,
+ base::Unretained(this),
+ wait_time),
+ 10);
+ message_loop_.Run();
+ }
+
+ FilterCollection* CreateFilterCollection() {
+ scoped_ptr<FilterCollection> collection(
+ new FilterCollection());
+ collection->SetDemuxerFactory(
+ new FFmpegDemuxerFactory(new FileDataSourceFactory(),
+ &message_loop_));
+ collection->AddAudioDecoder(new FFmpegAudioDecoder(
+ message_loop_factory_->GetMessageLoop("AudioDecoderThread")));
+ collection->AddVideoDecoder(new FFmpegVideoDecoder(
+ message_loop_factory_->GetMessageLoop("VideoDecoderThread")));
+ collection->AddVideoRenderer(new VideoRendererBase(
+ base::Bind(&PipelineIntegrationTest::OnVideoRendererPaint,
+ base::Unretained(this)),
+ base::Bind(&PipelineIntegrationTest::OnSetOpaque,
+ base::Unretained(this))));
+ collection->AddAudioRenderer(new NullAudioRenderer());
+ return collection.release();
+ }
+
+ protected:
+ MessageLoop message_loop_;
+ scoped_ptr<MessageLoopFactory> message_loop_factory_;
+ scoped_refptr<Pipeline> pipeline_;
+ bool ended_;
+
+ private:
+ MOCK_METHOD0(OnVideoRendererPaint, void());
+ MOCK_METHOD1(OnSetOpaque, void(bool));
+};
+
+
+TEST_F(PipelineIntegrationTest, BasicPlayback) {
+ Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK);
+
+ Play();
+
+ WaitUntilOnEnded();
+}
+
+TEST_F(PipelineIntegrationTest, SeekWhilePaused) {
+ Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK);
+
+ base::TimeDelta duration(pipeline_->GetMediaDuration());
+ base::TimeDelta start_seek_time(duration / 4);
+ base::TimeDelta seek_time(duration * 3 / 4);
+
+ Play();
+ WaitUntilCurrentTimeIsAfter(start_seek_time);
+ Pause();
+ Seek(seek_time);
+ EXPECT_EQ(pipeline_->GetCurrentTime(), seek_time);
+ Play();
+ WaitUntilOnEnded();
+
+ // Make sure seeking after reaching the end works as expected.
+ Pause();
+ Seek(seek_time);
+ EXPECT_EQ(pipeline_->GetCurrentTime(), seek_time);
+ Play();
+ WaitUntilOnEnded();
+}
+
+TEST_F(PipelineIntegrationTest, SeekWhilePlaying) {
+ Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK);
+
+ base::TimeDelta duration(pipeline_->GetMediaDuration());
+ base::TimeDelta start_seek_time(duration / 4);
+ base::TimeDelta seek_time(duration * 3 / 4);
+
+ Play();
+ WaitUntilCurrentTimeIsAfter(start_seek_time);
+ Seek(seek_time);
+ EXPECT_GE(pipeline_->GetCurrentTime(), seek_time);
+ WaitUntilOnEnded();
+
+ // Make sure seeking after reaching the end works as expected.
+ Seek(seek_time);
+ EXPECT_GE(pipeline_->GetCurrentTime(), seek_time);
+ WaitUntilOnEnded();
+}
+
+} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index b28fdac..d218f32 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Copyright (c) 2012 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.
@@ -621,6 +621,7 @@
'filters/ffmpeg_h264_bitstream_converter_unittest.cc',
'filters/ffmpeg_video_decoder_unittest.cc',
'filters/file_data_source_unittest.cc',
+ 'filters/pipeline_integration_test.cc',
'filters/video_renderer_base_unittest.cc',
'video/capture/video_capture_device_unittest.cc',
'webm/cluster_builder.cc',