diff options
-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; |