summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-28 20:36:37 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-28 20:36:37 +0000
commit46564213965397d01fb87975f3bddd597985fcbf (patch)
tree346f9cf4005fcb9865196622f13462065f52a83f /media
parentc2972193bd0d0c2354d0862444b7ce4662d52d77 (diff)
downloadchromium_src-46564213965397d01fb87975f3bddd597985fcbf.zip
chromium_src-46564213965397d01fb87975f3bddd597985fcbf.tar.gz
chromium_src-46564213965397d01fb87975f3bddd597985fcbf.tar.bz2
Implemented a proper clock for audio/video synchronization.
More or less a change to pull out time management from PipelineImpl into a new class ClockImpl. Biggest difference is ClockImpl will use the system clock + linear interpolation to provide a more "precise" representation of the current playback position. BUG=16508 TEST=a/v sync should remain the same, currentTime should report more precise values Review URL: http://codereview.chromium.org/159517 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21882 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/clock.h47
-rw-r--r--media/base/clock_impl.cc65
-rw-r--r--media/base/clock_impl.h60
-rw-r--r--media/base/clock_impl_unittest.cc217
-rw-r--r--media/base/pipeline_impl.cc23
-rw-r--r--media/base/pipeline_impl.h7
-rw-r--r--media/filters/video_renderer_base.cc40
-rw-r--r--media/media.gyp4
8 files changed, 437 insertions, 26 deletions
diff --git a/media/base/clock.h b/media/base/clock.h
new file mode 100644
index 0000000..93af4a9
--- /dev/null
+++ b/media/base/clock.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2009 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.
+//
+// A clock represent a single source of time to allow audio and video streams
+// to synchronize with each other. Clocks essentially track the media time with
+// respect to some other source of time, whether that may be the system clock,
+// audio hardware or some other OS-level API.
+//
+// Clocks start off paused with a playback rate of 1.0f and a media time of 0.
+//
+// TODO(scherkus): Clocks will some day be responsible for executing callbacks
+// given a media time. This will be used primarily by video renderers. For now
+// we'll keep using a poll-and-sleep solution.
+
+#ifndef MEDIA_BASE_CLOCK_H_
+#define MEDIA_BASE_CLOCK_H_
+
+#include "base/time.h"
+
+namespace media {
+
+class Clock {
+ public:
+ // Starts the clock and returns the current media time, which will increase
+ // with respect to the current playback rate.
+ virtual base::TimeDelta Play() = 0;
+
+ // Stops the clock and returns the current media time, which will remain
+ // constant until Play() is called.
+ virtual base::TimeDelta Pause() = 0;
+
+ // Sets a new playback rate. The rate at which the media time will increase
+ // will now change.
+ virtual void SetPlaybackRate(float playback_rate) = 0;
+
+ // Forcefully sets the media time to the given time. This should only be used
+ // where a discontinuity in the media is found (i.e., seeking).
+ virtual void SetTime(const base::TimeDelta& time) = 0;
+
+ // Returns the current elapsed media time.
+ virtual base::TimeDelta Elapsed() const = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_CLOCK_H_
diff --git a/media/base/clock_impl.cc b/media/base/clock_impl.cc
new file mode 100644
index 0000000..cc46568
--- /dev/null
+++ b/media/base/clock_impl.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2009 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/logging.h"
+#include "media/base/clock_impl.h"
+
+namespace media {
+
+ClockImpl::ClockImpl(TimeProvider* time_provider)
+ : time_provider_(time_provider),
+ playing_(false),
+ playback_rate_(1.0f) {
+}
+
+ClockImpl::~ClockImpl() {
+}
+
+base::TimeDelta ClockImpl::Play() {
+ DCHECK(!playing_);
+ reference_ = time_provider_();
+ playing_ = true;
+ return media_time_;
+}
+
+base::TimeDelta ClockImpl::Pause() {
+ DCHECK(playing_);
+ // Save our new accumulated amount of media time.
+ media_time_ = Elapsed();
+ playing_ = false;
+ return media_time_;
+}
+
+void ClockImpl::SetTime(const base::TimeDelta& time) {
+ if (playing_) {
+ reference_ = time_provider_();
+ }
+ media_time_ = time;
+}
+
+void ClockImpl::SetPlaybackRate(float playback_rate) {
+ if (playing_) {
+ base::Time time = time_provider_();
+ media_time_ = ElapsedViaProvidedTime(time);
+ reference_ = time;
+ }
+ playback_rate_ = playback_rate;
+}
+
+base::TimeDelta ClockImpl::Elapsed() const {
+ if (!playing_) {
+ return media_time_;
+ }
+ return ElapsedViaProvidedTime(time_provider_());
+}
+
+base::TimeDelta ClockImpl::ElapsedViaProvidedTime(
+ const base::Time& time) const {
+ // TODO(scherkus): floating point badness scaling time by playback rate.
+ int64 now_us = (time - reference_).InMicroseconds();
+ now_us = static_cast<int64>(now_us * playback_rate_);
+ return media_time_ + base::TimeDelta::FromMicroseconds(now_us);
+}
+
+} // namespace media
diff --git a/media/base/clock_impl.h b/media/base/clock_impl.h
new file mode 100644
index 0000000..689d010
--- /dev/null
+++ b/media/base/clock_impl.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 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.
+//
+// An implementation of Clock based on the system clock. ClockImpl uses linear
+// interpolation to calculate the current media time since the last time
+// SetTime() was called.
+//
+// ClockImpl is not thread-safe and must be externally locked.
+
+#ifndef MEDIA_BASE_CLOCK_IMPL_H_
+#define MEDIA_BASE_CLOCK_IMPL_H_
+
+#include "media/base/clock.h"
+
+namespace media {
+
+// Type for a static function pointer that acts as a time source.
+typedef base::Time(TimeProvider)();
+
+class ClockImpl : public Clock {
+ public:
+ ClockImpl(TimeProvider* time_provider);
+ virtual ~ClockImpl();
+
+ // Clock implementation.
+ virtual base::TimeDelta Play();
+ virtual base::TimeDelta Pause();
+ virtual void SetPlaybackRate(float playback_rate);
+ virtual void SetTime(const base::TimeDelta& time);
+ virtual base::TimeDelta Elapsed() const;
+
+ private:
+ // Returns the current media time treating the given time as the latest
+ // value as returned by |time_provider_|.
+ base::TimeDelta ElapsedViaProvidedTime(const base::Time& time) const;
+
+ // Function returning current time in base::Time units.
+ TimeProvider* time_provider_;
+
+ // Whether the clock is running.
+ bool playing_;
+
+ // The system clock time when this clock last starting playing or had its
+ // time set via SetTime().
+ base::Time reference_;
+
+ // Current accumulated amount of media time. The remaining portion must be
+ // calculated by comparing the system time to the reference time.
+ base::TimeDelta media_time_;
+
+ // Current playback rate.
+ float playback_rate_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClockImpl);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_CLOCK_IMPL_H_
diff --git a/media/base/clock_impl_unittest.cc b/media/base/clock_impl_unittest.cc
new file mode 100644
index 0000000..c23c18ec3
--- /dev/null
+++ b/media/base/clock_impl_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2009 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/logging.h"
+#include "media/base/clock_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::DefaultValue;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace {
+
+// Provide a stream output operator so we can use EXPECT_EQ(...) with TimeDelta.
+//
+// TODO(scherkus): move this into the testing package.
+std::ostream& operator<<(std::ostream& stream, const base::TimeDelta& time) {
+ return (stream << time.ToInternalValue());
+}
+
+} // namespace
+
+namespace media {
+
+class MockTimeProvider {
+ public:
+ MockTimeProvider() {
+ DCHECK(!instance_) << "Only one instance of MockTimeProvider can exist";
+ DCHECK(!DefaultValue<base::Time>::IsSet());
+ instance_ = this;
+ DefaultValue<base::Time>::Set(base::Time::FromInternalValue(0));
+ }
+
+ ~MockTimeProvider() {
+ instance_ = NULL;
+ DefaultValue<base::Time>::Clear();
+ }
+
+ MOCK_METHOD0(Now, base::Time());
+
+ static base::Time StaticNow() {
+ return instance_->Now();
+ }
+
+ private:
+ static MockTimeProvider* instance_;
+ DISALLOW_COPY_AND_ASSIGN(MockTimeProvider);
+};
+
+MockTimeProvider* MockTimeProvider::instance_ = NULL;
+
+TEST(ClockTest, Created) {
+ StrictMock<MockTimeProvider> mock_time;
+ const base::TimeDelta kExpected = base::TimeDelta::FromSeconds(0);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ EXPECT_EQ(kExpected, clock.Elapsed());
+}
+
+TEST(ClockTest, Play_NormalSpeed) {
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(4)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(6)));
+ const base::TimeDelta kZero;
+ const base::TimeDelta kExpected = base::TimeDelta::FromSeconds(2);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ EXPECT_EQ(kZero, clock.Play());
+ EXPECT_EQ(kExpected, clock.Elapsed());
+}
+
+TEST(ClockTest, Play_DoubleSpeed) {
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(4)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(9)));
+ const base::TimeDelta kZero;
+ const base::TimeDelta kExpected = base::TimeDelta::FromSeconds(10);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ clock.SetPlaybackRate(2.0f);
+ EXPECT_EQ(kZero, clock.Play());
+ EXPECT_EQ(kExpected, clock.Elapsed());
+}
+
+TEST(ClockTest, Play_HalfSpeed) {
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(4)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(8)));
+ const base::TimeDelta kZero;
+ const base::TimeDelta kExpected = base::TimeDelta::FromSeconds(2);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ clock.SetPlaybackRate(0.5f);
+ EXPECT_EQ(kZero, clock.Play());
+ EXPECT_EQ(kExpected, clock.Elapsed());
+}
+
+TEST(ClockTest, Play_ZeroSpeed) {
+ // We'll play for 2 seconds at normal speed, 4 seconds at zero speed, and 8
+ // seconds at normal speed:
+ // (1.0 x 2) + (0.0 x 4) + (1.0 x 8) = 10
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(4)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(6)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(10)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(18)));
+ const base::TimeDelta kZero;
+ const base::TimeDelta kExpected = base::TimeDelta::FromSeconds(10);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ EXPECT_EQ(kZero, clock.Play());
+ clock.SetPlaybackRate(0.0f);
+ clock.SetPlaybackRate(1.0f);
+ EXPECT_EQ(kExpected, clock.Elapsed());
+}
+
+TEST(ClockTest, Play_MultiSpeed) {
+ // We'll play for 2 seconds at half speed, 4 seconds at normal speed, and 8
+ // seconds at double speed:
+ // (0.5 x 2) + (1.0 x 4) + (2.0 x 8) = 21
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(4)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(6)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(10)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(18)));
+ const base::TimeDelta kZero;
+ const base::TimeDelta kExpected = base::TimeDelta::FromSeconds(21);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ clock.SetPlaybackRate(0.5f);
+ EXPECT_EQ(kZero, clock.Play());
+ clock.SetPlaybackRate(1.0f);
+ clock.SetPlaybackRate(2.0f);
+ EXPECT_EQ(kExpected, clock.Elapsed());
+}
+
+TEST(ClockTest, Pause) {
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(4)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(8)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(12)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(16)));
+ const base::TimeDelta kZero;
+ const base::TimeDelta kFirstPause = base::TimeDelta::FromSeconds(4);
+ const base::TimeDelta kSecondPause = base::TimeDelta::FromSeconds(8);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ EXPECT_EQ(kZero, clock.Play());
+ EXPECT_EQ(kFirstPause, clock.Pause());
+ EXPECT_EQ(kFirstPause, clock.Elapsed());
+ EXPECT_EQ(kFirstPause, clock.Play());
+ EXPECT_EQ(kSecondPause, clock.Pause());
+ EXPECT_EQ(kSecondPause, clock.Elapsed());
+}
+
+TEST(ClockTest, SetTime_Paused) {
+ // We'll remain paused while we set the time. The time should be simply
+ // updated without accessing the time provider.
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ const base::TimeDelta kFirstTime = base::TimeDelta::FromSeconds(4);
+ const base::TimeDelta kSecondTime = base::TimeDelta::FromSeconds(16);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ clock.SetTime(kFirstTime);
+ EXPECT_EQ(kFirstTime, clock.Elapsed());
+ clock.SetTime(kSecondTime);
+ EXPECT_EQ(kSecondTime, clock.Elapsed());
+}
+
+TEST(ClockTest, SetTime_Playing) {
+ // We'll play for 4 seconds, then set the time to 12, then play for 4 more
+ // seconds. We'll expect a media time of 16.
+ InSequence s;
+ StrictMock<MockTimeProvider> mock_time;
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(4)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(8)));
+ EXPECT_CALL(mock_time, Now())
+ .WillOnce(Return(base::Time::FromDoubleT(12)));
+ const base::TimeDelta kZero;
+ const base::TimeDelta kExepected = base::TimeDelta::FromSeconds(16);
+
+ ClockImpl clock(&MockTimeProvider::StaticNow);
+ EXPECT_EQ(kZero, clock.Play());
+ clock.SetTime(base::TimeDelta::FromSeconds(12));
+ EXPECT_EQ(kExepected, clock.Elapsed());
+}
+
+} // namespace media
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 20bd4cb..4ad6ff8 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -73,6 +73,7 @@ void DecrementCounter(Lock* lock, ConditionVariable* cond_var, int* count) {
PipelineImpl::PipelineImpl(MessageLoop* message_loop)
: message_loop_(message_loop),
+ clock_(&base::Time::Now),
state_(kCreated),
remaining_transitions_(0) {
ResetState();
@@ -201,7 +202,7 @@ void PipelineImpl::SetVolume(float volume) {
base::TimeDelta PipelineImpl::GetCurrentTime() const {
AutoLock auto_lock(lock_);
- return time_;
+ return clock_.Elapsed();
}
base::TimeDelta PipelineImpl::GetBufferedTime() const {
@@ -243,9 +244,10 @@ void PipelineImpl::SetPipelineErrorCallback(PipelineCallback* error_callback) {
void PipelineImpl::ResetState() {
AutoLock auto_lock(lock_);
+ const base::TimeDelta kZero;
running_ = false;
- duration_ = base::TimeDelta();
- buffered_time_ = base::TimeDelta();
+ duration_ = kZero;
+ buffered_time_ = kZero;
buffered_bytes_ = 0;
total_bytes_ = 0;
video_width_ = 0;
@@ -253,7 +255,7 @@ void PipelineImpl::ResetState() {
volume_ = 1.0f;
playback_rate_ = 0.0f;
error_ = PIPELINE_OK;
- time_ = base::TimeDelta();
+ clock_.SetTime(kZero);
rendered_mime_types_.clear();
}
@@ -307,7 +309,7 @@ base::TimeDelta PipelineImpl::GetTime() const {
void PipelineImpl::SetTime(base::TimeDelta time) {
DCHECK(IsRunning());
AutoLock auto_lock(lock_);
- time_ = time;
+ clock_.SetTime(time);
}
void PipelineImpl::SetDuration(base::TimeDelta duration) {
@@ -547,6 +549,7 @@ void PipelineImpl::ErrorChangedTask(PipelineError error) {
void PipelineImpl::PlaybackRateChangedTask(float playback_rate) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
+ clock_.SetPlaybackRate(playback_rate);
for (FilterVector::iterator iter = filters_.begin();
iter != filters_.end();
++iter) {
@@ -591,6 +594,7 @@ void PipelineImpl::SeekTask(base::TimeDelta time,
remaining_transitions_ = filters_.size();
// Kick off seeking!
+ clock_.Pause();
filters_.front()->Pause(
NewCallback(this, &PipelineImpl::OnFilterStateTransition));
}
@@ -610,6 +614,12 @@ void PipelineImpl::FilterStateTransitionTask() {
CHECK(remaining_transitions_ > 0u);
if (--remaining_transitions_ == 0) {
state_ = FindNextState(state_);
+ if (state_ == kSeeking) {
+ clock_.SetTime(seek_timestamp_);
+ } else if (state_ == kStarting) {
+ clock_.Play();
+ }
+
if (StateTransitionsToStarted(state_)) {
remaining_transitions_ = filters_.size();
}
@@ -629,9 +639,6 @@ void PipelineImpl::FilterStateTransitionTask() {
NOTREACHED();
}
} else if (state_ == kStarted) {
- // We've completed the seek, update the time.
- SetTime(seek_timestamp_);
-
// Execute the seek callback, if present. Note that this might be the
// initial callback passed into Start().
if (seek_callback_.get()) {
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index be3ad28..5c84e26 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -16,6 +16,7 @@
#include "base/ref_counted.h"
#include "base/thread.h"
#include "base/time.h"
+#include "media/base/clock_impl.h"
#include "media/base/filter_host.h"
#include "media/base/pipeline.h"
@@ -282,8 +283,10 @@ class PipelineImpl : public Pipeline, public FilterHost {
// the filters.
float playback_rate_;
- // Current playback time. Set by filters.
- base::TimeDelta time_;
+ // Reference clock. Keeps track of current playback time. Uses system
+ // clock and linear interpolation, but can have its time manually set
+ // by filters.
+ ClockImpl clock_;
// Status of the pipeline. Initialized to PIPELINE_OK which indicates that
// the pipeline is operating correctly. Any other value indicates that the
diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc
index 6b4f570..6161852 100644
--- a/media/filters/video_renderer_base.cc
+++ b/media/filters/video_renderer_base.cc
@@ -21,12 +21,15 @@ namespace media {
// difficult sections in the movie and decoding slows down.
static const size_t kMaxFrames = 3;
-// Sleeping for negative amounts actually hangs your thread on Windows!
-static const int64 kMinSleepMilliseconds = 0;
-
-// This equates to ~13.33 fps, which is just under the typical 15 fps that
-// lower quality cameras or shooting modes usually use for video encoding.
-static const int64 kMaxSleepMilliseconds = 75;
+// This equates to ~16.67 fps, which is just slow enough to be tolerable when
+// our video renderer is ahead of the audio playback.
+//
+// A higher value will be a slower frame rate, which looks worse but allows the
+// audio renderer to catch up faster. A lower value will be a smoother frame
+// rate, but results in the video being out of sync for longer.
+//
+// TODO(scherkus): what if the native frame rate is 15 or 10 fps?
+static const int64 kMaxSleepMilliseconds = 60;
VideoRendererBase::VideoRendererBase()
: width_(0),
@@ -236,22 +239,27 @@ void VideoRendererBase::ThreadMain() {
}
}
- // Notify subclass that |current_frame_| has been updated.
- OnFrameAvailable();
-
// Calculate our sleep duration.
base::TimeDelta sleep = CalculateSleepDuration(next_frame, playback_rate);
+ int sleep_ms = static_cast<int>(sleep.InMilliseconds());
+
+ // If we're too far behind to catch up, simply drop the frame.
+ //
+ // This has the effect of potentially dropping a few frames when playback
+ // resumes after being paused. The alternative (sleeping for 0 milliseconds
+ // and trying to catch up) looks worse.
+ if (sleep_ms < 0)
+ continue;
// To be safe, limit our sleep duration.
- // TODO(scherkus): handle seeking gracefully.. right now a seek backwards
- // will hit kMinSleepMilliseconds whereas a seek forward will hit
- // kMaxSleepMilliseconds.
- int sleep_ms = static_cast<int>(sleep.InMilliseconds());
- if (sleep_ms < kMinSleepMilliseconds)
- sleep_ms = kMinSleepMilliseconds;
- else if (sleep_ms > kMaxSleepMilliseconds)
+ // TODO(scherkus): handle seeking gracefully.. right now we tend to hit
+ // kMaxSleepMilliseconds a lot when we seek backwards.
+ if (sleep_ms > kMaxSleepMilliseconds)
sleep_ms = kMaxSleepMilliseconds;
+ // Notify subclass that |current_frame_| has been updated.
+ OnFrameAvailable();
+
PlatformThread::Sleep(sleep_ms);
}
}
diff --git a/media/media.gyp b/media/media.gyp
index 2c8e963..f725862 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -51,6 +51,9 @@
'base/buffer_queue.h',
'base/buffer_queue.cc',
'base/buffers.h',
+ 'base/clock.h',
+ 'base/clock_impl.cc',
+ 'base/clock_impl.h',
'base/data_buffer.cc',
'base/data_buffer.h',
'base/factory.h',
@@ -154,6 +157,7 @@
'audio/simple_sources_unittest.cc',
'audio/win/audio_output_win_unittest.cc',
'base/buffer_queue_unittest.cc',
+ 'base/clock_impl_unittest.cc',
'base/data_buffer_unittest.cc',
'base/mock_ffmpeg.cc',
'base/mock_ffmpeg.h',