summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/base/filters.h19
-rw-r--r--media/base/mock_filters.h2
-rw-r--r--media/base/pipeline.h4
-rw-r--r--media/base/pipeline_impl.cc32
-rw-r--r--media/base/pipeline_impl.h15
-rw-r--r--media/filters/adaptive_demuxer.cc2
-rw-r--r--media/filters/adaptive_demuxer.h1
-rw-r--r--media/filters/ffmpeg_demuxer.cc10
-rw-r--r--media/filters/ffmpeg_demuxer.h2
-rw-r--r--media/filters/file_data_source.cc2
-rw-r--r--media/filters/file_data_source.h1
-rw-r--r--webkit/glue/media/buffered_data_source.cc50
-rw-r--r--webkit/glue/media/buffered_data_source.h17
-rw-r--r--webkit/glue/media/buffered_resource_loader.cc119
-rw-r--r--webkit/glue/media/buffered_resource_loader.h37
-rw-r--r--webkit/glue/media/buffered_resource_loader_unittest.cc136
-rw-r--r--webkit/glue/media/simple_data_source.cc2
-rw-r--r--webkit/glue/media/simple_data_source.h1
-rw-r--r--webkit/glue/webmediaplayer_impl.cc14
-rw-r--r--webkit/glue/webmediaplayer_impl.h2
20 files changed, 314 insertions, 154 deletions
diff --git a/media/base/filters.h b/media/base/filters.h
index ebe46e5..7d56114 100644
--- a/media/base/filters.h
+++ b/media/base/filters.h
@@ -45,6 +45,19 @@ class FilterHost;
struct PipelineStatistics;
+// Used to specify video preload states. They are "hints" to the browser about
+// how aggressively the browser should load and buffer data.
+// Please see the HTML5 spec for the descriptions of these values:
+// http://www.w3.org/TR/html5/video.html#attr-media-preload
+//
+// Enum values must match the values in WebCore::MediaPlayer::Preload and
+// there will be assertions at compile time if they do not match.
+enum Preload {
+ NONE,
+ METADATA,
+ AUTO,
+};
+
// Used for completing asynchronous methods.
typedef Callback0::Type FilterCallback;
@@ -129,6 +142,9 @@ class DataSource : public Filter {
// Returns true if we are performing streaming. In this case seeking is
// not possible.
virtual bool IsStreaming() = 0;
+
+ // Alert the DataSource that the video preload value has been changed.
+ virtual void SetPreload(Preload preload) = 0;
};
class DemuxerStream : public base::RefCountedThreadSafe<DemuxerStream> {
@@ -166,6 +182,9 @@ class Demuxer : public Filter {
public:
// Returns the given stream type, or NULL if that type is not present.
virtual scoped_refptr<DemuxerStream> GetStream(DemuxerStream::Type type) = 0;
+
+ // Alert the Demuxer that the video preload value has been changed.
+ virtual void SetPreload(Preload preload) = 0;
};
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 205218f..636b60f 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -81,6 +81,7 @@ class MockDataSource : public DataSource {
MOCK_METHOD4(Read, void(int64 position, size_t size, uint8* data,
DataSource::ReadCallback* callback));
MOCK_METHOD1(GetSize, bool(int64* size_out));
+ MOCK_METHOD1(SetPreload, void(Preload preload));
MOCK_METHOD0(IsStreaming, bool());
// Sets the TotalBytes & BufferedBytes values to be sent to host() when
@@ -104,6 +105,7 @@ class MockDemuxer : public Demuxer {
virtual void set_host(FilterHost* host);
MOCK_METHOD1(Stop, void(FilterCallback* callback));
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
+ MOCK_METHOD1(SetPreload, void(Preload preload));
MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback));
MOCK_METHOD0(OnAudioRendererDisabled, void());
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index 0df1bd3..ca7727a 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -12,6 +12,7 @@
#include <string>
#include "base/callback.h"
+#include "media/base/filters.h"
#include "media/base/pipeline_status.h"
namespace base {
@@ -124,6 +125,9 @@ class Pipeline : public base::RefCountedThreadSafe<Pipeline> {
// channels proportionately for multi-channel audio streams.
virtual void SetVolume(float volume) = 0;
+ // Set the preload value for the pipeline.
+ virtual void SetPreload(Preload preload) = 0;
+
// Gets the current pipeline time. For a pipeline "time" progresses from 0 to
// the end of the media.
virtual base::TimeDelta GetCurrentTime() const = 0;
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 7f5f3f3..2555bd7 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -55,7 +55,6 @@ media::PipelineStatus PipelineStatusNotification::status() {
class PipelineImpl::PipelineInitState {
public:
- scoped_refptr<Demuxer> demuxer_;
scoped_refptr<AudioDecoder> audio_decoder_;
scoped_refptr<VideoDecoder> video_decoder_;
scoped_refptr<CompositeFilter> composite_;
@@ -219,6 +218,21 @@ void PipelineImpl::SetVolume(float volume) {
}
}
+Preload PipelineImpl::GetPreload() const {
+ base::AutoLock auto_lock(lock_);
+ return preload_;
+}
+
+void PipelineImpl::SetPreload(Preload preload) {
+ base::AutoLock auto_lock(lock_);
+ preload_ = preload;
+ if (running_) {
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &PipelineImpl::PreloadChangedTask,
+ 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
@@ -630,7 +644,7 @@ void PipelineImpl::InitializeTask() {
if (state_ == kInitDemuxer) {
set_state(kInitAudioDecoder);
// If this method returns false, then there's no audio stream.
- if (InitializeAudioDecoder(pipeline_init_state_->demuxer_))
+ if (InitializeAudioDecoder(demuxer_))
return;
}
@@ -649,7 +663,7 @@ void PipelineImpl::InitializeTask() {
if (state_ == kInitAudioRenderer) {
// Then perform the stage of initialization, i.e. initialize video decoder.
set_state(kInitVideoDecoder);
- if (InitializeVideoDecoder(pipeline_init_state_->demuxer_))
+ if (InitializeVideoDecoder(demuxer_))
return;
}
@@ -685,6 +699,7 @@ void PipelineImpl::InitializeTask() {
// Initialization was successful, we are now considered paused, so it's safe
// to set the initial playback rate and volume.
+ PreloadChangedTask(GetPreload());
PlaybackRateChangedTask(GetPlaybackRate());
VolumeChangedTask(GetVolume());
@@ -785,6 +800,12 @@ void PipelineImpl::VolumeChangedTask(float volume) {
}
}
+void PipelineImpl::PreloadChangedTask(Preload preload) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ if (demuxer_)
+ demuxer_->SetPreload(preload);
+}
+
void PipelineImpl::SeekTask(base::TimeDelta time,
PipelineStatusCallback* seek_callback) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
@@ -994,9 +1015,10 @@ void PipelineImpl::FinishDestroyingFiltersTask() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
DCHECK(IsPipelineStopped());
- // Clear renderer references.
+ // Clear filter references.
audio_renderer_ = NULL;
video_renderer_ = NULL;
+ demuxer_ = NULL;
pipeline_filter_ = NULL;
@@ -1057,7 +1079,7 @@ void PipelineImpl::OnDemuxerBuilt(PipelineStatus status, Demuxer* demuxer) {
if (!PrepareFilter(demuxer))
return;
- pipeline_init_state_->demuxer_ = demuxer;
+ demuxer_ = demuxer;
OnFilterInitialize();
}
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index 41fa234..5dcbcea 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -114,6 +114,7 @@ class PipelineImpl : public Pipeline, public FilterHost {
virtual void SetPlaybackRate(float playback_rate);
virtual float GetVolume() const;
virtual void SetVolume(float volume);
+ virtual void SetPreload(Preload preload);
virtual base::TimeDelta GetCurrentTime() const;
virtual base::TimeDelta GetBufferedTime();
virtual base::TimeDelta GetMediaDuration() const;
@@ -240,6 +241,12 @@ class PipelineImpl : public Pipeline, public FilterHost {
// Carries out notifying filters that the volume has changed.
void VolumeChangedTask(float volume);
+ // Returns media preload value.
+ virtual Preload GetPreload() const;
+
+ // Carries out notifying filters that the preload value has changed.
+ void PreloadChangedTask(Preload preload);
+
// Carries out notifying filters that we are seeking to a new timestamp.
void SeekTask(base::TimeDelta time, PipelineStatusCallback* seek_callback);
@@ -355,6 +362,11 @@ class PipelineImpl : public Pipeline, public FilterHost {
// filters.
float volume_;
+ // Current value of preload attribute. This value is set immediately via
+ // SetPreload() and a task is dispatched on the message loop to notify the
+ // filters.
+ Preload preload_;
+
// Current playback rate (>= 0.0f). This value is set immediately via
// SetPlaybackRate() and a task is dispatched on the message loop to notify
// the filters.
@@ -423,6 +435,9 @@ class PipelineImpl : public Pipeline, public FilterHost {
scoped_refptr<AudioRenderer> audio_renderer_;
scoped_refptr<VideoRenderer> video_renderer_;
+ // Demuxer reference used for setting the preload value.
+ scoped_refptr<Demuxer> demuxer_;
+
// Helper class that stores filter references during pipeline
// initialization.
class PipelineInitState;
diff --git a/media/filters/adaptive_demuxer.cc b/media/filters/adaptive_demuxer.cc
index 0267c11..09a1e67 100644
--- a/media/filters/adaptive_demuxer.cc
+++ b/media/filters/adaptive_demuxer.cc
@@ -229,6 +229,8 @@ void AdaptiveDemuxer::set_host(FilterHost* filter_host) {
if (video && audio != video) video->set_host(filter_host);
}
+void AdaptiveDemuxer::SetPreload(Preload preload) {}
+
scoped_refptr<DemuxerStream> AdaptiveDemuxer::GetStream(
DemuxerStream::Type type) {
switch (type) {
diff --git a/media/filters/adaptive_demuxer.h b/media/filters/adaptive_demuxer.h
index 23cd570..937e01c 100644
--- a/media/filters/adaptive_demuxer.h
+++ b/media/filters/adaptive_demuxer.h
@@ -82,6 +82,7 @@ class AdaptiveDemuxer : public Demuxer {
virtual void Seek(base::TimeDelta time, FilterCallback* callback);
virtual void OnAudioRendererDisabled();
virtual void set_host(FilterHost* filter_host);
+ virtual void SetPreload(Preload preload);
// TODO(fischman): add support for SetPlaybackRate().
// Demuxer implementation.
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 15df542..b7d53bc 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -301,6 +301,16 @@ void FFmpegDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) {
NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, callback));
}
+void FFmpegDemuxer::SetPlaybackRate(float playback_rate) {
+ DCHECK(data_source_.get());
+ data_source_->SetPlaybackRate(playback_rate);
+}
+
+void FFmpegDemuxer::SetPreload(Preload preload) {
+ DCHECK(data_source_.get());
+ data_source_->SetPreload(preload);
+}
+
void FFmpegDemuxer::OnAudioRendererDisabled() {
message_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &FFmpegDemuxer::DisableAudioStreamTask));
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 9dc5800..e65cb34 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -130,6 +130,8 @@ class FFmpegDemuxer : public Demuxer,
virtual void Seek(base::TimeDelta time, FilterCallback* callback);
virtual void OnAudioRendererDisabled();
virtual void set_host(FilterHost* filter_host);
+ virtual void SetPlaybackRate(float playback_rate);
+ virtual void SetPreload(Preload preload);
// Demuxer implementation.
virtual scoped_refptr<DemuxerStream> GetStream(DemuxerStream::Type type);
diff --git a/media/filters/file_data_source.cc b/media/filters/file_data_source.cc
index 98a434d..da03e98 100644
--- a/media/filters/file_data_source.cc
+++ b/media/filters/file_data_source.cc
@@ -116,4 +116,6 @@ bool FileDataSource::IsStreaming() {
return false;
}
+void FileDataSource::SetPreload(Preload preload) {}
+
} // namespace media
diff --git a/media/filters/file_data_source.h b/media/filters/file_data_source.h
index 0539a36..27e0d0e 100644
--- a/media/filters/file_data_source.h
+++ b/media/filters/file_data_source.h
@@ -32,6 +32,7 @@ class FileDataSource : public DataSource {
ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
virtual bool IsStreaming();
+ virtual void SetPreload(Preload preload);
private:
// Only allow factories and tests to create this object.
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index d0d42aa..03b2a0a 100644
--- a/webkit/glue/media/buffered_data_source.cc
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -64,6 +64,8 @@ BufferedDataSource::BufferedDataSource(
stop_signal_received_(false),
stopped_on_render_loop_(false),
media_is_paused_(true),
+ media_has_played_(false),
+ preload_(media::METADATA),
using_range_request_(true) {
}
@@ -154,6 +156,12 @@ void BufferedDataSource::SetPlaybackRate(float playback_rate) {
playback_rate));
}
+void BufferedDataSource::SetPreload(media::Preload preload) {
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::SetPreloadTask,
+ preload));
+}
+
/////////////////////////////////////////////////////////////////////////////
// media::DataSource implementation.
void BufferedDataSource::Read(int64 position, size_t size, uint8* data,
@@ -310,7 +318,8 @@ void BufferedDataSource::RestartLoadingTask() {
}
loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
- loader_->SetAllowDefer(!media_is_paused_);
+ BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
+ loader_->UpdateDeferStrategy(strategy);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
NewCallback(this, &BufferedDataSource::NetworkEventCallback),
@@ -346,7 +355,8 @@ void BufferedDataSource::WatchDogTask() {
// retry the request.
loader_->Stop();
loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
- loader_->SetAllowDefer(!media_is_paused_);
+ BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
+ loader_->UpdateDeferStrategy(strategy);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
NewCallback(this, &BufferedDataSource::NetworkEventCallback),
@@ -360,13 +370,37 @@ void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
bool previously_paused = media_is_paused_;
media_is_paused_ = (playback_rate == 0.0);
- // Disallow deferring data when we are pausing, allow deferring data
- // when we resume playing.
- if (previously_paused && !media_is_paused_) {
- loader_->SetAllowDefer(true);
- } else if (!previously_paused && media_is_paused_) {
- loader_->SetAllowDefer(false);
+ if (!media_has_played_ && previously_paused && !media_is_paused_)
+ media_has_played_ = true;
+
+ BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
+ loader_->UpdateDeferStrategy(strategy);
+}
+
+void BufferedDataSource::SetPreloadTask(media::Preload preload) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ preload_ = preload;
+}
+
+BufferedResourceLoader::DeferStrategy
+BufferedDataSource::ChooseDeferStrategy() {
+ // If the user indicates preload=metadata, then just load exactly
+ // what is needed for starting the pipeline and prerolling frames.
+ if (preload_ == media::METADATA && !media_has_played_)
+ return BufferedResourceLoader::kReadThenDefer;
+
+ // In general, we want to try to buffer the entire video when the video
+ // is paused. But we don't want to do this if the video hasn't played yet
+ // and preload!=auto.
+ if (media_is_paused_ &&
+ (preload_ == media::AUTO || media_has_played_)) {
+ return BufferedResourceLoader::kNeverDefer;
}
+
+ // When the video is playing, regardless of preload state, we buffer up
+ // to a hard limit and enable/disable deferring when the buffer is
+ // depleted/full.
+ return BufferedResourceLoader::kThresholdDefer;
}
// This method is the place where actual read happens, |loader_| must be valid
diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h
index 2c9e904..933ffb9 100644
--- a/webkit/glue/media/buffered_data_source.h
+++ b/webkit/glue/media/buffered_data_source.h
@@ -11,6 +11,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "media/base/filter_factories.h"
+#include "media/base/filters.h"
#include "webkit/glue/media/buffered_resource_loader.h"
namespace webkit_glue {
@@ -40,6 +41,7 @@ class BufferedDataSource : public WebDataSource {
media::DataSource::ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
virtual bool IsStreaming();
+ virtual void SetPreload(media::Preload preload);
const media::MediaFormat& media_format() {
return media_format_;
@@ -90,6 +92,13 @@ class BufferedDataSource : public WebDataSource {
// and signals the buffered resource loader accordingly.
void SetPlaybackRateTask(float playback_rate);
+ // This task saves the preload value for the media.
+ void SetPreloadTask(media::Preload preload);
+
+ // Decides which DeferStrategy to used based on the current state of the
+ // BufferedDataSource.
+ BufferedResourceLoader::DeferStrategy ChooseDeferStrategy();
+
// The method that performs actual read. This method can only be executed on
// the render thread.
void ReadInternal();
@@ -196,6 +205,14 @@ class BufferedDataSource : public WebDataSource {
// are in a playing state.
bool media_is_paused_;
+ // This variable is true when the user has requested the video to play at
+ // least once.
+ bool media_has_played_;
+
+ // This variable holds the value of the preload attribute for the video
+ // element.
+ media::Preload preload_;
+
// This timer is to run the WatchDogTask repeatedly. We use a timer instead
// of doing PostDelayedTask() reduce the extra reference held by the message
// loop. The RepeatingTimer does PostDelayedTask() internally, by using it
diff --git a/webkit/glue/media/buffered_resource_loader.cc b/webkit/glue/media/buffered_resource_loader.cc
index 5adb76f..d88c654 100644
--- a/webkit/glue/media/buffered_resource_loader.cc
+++ b/webkit/glue/media/buffered_resource_loader.cc
@@ -50,7 +50,7 @@ BufferedResourceLoader::BufferedResourceLoader(
int64 last_byte_position)
: buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)),
deferred_(false),
- defer_allowed_(true),
+ defer_strategy_(kReadThenDefer),
completed_(false),
range_requested_(false),
partial_response_(false),
@@ -180,10 +180,15 @@ void BufferedResourceLoader::Read(int64 position,
// If we can serve the request now, do the actual read.
if (CanFulfillRead()) {
ReadInternal();
- DisableDeferIfNeeded();
+ UpdateDeferBehavior();
return;
}
+ // If you're deferred and you can't fulfill the read because you don't have
+ // enough data, you will never fulfill the read.
+ // Update defer behavior to re-enable deferring if need be.
+ UpdateDeferBehavior();
+
// If we expected the read request to be fulfilled later, returns
// immediately and let more data to flow in.
if (WillFulfillRead())
@@ -199,11 +204,6 @@ int64 BufferedResourceLoader::GetBufferedPosition() {
return kPositionNotSpecified;
}
-void BufferedResourceLoader::SetAllowDefer(bool is_allowed) {
- defer_allowed_ = is_allowed;
- DisableDeferIfNeeded();
-}
-
int64 BufferedResourceLoader::content_length() {
return content_length_;
}
@@ -344,21 +344,19 @@ void BufferedResourceLoader::didReceiveData2(
buffer_->Append(reinterpret_cast<const uint8*>(data), data_length);
// If there is an active read request, try to fulfill the request.
- if (HasPendingRead() && CanFulfillRead()) {
+ if (HasPendingRead() && CanFulfillRead())
ReadInternal();
- } else if (!defer_allowed_) {
- // If we're not allowed to defer, slide the buffer window forward instead
- // of deferring.
- if (buffer_->forward_bytes() > buffer_->forward_capacity()) {
- size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity();
- bool success = buffer_->Seek(excess);
- DCHECK(success);
- offset_ += first_offset_ + excess;
- }
- }
// At last see if the buffer is full and we need to defer the downloading.
- EnableDeferIfNeeded();
+ UpdateDeferBehavior();
+
+ // Consume excess bytes from our in-memory buffer if necessary.
+ if (buffer_->forward_bytes() > buffer_->forward_capacity()) {
+ size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity();
+ bool success = buffer_->Seek(excess);
+ DCHECK(success);
+ offset_ += first_offset_ + excess;
+ }
// Notify that we have received some data.
NotifyNetworkEvent();
@@ -441,32 +439,83 @@ bool BufferedResourceLoader::HasSingleOrigin() const {
/////////////////////////////////////////////////////////////////////////////
// Helper methods.
-void BufferedResourceLoader::EnableDeferIfNeeded() {
- if (!defer_allowed_)
+void BufferedResourceLoader::UpdateDeferBehavior() {
+ if (!url_loader_.get() || !buffer_.get())
return;
- if (!deferred_ &&
- buffer_->forward_bytes() >= buffer_->forward_capacity()) {
- deferred_ = true;
+ if ((deferred_ && ShouldDisableDefer()) ||
+ (!deferred_ && ShouldEnableDefer())) {
+ bool eventOccurred = ToggleDeferring();
+ if (eventOccurred)
+ NotifyNetworkEvent();
+ }
+}
+
+void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) {
+ defer_strategy_ = strategy;
+ UpdateDeferBehavior();
+}
+
+bool BufferedResourceLoader::ShouldEnableDefer() {
+ // If we're already deferring, then enabling makes no sense.
+ if (deferred_)
+ return false;
+
+ switch(defer_strategy_) {
+ // Never defer at all, so never enable defer.
+ case kNeverDefer:
+ return false;
- if (url_loader_.get())
- url_loader_->setDefersLoading(true);
+ // Defer if nothing is being requested.
+ case kReadThenDefer:
+ return !read_callback_.get();
- NotifyNetworkEvent();
+ // Defer if we've reached the max capacity of the threshold.
+ case kThresholdDefer:
+ return buffer_->forward_bytes() >= buffer_->forward_capacity();
}
+ // Otherwise don't enable defer.
+ return false;
}
-void BufferedResourceLoader::DisableDeferIfNeeded() {
- if (deferred_ &&
- (!defer_allowed_ ||
- buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) {
- deferred_ = false;
+bool BufferedResourceLoader::ShouldDisableDefer() {
+ // If we're not deferring, then disabling makes no sense.
+ if (!deferred_)
+ return false;
- if (url_loader_.get())
- url_loader_->setDefersLoading(false);
+ switch(defer_strategy_) {
+ // Always disable deferring.
+ case kNeverDefer:
+ return true;
+
+ // We have an outstanding read request, and we have not buffered enough
+ // yet to fulfill the request; disable defer to get more data.
+ case kReadThenDefer: {
+ size_t amount_buffered = buffer_->forward_bytes();
+ size_t amount_to_read = static_cast<size_t>(read_size_);
+ return read_callback_.get() && amount_buffered < amount_to_read;
+ }
+
+ // We have less than half the capacity of our threshold, so
+ // disable defer to get more data.
+ case kThresholdDefer: {
+ size_t amount_buffered = buffer_->forward_bytes();
+ size_t half_capacity = buffer_->forward_capacity() / 2;
+ return amount_buffered < half_capacity;
+ }
+ }
+
+ // Otherwise keep deferring.
+ return false;
+}
- NotifyNetworkEvent();
+bool BufferedResourceLoader::ToggleDeferring() {
+ deferred_ = !deferred_;
+ if (url_loader_.get()) {
+ url_loader_->setDefersLoading(deferred_);
+ return true;
}
+ return false;
}
bool BufferedResourceLoader::CanFulfillRead() {
diff --git a/webkit/glue/media/buffered_resource_loader.h b/webkit/glue/media/buffered_resource_loader.h
index 3e04b43..22b8bd1 100644
--- a/webkit/glue/media/buffered_resource_loader.h
+++ b/webkit/glue/media/buffered_resource_loader.h
@@ -36,6 +36,15 @@ class BufferedResourceLoader :
public base::RefCountedThreadSafe<BufferedResourceLoader>,
public WebKit::WebURLLoaderClient {
public:
+ // kNeverDefer - Aggresively buffer; never defer loading while paused.
+ // kReadThenDefer - Request only enough data to fulfill read requests.
+ // kThresholdDefer - Try to keep amount of buffered data at a threshold.
+ enum DeferStrategy {
+ kNeverDefer,
+ kReadThenDefer,
+ kThresholdDefer,
+ };
+
typedef Callback0::Type NetworkEventCallback;
// |url| - URL for the resource to be loaded.
@@ -85,9 +94,6 @@ class BufferedResourceLoader :
// |kPositionNotSpecified| if such value is not available.
virtual int64 GetBufferedPosition();
- // Sets whether deferring data is allowed or disallowed.
- virtual void SetAllowDefer(bool is_allowed);
-
// Gets the content length in bytes of the instance after this loader has been
// started. If this value is |kPositionNotSpecified|, then content length is
// unknown.
@@ -149,19 +155,30 @@ class BufferedResourceLoader :
bool HasSingleOrigin() const;
+ // Sets the defer strategy to the given value.
+ void UpdateDeferStrategy(DeferStrategy strategy);
+
protected:
friend class base::RefCountedThreadSafe<BufferedResourceLoader>;
-
virtual ~BufferedResourceLoader();
private:
friend class BufferedResourceLoaderTest;
- // Defer the resource loading if the buffer is full.
- void EnableDeferIfNeeded();
+ // Toggles whether the resource loading is deferred or not.
+ // Returns true if a network event was fired.
+ bool ToggleDeferring();
+
+ // Returns true if we should defer resource loading, based
+ // on current buffering scheme.
+ bool ShouldEnableDefer();
+
+ // Returns true if we should enable resource loading, based
+ // on current buffering scheme.
+ bool ShouldDisableDefer();
- // Disable defer loading if we are under-buffered.
- void DisableDeferIfNeeded();
+ // Updates deferring behavior based on current buffering scheme.
+ void UpdateDeferBehavior();
// Returns true if the current read request can be fulfilled by what is in
// the buffer.
@@ -204,8 +221,8 @@ class BufferedResourceLoader :
// True if resource loading was deferred.
bool deferred_;
- // True if resource loader is allowed to defer, false otherwise.
- bool defer_allowed_;
+ // Current buffering algorithm in place for resource loading.
+ DeferStrategy defer_strategy_;
// True if resource loading has completed.
bool completed_;
diff --git a/webkit/glue/media/buffered_resource_loader_unittest.cc b/webkit/glue/media/buffered_resource_loader_unittest.cc
index bb6e7d9..b30ce82 100644
--- a/webkit/glue/media/buffered_resource_loader_unittest.cc
+++ b/webkit/glue/media/buffered_resource_loader_unittest.cc
@@ -176,18 +176,8 @@ class BufferedResourceLoaderTest : public testing::Test {
EXPECT_EQ(0, memcmp(buffer, data_ + pos, size));
}
- // Helper method to disallow deferring in |loader_|.
- void DisallowLoaderDefer() {
- if (loader_->deferred_) {
- EXPECT_CALL(*url_loader_, setDefersLoading(false));
- EXPECT_CALL(*this, NetworkCallback());
- }
- loader_->SetAllowDefer(false);
- }
-
- // Helper method to allow deferring in |loader_|.
- void AllowLoaderDefer() {
- loader_->SetAllowDefer(true);
+ void ConfirmLoaderDeferredState(bool expectedVal) {
+ EXPECT_EQ(loader_->deferred_, expectedVal);
}
MOCK_METHOD1(StartCallback, void(int error));
@@ -275,6 +265,7 @@ TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
// Tests the logic of sliding window for data buffering and reading.
TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
Initialize(kHttpUrl, 10, 29);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
Start();
PartialResponse(10, 29, 30);
@@ -325,6 +316,7 @@ TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
Initialize(kHttpUrl, 10, 0x00FFFFFF);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
Start();
PartialResponse(10, 0x00FFFFFF, 0x01000000);
@@ -368,128 +360,86 @@ TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
loader_->didFail(url_loader_, error);
}
-// Tests the logic of caching data to disk when media is paused.
-TEST_F(BufferedResourceLoaderTest, AllowDefer_NoDataReceived) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- // Start in undeferred state, then disallow defer, then allow defer
- // without receiving data in between.
- DisallowLoaderDefer();
- AllowLoaderDefer();
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadSameWindow) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
-
- uint8 buffer[10];
-
- // Start in undeferred state, disallow defer, receive data but don't shift
- // buffer window, then allow defer and read.
- DisallowLoaderDefer();
- WriteLoader(10, 10);
- AllowLoaderDefer();
-
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(10, 10, buffer);
- VerifyBuffer(buffer, 10, 10);
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadPastWindow) {
+// Tests the data buffering logic of NeverDefer strategy.
+TEST_F(BufferedResourceLoaderTest, NeverDeferStrategy) {
Initialize(kHttpUrl, 10, 99);
SetLoaderBuffer(10, 20);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer);
Start();
PartialResponse(10, 99, 100);
uint8 buffer[10];
- // Not deferred, disallow defer, received data and shift buffer window,
- // allow defer, then read in area outside of buffer window.
- DisallowLoaderDefer();
+ // Read past the buffer size; should not defer regardless.
WriteLoader(10, 10);
WriteLoader(20, 50);
- AllowLoaderDefer();
+ ConfirmLoaderDeferredState(false);
+ // Should move past window.
EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
ReadLoader(10, 10, buffer);
+
StopWhenLoad();
}
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) {
+// Tests the data buffering logic of ReadThenDefer strategy.
+TEST_F(BufferedResourceLoaderTest, ReadThenDeferStrategy) {
Initialize(kHttpUrl, 10, 99);
SetLoaderBuffer(10, 20);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kReadThenDefer);
Start();
PartialResponse(10, 99, 100);
uint8 buffer[10];
- // Start in deferred state, then disallow defer, receive no data, and
- // allow defer and read.
- EXPECT_CALL(*url_loader_, setDefersLoading(true));
+ // Make an outstanding read request.
+ // We should disable deferring after the read request, so expect
+ // a network event.
EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 40);
-
- DisallowLoaderDefer();
- AllowLoaderDefer();
-
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(20, 10, buffer);
- VerifyBuffer(buffer, 20, 10);
- StopWhenLoad();
-}
-
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadSameWindow) {
- Initialize(kHttpUrl, 10, 99);
- SetLoaderBuffer(10, 20);
- Start();
- PartialResponse(10, 99, 100);
+ ReadLoader(10, 10, buffer);
- uint8 buffer[10];
+ // Receive almost enough data to cover, shouldn't defer.
+ WriteLoader(10, 9);
+ ConfirmLoaderDeferredState(false);
- // Start in deferred state, disallow defer, receive data and shift buffer
- // window, allow defer, and read in a place that's still in the window.
- EXPECT_CALL(*url_loader_, setDefersLoading(true));
+ // As soon as we have received enough data to fulfill the read, defer.
EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 30);
+ EXPECT_CALL(*this, ReadCallback(10));
+ WriteLoader(19, 1);
- DisallowLoaderDefer();
- WriteLoader(40, 5);
- AllowLoaderDefer();
+ ConfirmLoaderDeferredState(true);
+ VerifyBuffer(buffer, 10, 10);
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(20, 10, buffer);
- VerifyBuffer(buffer, 20, 10);
StopWhenLoad();
}
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) {
+// Tests the data buffering logic of ThresholdDefer strategy.
+TEST_F(BufferedResourceLoaderTest, ThresholdDeferStrategy) {
Initialize(kHttpUrl, 10, 99);
SetLoaderBuffer(10, 20);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
Start();
PartialResponse(10, 99, 100);
uint8 buffer[10];
- // Start in deferred state, disallow defer, receive data and shift buffer
- // window, allow defer, and read outside of the buffer window.
- EXPECT_CALL(*url_loader_, setDefersLoading(true));
+ WriteLoader(10, 5);
+ // Haven't reached threshold, don't defer.
+ ConfirmLoaderDeferredState(false);
+
+ // We're at the threshold now, let's defer.
EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 40);
+ WriteLoader(15, 5);
+ ConfirmLoaderDeferredState(true);
- DisallowLoaderDefer();
- WriteLoader(50, 20);
- WriteLoader(70, 40);
- AllowLoaderDefer();
+ // Now we've read over half of the buffer, disable deferring.
+ EXPECT_CALL(*this, ReadCallback(6));
+ EXPECT_CALL(*this, NetworkCallback());
+ ReadLoader(10, 6, buffer);
+
+ ConfirmLoaderDeferredState(false);
+ VerifyBuffer(buffer, 10, 6);
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
- ReadLoader(20, 5, buffer);
StopWhenLoad();
}
diff --git a/webkit/glue/media/simple_data_source.cc b/webkit/glue/media/simple_data_source.cc
index 186f6d7..4b2281d 100644
--- a/webkit/glue/media/simple_data_source.cc
+++ b/webkit/glue/media/simple_data_source.cc
@@ -139,6 +139,8 @@ bool SimpleDataSource::IsStreaming() {
return false;
}
+void SimpleDataSource::SetPreload(media::Preload preload) {}
+
void SimpleDataSource::SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader) {
url_loader_.reset(mock_loader);
keep_test_loader_ = true;
diff --git a/webkit/glue/media/simple_data_source.h b/webkit/glue/media/simple_data_source.h
index 552d23c..3ecd5bb 100644
--- a/webkit/glue/media/simple_data_source.h
+++ b/webkit/glue/media/simple_data_source.h
@@ -51,6 +51,7 @@ class SimpleDataSource : public WebDataSource,
uint8* data, ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
virtual bool IsStreaming();
+ virtual void SetPreload(media::Preload preload);
// Used to inject a mock used for unittests.
virtual void SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader);
diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc
index 527a297..c01b331 100644
--- a/webkit/glue/webmediaplayer_impl.cc
+++ b/webkit/glue/webmediaplayer_impl.cc
@@ -387,6 +387,8 @@ void WebMediaPlayerImpl::load(const WebKit::WebURL& url) {
// Handle any volume changes that occured before load().
setVolume(GetClient()->volume());
+ // Get the preload value.
+ setPreload(GetClient()->preload());
// Initialize the pipeline.
SetNetworkState(WebKit::WebMediaPlayer::Loading);
@@ -500,10 +502,18 @@ void WebMediaPlayerImpl::setVisible(bool visible) {
return;
}
-bool WebMediaPlayerImpl::setAutoBuffer(bool autoBuffer) {
+#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \
+ COMPILE_ASSERT(int(WebKit::WebMediaPlayer::webkit_name) == \
+ int(media::chromium_name), \
+ mismatching_enums)
+COMPILE_ASSERT_MATCHING_ENUM(None, NONE);
+COMPILE_ASSERT_MATCHING_ENUM(MetaData, METADATA);
+COMPILE_ASSERT_MATCHING_ENUM(Auto, AUTO);
+
+void WebMediaPlayerImpl::setPreload(WebKit::WebMediaPlayer::Preload preload) {
DCHECK(MessageLoop::current() == main_loop_);
- return false;
+ pipeline_->SetPreload(static_cast<media::Preload>(preload));
}
bool WebMediaPlayerImpl::totalBytesKnown() {
diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h
index a8d7837..68b06d5 100644
--- a/webkit/glue/webmediaplayer_impl.h
+++ b/webkit/glue/webmediaplayer_impl.h
@@ -208,7 +208,7 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
virtual void setRate(float rate);
virtual void setVolume(float volume);
virtual void setVisible(bool visible);
- virtual bool setAutoBuffer(bool autoBuffer);
+ virtual void setPreload(WebKit::WebMediaPlayer::Preload preload);
virtual bool totalBytesKnown();
virtual const WebKit::WebTimeRanges& buffered();
virtual float maxTimeSeekable() const;