diff options
author | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-05 16:28:20 +0000 |
---|---|---|
committer | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-05 16:28:20 +0000 |
commit | 23a8b1d8f4c8c3b3c37811f310891256f45c8063 (patch) | |
tree | 3576777d4ec689cbba94c172f92ee5ed1e3077f9 | |
parent | 702b280dbaa67b88d0b07da22dc73f2f3116afee (diff) | |
download | chromium_src-23a8b1d8f4c8c3b3c37811f310891256f45c8063.zip chromium_src-23a8b1d8f4c8c3b3c37811f310891256f45c8063.tar.gz chromium_src-23a8b1d8f4c8c3b3c37811f310891256f45c8063.tar.bz2 |
Implementing preload=metadata for video
This patch implements the logic necessary to respect the preload attribute
when it is set to MetaData. This also refactors the BufferedResourceLoader
to determine its buffering techniques based on a DeferStrategy value.
BUG=16482,76555
TEST=media/video-preload.html, test_shell_tests
Review URL: http://codereview.chromium.org/6625059
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80465 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/base/filters.h | 19 | ||||
-rw-r--r-- | media/base/mock_filters.h | 2 | ||||
-rw-r--r-- | media/base/pipeline.h | 4 | ||||
-rw-r--r-- | media/base/pipeline_impl.cc | 32 | ||||
-rw-r--r-- | media/base/pipeline_impl.h | 15 | ||||
-rw-r--r-- | media/filters/adaptive_demuxer.cc | 2 | ||||
-rw-r--r-- | media/filters/adaptive_demuxer.h | 1 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 10 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.h | 2 | ||||
-rw-r--r-- | media/filters/file_data_source.cc | 2 | ||||
-rw-r--r-- | media/filters/file_data_source.h | 1 | ||||
-rw-r--r-- | webkit/glue/media/buffered_data_source.cc | 50 | ||||
-rw-r--r-- | webkit/glue/media/buffered_data_source.h | 17 | ||||
-rw-r--r-- | webkit/glue/media/buffered_resource_loader.cc | 119 | ||||
-rw-r--r-- | webkit/glue/media/buffered_resource_loader.h | 37 | ||||
-rw-r--r-- | webkit/glue/media/buffered_resource_loader_unittest.cc | 136 | ||||
-rw-r--r-- | webkit/glue/media/simple_data_source.cc | 2 | ||||
-rw-r--r-- | webkit/glue/media/simple_data_source.h | 1 | ||||
-rw-r--r-- | webkit/glue/webmediaplayer_impl.cc | 14 | ||||
-rw-r--r-- | webkit/glue/webmediaplayer_impl.h | 2 |
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; |