diff options
Diffstat (limited to 'webkit/glue')
-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. |