diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 01:29:50 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 01:29:50 +0000 |
commit | 4f92fbc2765f3bd2db6d076c9b4d17410b847538 (patch) | |
tree | 2b4977e589b6cebefea29f3d425bd5d0a34c46b6 /webkit | |
parent | 30973dfd8a5c23cce0d726c6fdd8c298ad94f324 (diff) | |
download | chromium_src-4f92fbc2765f3bd2db6d076c9b4d17410b847538.zip chromium_src-4f92fbc2765f3bd2db6d076c9b4d17410b847538.tar.gz chromium_src-4f92fbc2765f3bd2db6d076c9b4d17410b847538.tar.bz2 |
Report stalled event correctly for <video>
BUG=20127
BUG=13568
TEST=Opens a web page, stalled event should be fire only if network
is actuall stalled.
What this patch does:
1. Report Loading / Idle according to whether we are actively
receiving data in the BuffereResourceLoader. This is done by
signaling the network events to BufferedDataSource and then
to the media playback pipeline.
2. Report byteLoaded() as the last byte position buffered. This
will enable an actual ticking progress for the progress event.
With this value actually ticking, stalled event is suppressed.
Review URL: http://codereview.chromium.org/269002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29235 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/glue/media/buffered_data_source.cc | 323 | ||||
-rw-r--r-- | webkit/glue/media/buffered_data_source.h | 67 | ||||
-rw-r--r-- | webkit/glue/media/buffered_data_source_unittest.cc | 58 | ||||
-rw-r--r-- | webkit/glue/webmediaplayer_impl.cc | 28 | ||||
-rw-r--r-- | webkit/glue/webmediaplayer_impl.h | 6 |
5 files changed, 330 insertions, 152 deletions
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc index a4d753b..34924c9 100644 --- a/webkit/glue/media/buffered_data_source.cc +++ b/webkit/glue/media/buffered_data_source.cc @@ -98,13 +98,17 @@ BufferedResourceLoader::BufferedResourceLoader( BufferedResourceLoader::~BufferedResourceLoader() { } -void BufferedResourceLoader::Start(net::CompletionCallback* start_callback) { +void BufferedResourceLoader::Start(net::CompletionCallback* start_callback, + NetworkEventCallback* event_callback) { // Make sure we have not started. DCHECK(!bridge_.get()); DCHECK(!start_callback_.get()); + DCHECK(!event_callback_.get()); DCHECK(start_callback); + DCHECK(event_callback); start_callback_.reset(start_callback); + event_callback_.reset(event_callback); if (first_byte_position_ != kPositionNotSpecified) { range_requested_ = true; @@ -133,8 +137,14 @@ void BufferedResourceLoader::Start(net::CompletionCallback* start_callback) { void BufferedResourceLoader::Stop() { // Reset callbacks. start_callback_.reset(); + event_callback_.reset(); read_callback_.reset(); + // Use the internal buffer to signal that we have been stopped. + // TODO(hclam): Not so pretty to do this. + if (!buffer_.get()) + return; + // Destroy internal buffer. buffer_.reset(); @@ -199,6 +209,18 @@ void BufferedResourceLoader::Read(int64 position, DoneRead(net::ERR_CACHE_MISS); } +int64 BufferedResourceLoader::GetBufferedFirstBytePosition() { + if (buffer_.get()) + return offset_ - static_cast<int>(buffer_->backward_bytes()); + return kPositionNotSpecified; +} + +int64 BufferedResourceLoader::GetBufferedLastBytePosition() { + if (buffer_.get()) + return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1; + return kPositionNotSpecified; +} + ///////////////////////////////////////////////////////////////////////////// // BufferedResourceLoader, // webkit_glue::ResourceLoaderBridge::Peer implementations @@ -300,6 +322,9 @@ void BufferedResourceLoader::OnReceivedData(const char* data, int len) { // At last see if the buffer is full and we need to defer the downloading. EnableDeferIfNeeded(); + + // Notify that we have received some data. + NotifyNetworkEvent(); } void BufferedResourceLoader::OnCompletedRequest( @@ -335,6 +360,9 @@ void BufferedResourceLoader::OnCompletedRequest( // There must not be any outstanding read request. DCHECK(!HasPendingRead()); + // Notify that network response is completed. + NotifyNetworkEvent(); + // We incremented the reference count when the loader was started. We balance // that reference here so that we get destroyed. This is also the only safe // place to destroy the ResourceLoaderBridge. @@ -351,6 +379,8 @@ void BufferedResourceLoader::EnableDeferIfNeeded() { if (bridge_.get()) bridge_->SetDefersLoading(true); + + NotifyNetworkEvent(); } } @@ -361,6 +391,8 @@ void BufferedResourceLoader::DisableDeferIfNeeded() { if (bridge_.get()) bridge_->SetDefersLoading(false); + + NotifyNetworkEvent(); } } @@ -418,21 +450,6 @@ void BufferedResourceLoader::ReadInternal() { DoneRead(read); } -void BufferedResourceLoader::DoneRead(int error) { - read_callback_->RunWithParams(Tuple1<int>(error)); - read_callback_.reset(); - read_position_ = 0; - read_size_ = 0; - read_buffer_ = NULL; - first_offset_ = 0; - last_offset_ = 0; -} - -void BufferedResourceLoader::DoneStart(int error) { - start_callback_->RunWithParams(Tuple1<int>(error)); - start_callback_.reset(); -} - bool BufferedResourceLoader::VerifyPartialResponse( const ResourceLoaderBridge::ResponseInfo& info) { int64 first_byte_position, last_byte_position, instance_size; @@ -455,15 +472,37 @@ bool BufferedResourceLoader::VerifyPartialResponse( return true; } -////////////////////////////////////////////////////////////////////////////// +void BufferedResourceLoader::DoneRead(int error) { + read_callback_->RunWithParams(Tuple1<int>(error)); + read_callback_.reset(); + read_position_ = 0; + read_size_ = 0; + read_buffer_ = NULL; + first_offset_ = 0; + last_offset_ = 0; +} + +void BufferedResourceLoader::DoneStart(int error) { + start_callback_->RunWithParams(Tuple1<int>(error)); + start_callback_.reset(); +} + +void BufferedResourceLoader::NotifyNetworkEvent() { + if (event_callback_.get()) + event_callback_->Run(); +} + +///////////////////////////////////////////////////////////////////////////// // BufferedDataSource, protected BufferedDataSource::BufferedDataSource( MessageLoop* render_loop, webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory) : total_bytes_(kPositionNotSpecified), + loaded_(false), streaming_(false), bridge_factory_(bridge_factory), loader_(NULL), + network_activity_(false), initialize_callback_(NULL), read_callback_(NULL), read_position_(0), @@ -473,8 +512,8 @@ BufferedDataSource::BufferedDataSource( intermediate_read_buffer_(new uint8[kInitialReadBufferSize]), intermediate_read_buffer_size_(kInitialReadBufferSize), render_loop_(render_loop), - stopped_(false), - stop_task_finished_(false) { + stop_signal_received_(false), + stopped_on_render_loop_(false) { } BufferedDataSource::~BufferedDataSource() { @@ -483,7 +522,7 @@ BufferedDataSource::~BufferedDataSource() { // A factory method to create BufferedResourceLoader using the read parameters. // This method can be overrided to inject mock BufferedResourceLoader object // for testing purpose. -BufferedResourceLoader* BufferedDataSource::CreateLoader( +BufferedResourceLoader* BufferedDataSource::CreateResourceLoader( int64 first_byte_position, int64 last_byte_position) { DCHECK(MessageLoop::current() == render_loop_); @@ -530,7 +569,7 @@ void BufferedDataSource::Initialize(const std::string& url, void BufferedDataSource::Stop() { { AutoLock auto_lock(lock_); - stopped_ = true; + stop_signal_received_ = true; } render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &BufferedDataSource::StopTask)); @@ -538,8 +577,7 @@ void BufferedDataSource::Stop() { ///////////////////////////////////////////////////////////////////////////// // BufferedDataSource, media::DataSource implementation -void BufferedDataSource::Read(int64 position, size_t size, - uint8* data, +void BufferedDataSource::Read(int64 position, size_t size, uint8* data, media::DataSource::ReadCallback* read_callback) { render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &BufferedDataSource::ReadTask, @@ -564,6 +602,7 @@ bool BufferedDataSource::IsStreaming() { void BufferedDataSource::InitializeTask() { DCHECK(MessageLoop::current() == render_loop_); DCHECK(!loader_.get()); + DCHECK(!stopped_on_render_loop_); // Kick starts the watch dog task that will handle connection timeout. // We run the watch dog 2 times faster the actual timeout so as to catch @@ -579,18 +618,18 @@ void BufferedDataSource::InitializeTask() { // This also serve as a probe to determine server capability to serve // range request. // TODO(hclam): Do some experiments for the best approach. - loader_ = CreateLoader(0, 1024); + loader_ = CreateResourceLoader(0, 1024); loader_->Start( - NewCallback(this, - &BufferedDataSource::HttpInitialStartCallback)); + NewCallback(this, &BufferedDataSource::HttpInitialStartCallback), + NewCallback(this, &BufferedDataSource::NetworkEventCallback)); } else { // For all other protocols, assume they support range request. We fetch // the full range of the resource to obtain the instance size because // we won't be served HTTP headers. - loader_ = CreateLoader(-1, -1); + loader_ = CreateResourceLoader(-1, -1); loader_->Start( - NewCallback(this, - &BufferedDataSource::NonHttpInitialStartCallback)); + NewCallback(this, &BufferedDataSource::NonHttpInitialStartCallback), + NewCallback(this, &BufferedDataSource::NetworkEventCallback)); } } @@ -601,9 +640,9 @@ void BufferedDataSource::ReadTask( // If StopTask() was executed we should return immediately. We check this // variable to prevent doing any actual work after clean up was done. We do - // not check |stopped_| because anything use of it has to be within |lock_| - // which is not desirable. - if (stop_task_finished_) + // not check |stop_signal_received_| because anything use of it has to be + // within |lock_| which is not desirable. + if (stopped_on_render_loop_) return; DCHECK(!read_callback_.get()); @@ -623,6 +662,7 @@ void BufferedDataSource::ReadTask( void BufferedDataSource::StopTask() { DCHECK(MessageLoop::current() == render_loop_); + DCHECK(!stopped_on_render_loop_); // Stop the watch dog. watch_dog_timer_.Stop(); @@ -640,21 +680,34 @@ void BufferedDataSource::StopTask() { read_attempts_ = 0; // Signal that stop task has finished execution. - stop_task_finished_ = true; + stopped_on_render_loop_ = true; } -void BufferedDataSource::SwapLoaderTask( - scoped_refptr<BufferedResourceLoader> loader) { +void BufferedDataSource::RestartLoadingTask() { DCHECK(MessageLoop::current() == render_loop_); - DCHECK(loader); - loader_ = loader; - loader_->Start(NewCallback(this, - &BufferedDataSource::PartialReadStartCallback)); + // This variable is set in StopTask(). We check this and do an early return. + // The sequence of actions which enable this conditions is: + // 1. Stop() is called from the pipeline. + // 2. ReadCallback() is called from the resource loader. + // 3. StopTask() is executed. + // 4. RestartLoadingTask() is executed. + if (stopped_on_render_loop_) + return; + + // If there's no outstanding read then return early. + if (!read_callback_.get()) + return; + + loader_ = CreateResourceLoader(read_position_, -1); + loader_->Start( + NewCallback(this, &BufferedDataSource::PartialReadStartCallback), + NewCallback(this, &BufferedDataSource::NetworkEventCallback)); } void BufferedDataSource::WatchDogTask() { DCHECK(MessageLoop::current() == render_loop_); + DCHECK(!stopped_on_render_loop_); // We only care if there is an active read request. if (!read_callback_.get()) @@ -673,10 +726,13 @@ void BufferedDataSource::WatchDogTask() { ++read_attempts_; read_submitted_time_ = base::Time::Now(); - // Stops the current loader and swap in a new resource loader and + // Stops the current loader and creates a new resource loader and // retry the request. loader_->Stop(); - SwapLoaderTask(CreateLoader(read_position_, -1)); + loader_ = CreateResourceLoader(read_position_, -1); + loader_->Start( + NewCallback(this, &BufferedDataSource::PartialReadStartCallback), + NewCallback(this, &BufferedDataSource::NetworkEventCallback)); } // This method is the place where actual read happens, |loader_| must be valid @@ -698,8 +754,9 @@ void BufferedDataSource::ReadInternal() { // Method to report the results of the current read request. Also reset all // the read parameters. -void BufferedDataSource::DoneRead(int error) { +void BufferedDataSource::DoneRead_Locked(int error) { DCHECK(MessageLoop::current() == render_loop_); + DCHECK(read_callback_.get()); lock_.AssertAcquired(); if (error >= 0) { @@ -715,7 +772,8 @@ void BufferedDataSource::DoneRead(int error) { read_buffer_ = 0; } -void BufferedDataSource::DoneInitialization() { +void BufferedDataSource::DoneInitialization_Locked() { + DCHECK(MessageLoop::current() == render_loop_); DCHECK(initialize_callback_.get()); lock_.AssertAcquired(); @@ -729,6 +787,22 @@ void BufferedDataSource::DoneInitialization() { // BufferedResourceLoader. void BufferedDataSource::HttpInitialStartCallback(int error) { DCHECK(MessageLoop::current() == render_loop_); + DCHECK(loader_.get()); + + int64 instance_size = loader_->instance_size(); + bool partial_response = loader_->partial_response(); + bool success = error == net::OK; + + if (success) { + // TODO(hclam): Needs more thinking about supporting servers without range + // request or their partial response is not complete. + total_bytes_ = instance_size; + loaded_ = false; + streaming_ = (instance_size == kPositionNotSpecified) || !partial_response; + } else { + // TODO(hclam): In case of failure, we can retry several times. + loader_->Stop(); + } // We need to prevent calling to filter host and running the callback if // we have received the stop signal. We need to lock down the whole callback @@ -739,40 +813,44 @@ void BufferedDataSource::HttpInitialStartCallback(int error) { // this object when Stop() method is ever called. Locking this method is safe // because |lock_| is only acquired in tasks on render thread. AutoLock auto_lock(lock_); - if (stopped_) + if (stop_signal_received_) return; - if (error != net::OK) { - // TODO(hclam): In case of failure, we can retry several times. + if (!success) { host()->SetError(media::PIPELINE_ERROR_NETWORK); - DCHECK(loader_.get()); - loader_->Stop(); - DoneInitialization(); + DoneInitialization_Locked(); return; } - // TODO(hclam): Needs more thinking about supporting servers without range - // request or their partial response is not complete. - total_bytes_ = loader_->instance_size(); - if (total_bytes_ >= 0 && loader_->partial_response()) { - // This value governs the range that we can seek to. - // TODO(hclam): Report the correct value of buffered bytes. - host()->SetTotalBytes(total_bytes_); - host()->SetBufferedBytes(total_bytes_); - } else { + if (streaming_) { // If the server didn't reply with an instance size, it is likely this // is a streaming response. - streaming_ = true; host()->SetStreaming(true); + } else { + // This value governs the range that we can seek to. + // TODO(hclam): Report the correct value of buffered bytes. + host()->SetTotalBytes(total_bytes_); + host()->SetBufferedBytes(0); } // Currently, only files can be used reliably w/o a network. host()->SetLoaded(false); - DoneInitialization(); + DoneInitialization_Locked(); } void BufferedDataSource::NonHttpInitialStartCallback(int error) { DCHECK(MessageLoop::current() == render_loop_); + DCHECK(loader_.get()); + + int64 instance_size = loader_->instance_size(); + bool success = error == net::OK && instance_size != kPositionNotSpecified; + + if (success) { + total_bytes_ = instance_size; + loaded_ = true; + } else { + loader_->Stop(); + } // We need to prevent calling to filter host and running the callback if // we have received the stop signal. We need to lock down the whole callback @@ -783,23 +861,17 @@ void BufferedDataSource::NonHttpInitialStartCallback(int error) { // this object when Stop() method is ever called. Locking this method is safe // because |lock_| is only acquired in tasks on render thread. AutoLock auto_lock(lock_); - if (stopped_) + if (stop_signal_received_) return; - DCHECK(loader_.get()); - - if (error != net::OK || loader_->instance_size() == kPositionNotSpecified) { + if (success) { + host()->SetTotalBytes(total_bytes_); + host()->SetBufferedBytes(total_bytes_); + host()->SetLoaded(loaded_); + } else { host()->SetError(media::PIPELINE_ERROR_NETWORK); - loader_->Stop(); - DoneInitialization(); - return; } - - total_bytes_ = loader_->instance_size(); - host()->SetTotalBytes(total_bytes_); - host()->SetBufferedBytes(total_bytes_); - host()->SetLoaded(true); - DoneInitialization(); + DoneInitialization_Locked(); } void BufferedDataSource::PartialReadStartCallback(int error) { @@ -813,27 +885,42 @@ void BufferedDataSource::PartialReadStartCallback(int error) { // Once the range request has started successfully, we can proceed with // reading from it. ReadInternal(); - } else { - loader_->Stop(); - - // We need to prevent calling to filter host and running the callback if - // we have received the stop signal. We need to lock down the whole callback - // method to prevent bad things from happening. The reason behind this is - // that we cannot guarantee tasks on render thread have completely stopped - // when we receive the Stop() method call. So only way to solve this is to - // let tasks on render thread to run but make sure they don't call outside - // this object when Stop() method is ever called. Locking this method is - // safe because |lock_| is only acquired in tasks on render thread. - AutoLock auto_lock(lock_); - if (stopped_) - return; - DoneRead(net::ERR_INVALID_RESPONSE); + return; } + + // Stop the resource loader since we have received an error. + loader_->Stop(); + + // We need to prevent calling to filter host and running the callback if + // we have received the stop signal. We need to lock down the whole callback + // method to prevent bad things from happening. The reason behind this is + // that we cannot guarantee tasks on render thread have completely stopped + // when we receive the Stop() method call. So only way to solve this is to + // let tasks on render thread to run but make sure they don't call outside + // this object when Stop() method is ever called. Locking this method is + // safe because |lock_| is only acquired in tasks on render thread. + AutoLock auto_lock(lock_); + if (stop_signal_received_) + return; + DoneRead_Locked(net::ERR_INVALID_RESPONSE); } void BufferedDataSource::ReadCallback(int error) { DCHECK(MessageLoop::current() == render_loop_); + if (error < 0) { + DCHECK(loader_.get()); + + // Stop the resource load if it failed. + loader_->Stop(); + + if (error == net::ERR_CACHE_MISS) { + render_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &BufferedDataSource::RestartLoadingTask)); + return; + } + } + // We need to prevent calling to filter host and running the callback if // we have received the stop signal. We need to lock down the whole callback // method to prevent bad things from happening. The reason behind this is @@ -843,36 +930,50 @@ void BufferedDataSource::ReadCallback(int error) { // this object when Stop() method is ever called. Locking this method is safe // because |lock_| is only acquired in tasks on render thread. AutoLock auto_lock(lock_); - if (stopped_) + if (stop_signal_received_) return; - DCHECK(loader_.get()); - DCHECK(read_callback_.get()); - - if (error >= 0) { + if (error > 0) { // If a position error code is received, read was successful. So copy // from intermediate read buffer to the target read buffer. memcpy(read_buffer_, intermediate_read_buffer_.get(), error); - DoneRead(error); - } else if (error == net::ERR_CACHE_MISS) { - // If the current loader cannot serve this read request, we need to create - // a new one. - // TODO(hclam): we need to count how many times it failed to prevent - // excessive trials. - - // Stops the current resource loader. - loader_->Stop(); + } + DoneRead_Locked(error); +} - // Since this method is called from the current buffered resource loader, - // we cannot delete it. So we need to post a task to swap in a new - // resource loader and starts it. - render_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &BufferedDataSource::SwapLoaderTask, - CreateLoader(read_position_, -1))); - } else { - loader_->Stop(); - DoneRead(error); +void BufferedDataSource::NetworkEventCallback() { + DCHECK(MessageLoop::current() == render_loop_); + DCHECK(loader_.get()); + + // In case of non-HTTP request we don't need to report network events, + // so return immediately. + if (loaded_) + return; + + bool network_activity = loader_->network_activity(); + int64 buffered_last_byte_position = loader_->GetBufferedLastBytePosition(); + + // If we get an unspecified value, return immediately. + if (buffered_last_byte_position == kPositionNotSpecified) + return; + + // We need to prevent calling to filter host and running the callback if + // we have received the stop signal. We need to lock down the whole callback + // method to prevent bad things from happening. The reason behind this is + // that we cannot guarantee tasks on render thread have completely stopped + // when we receive the Stop() method call. So only way to solve this is to + // let tasks on render thread to run but make sure they don't call outside + // this object when Stop() method is ever called. Locking this method is safe + // because |lock_| is only acquired in tasks on render thread. + AutoLock auto_lock(lock_); + if (stop_signal_received_) + return; + + if (network_activity != network_activity_) { + network_activity_ = network_activity; + host()->SetNetworkActivity(network_activity); } + host()->SetBufferedBytes(buffered_last_byte_position + 1); } } // namespace webkit_glue diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h index 532fa56..dbbb54c 100644 --- a/webkit/glue/media/buffered_data_source.h +++ b/webkit/glue/media/buffered_data_source.h @@ -5,7 +5,9 @@ #ifndef WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_H_ #define WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_H_ +#include <algorithm> #include <string> +#include <vector> #include "base/lock.h" #include "base/scoped_ptr.h" @@ -33,6 +35,8 @@ class BufferedResourceLoader : public base::RefCountedThreadSafe<BufferedResourceLoader>, public webkit_glue::ResourceLoaderBridge::Peer { public: + typedef Callback0::Type NetworkEventCallback; + // |bridge_factory| - Factory to create a ResourceLoaderBridge. // |url| - URL for the resource to be loaded. // |first_byte_position| - First byte to start loading from, -1 for not @@ -57,7 +61,10 @@ class BufferedResourceLoader : // An invalid response is received from the server. // - (Anything else) // An error code that indicates the request has failed. - virtual void Start(net::CompletionCallback* callback); + // |event_callback| is called when the response is completed, data is + // received, the request is suspended or resumed. + virtual void Start(net::CompletionCallback* callback, + NetworkEventCallback* event_callback); // Stop this loader, cancels and request and release internal buffer. virtual void Stop(); @@ -75,6 +82,14 @@ class BufferedResourceLoader : virtual void Read(int64 position, int read_size, uint8* buffer, net::CompletionCallback* callback); + // Returns the position of the first byte buffered. Returns -1 if such value + // is not available. + virtual int64 GetBufferedFirstBytePosition(); + + // Returns the position of the last byte buffered. Returns -1 if such value + // is not available. + virtual int64 GetBufferedLastBytePosition(); + // Gets the content length in bytes of the instance after this loader has been // started. If this value is -1, then content length is unknown. virtual int64 content_length() { return content_length_; } @@ -87,6 +102,9 @@ class BufferedResourceLoader : // It means a 206 response in HTTP/HTTPS protocol. virtual bool partial_response() { return partial_response_; } + // Returns true if network is currently active. + virtual bool network_activity() { return !completed_ && !deferred_; } + ///////////////////////////////////////////////////////////////////////////// // webkit_glue::ResourceLoaderBridge::Peer implementations. virtual void OnUploadProgress(uint64 position, uint64 size) {} @@ -134,6 +152,9 @@ class BufferedResourceLoader : // Done with start. Invokes the start callback and reset it. void DoneStart(int error); + // Calls |event_callback_| in terms of a network event. + void NotifyNetworkEvent(); + bool HasPendingRead() { return read_callback_.get() != NULL; } // A sliding window of buffer. @@ -156,6 +177,9 @@ class BufferedResourceLoader : int64 first_byte_position_; int64 last_byte_position_; + // Callback method that listens to network events. + scoped_ptr<NetworkEventCallback> event_callback_; + // Members used during request start. scoped_ptr<net::CompletionCallback> start_callback_; scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_; @@ -218,8 +242,8 @@ class BufferedDataSource : public media::DataSource { // A factory method to create a BufferedResourceLoader based on the read // parameters. We can override this file to object a mock // BufferedResourceLoader for testing. - virtual BufferedResourceLoader* CreateLoader(int64 first_byte_position, - int64 last_byte_position); + virtual BufferedResourceLoader* CreateResourceLoader( + int64 first_byte_position, int64 last_byte_position); // Gets the number of milliseconds to declare a request timeout since // the request was made. This method is made virtual so as to inject a @@ -232,25 +256,23 @@ class BufferedDataSource : public media::DataSource { MessageLoop*, webkit_glue::MediaResourceLoaderBridgeFactory*>; - // Posted to perform initialization on render thread. + // Posted to perform initialization on render thread and start resource + // loading. void InitializeTask(); - // Task posted to perform resource loading and actual reading on the render - // thread. - void ReadTask(int64 position, int read_size, - uint8* read_buffer, + // Task posted to perform actual reading on the render thread. + void ReadTask(int64 position, int read_size, uint8* read_buffer, media::DataSource::ReadCallback* read_callback); // Task posted when Stop() is called. void StopTask(); - // Reset |loader_| with |loader| and starts it. This task is posted from - // callback method from the current buffered resource loader. - void SwapLoaderTask(scoped_refptr<BufferedResourceLoader> loader); + // Restart resource loading on render thread. + void RestartLoadingTask(); // This task monitors the current active read request. If the current read // request has timed out, this task will destroy the current loader and - // creates a new to accomodate the read request. + // creates a new one to accomodate the read request. void WatchDogTask(); // The method that performs actual read. This method can only be executed on @@ -258,10 +280,10 @@ class BufferedDataSource : public media::DataSource { void ReadInternal(); // Calls |read_callback_| and reset all read parameters. - void DoneRead(int error); + void DoneRead_Locked(int error); // Calls |initialize_callback_| and reset it. - void DoneInitialization(); + void DoneInitialization_Locked(); // Callback method for |loader_| if URL for the resource requested is using // HTTP protocol. This method is called when response for initial request is @@ -284,6 +306,9 @@ class BufferedDataSource : public media::DataSource { // the error code or the number of bytes read. void ReadCallback(int error); + // Callback method when a network event is received. + void NetworkEventCallback(); + media::MediaFormat media_format_; // URL of the resource requested. @@ -295,6 +320,9 @@ class BufferedDataSource : public media::DataSource { // need to protect it. int64 total_bytes_; + // True if this data source is considered loaded. + bool loaded_; + // This value will be true if this data source can only support streaming. // i.e. range request is not supported. bool streaming_; @@ -305,6 +333,9 @@ class BufferedDataSource : public media::DataSource { // A resource loader for the media resource. scoped_refptr<BufferedResourceLoader> loader_; + // True if network is active. + bool network_activity_; + // Callback method from the pipeline for initialization. scoped_ptr<media::FilterCallback> initialize_callback_; @@ -337,11 +368,11 @@ class BufferedDataSource : public media::DataSource { // Stop signal to suppressing activities. This variable is set on the pipeline // thread and read from the render thread. - bool stopped_; + bool stop_signal_received_; - // This variable is set by StopTask() and read from ReadTask(). It is used to - // prevent ReadTask() from doing anything after StopTask() is executed. - bool stop_task_finished_; + // This variable is set by StopTask() that indicates this object is stopped + // on the render thread. + bool stopped_on_render_loop_; // This timer is to run the WatchDogTask repeatedly. We use a timer instead // of doing PostDelayedTask() reduce the extra reference held by the message diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc index c05c9b9..53915f1 100644 --- a/webkit/glue/media/buffered_data_source_unittest.cc +++ b/webkit/glue/media/buffered_data_source_unittest.cc @@ -82,8 +82,9 @@ class BufferedResourceLoaderTest : public testing::Test { CreateBridge(gurl_, _, first_position_, last_position_)) .WillOnce(Return(bridge_.get())); EXPECT_CALL(*bridge_, Start(loader_.get())); - loader_->Start(NewCallback(this, - &BufferedResourceLoaderTest::StartCallback)); + loader_->Start( + NewCallback(this, &BufferedResourceLoaderTest::StartCallback), + NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback)); } void FullResponse(int64 instance_size) { @@ -134,6 +135,7 @@ class BufferedResourceLoaderTest : public testing::Test { // Helper method to write to |loader_| from |data_|. void WriteLoader(int position, int size) { + EXPECT_CALL(*this, NetworkCallback()); loader_->OnReceivedData(reinterpret_cast<char*>(data_ + position), size); } @@ -150,6 +152,7 @@ class BufferedResourceLoaderTest : public testing::Test { MOCK_METHOD1(StartCallback, void(int error)); MOCK_METHOD1(ReadCallback, void(int error)); + MOCK_METHOD0(NetworkCallback, void()); protected: GURL gurl_; @@ -282,6 +285,7 @@ TEST_F(BufferedResourceLoaderTest, BufferAndRead) { ReadLoader(9, 10, buffer); // Response has completed. + EXPECT_CALL(*this, NetworkCallback()); EXPECT_CALL(*bridge_, OnDestroy()) .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge)); URLRequestStatus status; @@ -327,6 +331,7 @@ TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) { ReadLoader(25, 10, buffer); EXPECT_CALL(*this, ReadCallback(5)); + EXPECT_CALL(*this, NetworkCallback()); EXPECT_CALL(*bridge_, OnDestroy()) .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge)); URLRequestStatus status; @@ -344,6 +349,7 @@ TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) { ReadLoader(10, 10, buffer); EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED)); + EXPECT_CALL(*this, NetworkCallback()); EXPECT_CALL(*bridge_, OnDestroy()) .WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge)); URLRequestStatus status; @@ -358,20 +364,24 @@ class MockBufferedResourceLoader : public BufferedResourceLoader { MockBufferedResourceLoader() : BufferedResourceLoader() { } - MOCK_METHOD1(Start, void(net::CompletionCallback* read_callback)); + MOCK_METHOD2(Start, void(net::CompletionCallback* read_callback, + NetworkEventCallback* network_callback)); MOCK_METHOD0(Stop, void()); MOCK_METHOD4(Read, void(int64 position, int read_size, uint8* buffer, net::CompletionCallback* callback)); MOCK_METHOD0(content_length, int64()); MOCK_METHOD0(instance_size, int64()); MOCK_METHOD0(partial_response, bool()); + MOCK_METHOD0(network_activity, bool()); + MOCK_METHOD0(GetBufferedFirstBytePosition, int64()); + MOCK_METHOD0(GetBufferedLastBytePosition, int64()); private: DISALLOW_COPY_AND_ASSIGN(MockBufferedResourceLoader); }; // A mock BufferedDataSource to inject mock BufferedResourceLoader through -// CreateLoader() method. +// CreateResourceLoader() method. class MockBufferedDataSource : public BufferedDataSource { public: // Static methods for creating this class. @@ -390,13 +400,12 @@ class MockBufferedDataSource : public BufferedDataSource { return base::TimeDelta::FromMilliseconds(100); } - MOCK_METHOD2(CreateLoader, BufferedResourceLoader*(int64 first_position, - int64 last_position)); + MOCK_METHOD2(CreateResourceLoader, BufferedResourceLoader*( + int64 first_position, int64 last_position)); protected: MockBufferedDataSource( - MessageLoop* message_loop, - MediaResourceLoaderBridgeFactory* factory) + MessageLoop* message_loop, MediaResourceLoaderBridgeFactory* factory) : BufferedDataSource(message_loop, factory) { } @@ -459,11 +468,11 @@ class BufferedDataSourceTest : public testing::Test { bool loaded = networkState == LOADED; { InSequence s; - EXPECT_CALL(*data_source_, CreateLoader(_, _)) + EXPECT_CALL(*data_source_, CreateResourceLoader(_, _)) .WillOnce(Return(loader_.get())); // The initial response loader will be started. - EXPECT_CALL(*loader_, Start(NotNull())) + EXPECT_CALL(*loader_, Start(NotNull(), NotNull())) .WillOnce( DoAll(Assign(&error_, error), Invoke(this, @@ -471,18 +480,21 @@ class BufferedDataSourceTest : public testing::Test { } StrictMock<media::MockFilterCallback> callback; + EXPECT_CALL(*loader_, instance_size()) + .WillRepeatedly(Return(instance_size)); + EXPECT_CALL(*loader_, partial_response()) + .WillRepeatedly(Return(partial_response)); if (error == net::OK) { // Expected loaded or not. EXPECT_CALL(host_, SetLoaded(loaded)); - EXPECT_CALL(*loader_, instance_size()) - .WillRepeatedly(Return(instance_size)); - EXPECT_CALL(*loader_, partial_response()) - .WillRepeatedly(Return(partial_response)); // TODO(hclam): The condition for streaming needs to be adjusted. if (instance_size != -1 && (loaded || partial_response)) { EXPECT_CALL(host_, SetTotalBytes(instance_size)); - EXPECT_CALL(host_, SetBufferedBytes(instance_size)); + if (loaded) + EXPECT_CALL(host_, SetBufferedBytes(instance_size)); + else + EXPECT_CALL(host_, SetBufferedBytes(0)); } else { EXPECT_CALL(host_, SetStreaming(true)); } @@ -526,9 +538,13 @@ class BufferedDataSourceTest : public testing::Test { bridge_factory_.release(); } - void InvokeStartCallback(net::CompletionCallback* callback) { + void InvokeStartCallback( + net::CompletionCallback* callback, + BufferedResourceLoader::NetworkEventCallback* network_callback) { callback->RunWithParams(Tuple1<int>(error_)); delete callback; + // TODO(hclam): Save this callback. + delete network_callback; } void InvokeReadCallback(int64 position, int size, uint8* buffer, @@ -578,11 +594,11 @@ class BufferedDataSourceTest : public testing::Test { // 2. Then the current loader will be stop and destroyed. StrictMock<MockBufferedResourceLoader> *new_loader = new StrictMock<MockBufferedResourceLoader>(); - EXPECT_CALL(*data_source_, CreateLoader(position, -1)) + EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1)) .WillOnce(Return(new_loader)); // 3. Then the new loader will be started. - EXPECT_CALL(*new_loader, Start(NotNull())) + EXPECT_CALL(*new_loader, Start(NotNull(), NotNull())) .WillOnce(DoAll(Assign(&error_, net::OK), Invoke(this, &BufferedDataSourceTest::InvokeStartCallback))); @@ -642,13 +658,13 @@ class BufferedDataSourceTest : public testing::Test { // 2. Then the current loader will be stop and destroyed. StrictMock<MockBufferedResourceLoader> *new_loader = new StrictMock<MockBufferedResourceLoader>(); - EXPECT_CALL(*data_source_, CreateLoader(position, -1)) + EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1)) .WillOnce(Return(new_loader)); // 3. Then the new loader will be started and respond to queries about // whether this is a partial response using the value of the previous // loader. - EXPECT_CALL(*new_loader, Start(NotNull())) + EXPECT_CALL(*new_loader, Start(NotNull(), NotNull())) .WillOnce(DoAll(Assign(&error_, net::OK), Invoke(this, &BufferedDataSourceTest::InvokeStartCallback))); @@ -684,7 +700,7 @@ class BufferedDataSourceTest : public testing::Test { scoped_ptr<StrictMock<MockMediaResourceLoaderBridgeFactory> > bridge_factory_; scoped_refptr<StrictMock<MockBufferedResourceLoader> > loader_; - scoped_refptr<MockBufferedDataSource > data_source_; + scoped_refptr<MockBufferedDataSource> data_source_; scoped_refptr<media::FilterFactory> factory_; StrictMock<media::MockFilterHost> host_; diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc index 7bf5c1e..2f9306f 100644 --- a/webkit/glue/webmediaplayer_impl.cc +++ b/webkit/glue/webmediaplayer_impl.cc @@ -123,6 +123,11 @@ void WebMediaPlayerImpl::Proxy::PipelineErrorCallback() { &WebMediaPlayerImpl::Proxy::PipelineErrorTask)); } +void WebMediaPlayerImpl::Proxy::NetworkEventCallback() { + render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, + &WebMediaPlayerImpl::Proxy::NetworkEventTask)); +} + void WebMediaPlayerImpl::Proxy::RepaintTask() { DCHECK(MessageLoop::current() == render_loop_); { @@ -163,6 +168,13 @@ void WebMediaPlayerImpl::Proxy::PipelineErrorTask() { } } +void WebMediaPlayerImpl::Proxy::NetworkEventTask() { + DCHECK(MessageLoop::current() == render_loop_); + if (webmediaplayer_) { + webmediaplayer_->OnNetworkEvent(); + } +} + ///////////////////////////////////////////////////////////////////////////// // WebMediaPlayerImpl implementation @@ -199,6 +211,8 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client, &WebMediaPlayerImpl::Proxy::PipelineEndedCallback)); pipeline_->SetPipelineErrorCallback(NewCallback(proxy_.get(), &WebMediaPlayerImpl::Proxy::PipelineErrorCallback)); + pipeline_->SetNetworkEventCallback(NewCallback(proxy_.get(), + &WebMediaPlayerImpl::Proxy::NetworkEventCallback)); // Add in the default filter factories. filter_factory_->AddFactory(media::FFmpegDemuxer::CreateFilterFactory()); @@ -505,10 +519,10 @@ void WebMediaPlayerImpl::OnPipelineInitialize() { DCHECK(MessageLoop::current() == main_loop_); if (pipeline_->GetError() == media::PIPELINE_OK) { // Only keep one time range starting from 0. - WebKit::WebTimeRanges new_buffered(static_cast<size_t>(1)); + WebKit::WebTimeRanges new_buffered(1u); new_buffered[0].start = 0.0f; new_buffered[0].end = - static_cast<float>(pipeline_->GetBufferedTime().InSecondsF()); + static_cast<float>(pipeline_->GetDuration().InSecondsF()); buffered_.swap(new_buffered); // Since we have initialized the pipeline, say we have everything. @@ -583,6 +597,16 @@ void WebMediaPlayerImpl::OnPipelineError() { Repaint(); } +void WebMediaPlayerImpl::OnNetworkEvent() { + DCHECK(MessageLoop::current() == main_loop_); + if (pipeline_->GetError() == media::PIPELINE_OK) { + if (pipeline_->IsNetworkActive()) + SetNetworkState(WebKit::WebMediaPlayer::Loading); + else + SetNetworkState(WebKit::WebMediaPlayer::Idle); + } +} + void WebMediaPlayerImpl::SetNetworkState( WebKit::WebMediaPlayer::NetworkState state) { DCHECK(MessageLoop::current() == main_loop_); diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h index 9348983..5c5de44 100644 --- a/webkit/glue/webmediaplayer_impl.h +++ b/webkit/glue/webmediaplayer_impl.h @@ -108,6 +108,7 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, void PipelineSeekCallback(); void PipelineEndedCallback(); void PipelineErrorCallback(); + void NetworkEventCallback(); private: // Invoke |webmediaplayer_| to perform a repaint. @@ -125,6 +126,9 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, // Notify |webmediaplayer_| that a pipeline error has been set. void PipelineErrorTask(); + // Notify |webmediaplayer_| that there's a network event. + void NetworkEventTask(); + // The render message loop where WebKit lives. MessageLoop* render_loop_; WebMediaPlayerImpl* webmediaplayer_; @@ -229,6 +233,8 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer, void OnPipelineError(); + void OnNetworkEvent(); + private: // Helpers that set the network/ready state and notifies the client if // they've changed. |