Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "webkit/glue/media/buffered_data_source.h"
      6 
      7 #include "media/base/filter_host.h"
      8 #include "net/base/net_errors.h"
      9 #include "webkit/glue/media/web_data_source_factory.h"
     10 #include "webkit/glue/webkit_glue.h"
     11 
     12 using WebKit::WebFrame;
     13 
     14 namespace webkit_glue {
     15 
     16 // Defines how long we should wait for more data before we declare a connection
     17 // timeout and start a new request.
     18 // TODO(hclam): Set it to 5s, calibrate this value later.
     19 static const int kTimeoutMilliseconds = 5000;
     20 
     21 // Defines how many times we should try to read from a buffered resource loader
     22 // before we declare a read error. After each failure of read from a buffered
     23 // resource loader, a new one is created to be read.
     24 static const int kReadTrials = 3;
     25 
     26 // BufferedDataSource has an intermediate buffer, this value governs the initial
     27 // size of that buffer. It is set to 32KB because this is a typical read size
     28 // of FFmpeg.
     29 static const int kInitialReadBufferSize = 32768;
     30 
     31 static WebDataSource* NewBufferedDataSource(MessageLoop* render_loop,
     32                                             WebKit::WebFrame* frame) {
     33   return new BufferedDataSource(render_loop, frame);
     34 }
     35 
     36 // static
     37 media::DataSourceFactory* BufferedDataSource::CreateFactory(
     38     MessageLoop* render_loop,
     39     WebKit::WebFrame* frame,
     40     WebDataSourceBuildObserverHack* build_observer) {
     41   return new WebDataSourceFactory(render_loop, frame, &NewBufferedDataSource,
     42                                   build_observer);
     43 }
     44 
     45 BufferedDataSource::BufferedDataSource(
     46     MessageLoop* render_loop,
     47     WebFrame* frame)
     48     : total_bytes_(kPositionNotSpecified),
     49       buffered_bytes_(0),
     50       loaded_(false),
     51       streaming_(false),
     52       frame_(frame),
     53       loader_(NULL),
     54       network_activity_(false),
     55       initialize_callback_(NULL),
     56       read_callback_(NULL),
     57       read_position_(0),
     58       read_size_(0),
     59       read_buffer_(NULL),
     60       read_attempts_(0),
     61       intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
     62       intermediate_read_buffer_size_(kInitialReadBufferSize),
     63       render_loop_(render_loop),
     64       stop_signal_received_(false),
     65       stopped_on_render_loop_(false),
     66       media_is_paused_(true),
     67       media_has_played_(false),
     68       preload_(media::METADATA),
     69       using_range_request_(true) {
     70 }
     71 
     72 BufferedDataSource::~BufferedDataSource() {
     73 }
     74 
     75 // A factory method to create BufferedResourceLoader using the read parameters.
     76 // This method can be overrided to inject mock BufferedResourceLoader object
     77 // for testing purpose.
     78 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
     79     int64 first_byte_position, int64 last_byte_position) {
     80   DCHECK(MessageLoop::current() == render_loop_);
     81 
     82   return new BufferedResourceLoader(url_,
     83                                     first_byte_position,
     84                                     last_byte_position);
     85 }
     86 
     87 // This method simply returns kTimeoutMilliseconds. The purpose of this
     88 // method is to be overidded so as to provide a different timeout value
     89 // for testing purpose.
     90 base::TimeDelta BufferedDataSource::GetTimeoutMilliseconds() {
     91   return base::TimeDelta::FromMilliseconds(kTimeoutMilliseconds);
     92 }
     93 
     94 void BufferedDataSource::set_host(media::FilterHost* host) {
     95   DataSource::set_host(host);
     96 
     97   if (loader_.get())
     98     UpdateHostState();
     99 }
    100 
    101 void BufferedDataSource::Initialize(const std::string& url,
    102                                     media::PipelineStatusCallback* callback) {
    103   // Saves the url.
    104   url_ = GURL(url);
    105 
    106   // This data source doesn't support data:// protocol so reject it.
    107   if (url_.SchemeIs(kDataScheme)) {
    108     callback->Run(media::DATASOURCE_ERROR_URL_NOT_SUPPORTED);
    109     delete callback;
    110     return;
    111   } else if (!IsProtocolSupportedForMedia(url_)) {
    112     callback->Run(media::PIPELINE_ERROR_NETWORK);
    113     delete callback;
    114     return;
    115   }
    116 
    117   DCHECK(callback);
    118   initialize_callback_.reset(callback);
    119 
    120   media_format_.SetAsString(media::MediaFormat::kURL, url);
    121 
    122   // Post a task to complete the initialization task.
    123   render_loop_->PostTask(FROM_HERE,
    124       NewRunnableMethod(this, &BufferedDataSource::InitializeTask));
    125 }
    126 
    127 void BufferedDataSource::CancelInitialize() {
    128   base::AutoLock auto_lock(lock_);
    129   DCHECK(initialize_callback_.get());
    130 
    131   initialize_callback_.reset();
    132 
    133   render_loop_->PostTask(
    134       FROM_HERE, NewRunnableMethod(this, &BufferedDataSource::CleanupTask));
    135 }
    136 
    137 /////////////////////////////////////////////////////////////////////////////
    138 // media::Filter implementation.
    139 void BufferedDataSource::Stop(media::FilterCallback* callback) {
    140   {
    141     base::AutoLock auto_lock(lock_);
    142     stop_signal_received_ = true;
    143   }
    144   if (callback) {
    145     callback->Run();
    146     delete callback;
    147   }
    148 
    149   render_loop_->PostTask(FROM_HERE,
    150       NewRunnableMethod(this, &BufferedDataSource::CleanupTask));
    151 }
    152 
    153 void BufferedDataSource::SetPlaybackRate(float playback_rate) {
    154   render_loop_->PostTask(FROM_HERE,
    155       NewRunnableMethod(this, &BufferedDataSource::SetPlaybackRateTask,
    156                         playback_rate));
    157 }
    158 
    159 void BufferedDataSource::SetPreload(media::Preload preload) {
    160   render_loop_->PostTask(FROM_HERE,
    161       NewRunnableMethod(this, &BufferedDataSource::SetPreloadTask,
    162                         preload));
    163 }
    164 
    165 /////////////////////////////////////////////////////////////////////////////
    166 // media::DataSource implementation.
    167 void BufferedDataSource::Read(int64 position, size_t size, uint8* data,
    168                               media::DataSource::ReadCallback* read_callback) {
    169   DCHECK(read_callback);
    170 
    171   {
    172     base::AutoLock auto_lock(lock_);
    173     DCHECK(!read_callback_.get());
    174 
    175     if (stop_signal_received_ || stopped_on_render_loop_) {
    176       read_callback->RunWithParams(
    177           Tuple1<size_t>(static_cast<size_t>(media::DataSource::kReadError)));
    178       delete read_callback;
    179       return;
    180     }
    181 
    182     read_callback_.reset(read_callback);
    183   }
    184 
    185   render_loop_->PostTask(FROM_HERE,
    186       NewRunnableMethod(this, &BufferedDataSource::ReadTask,
    187                         position, static_cast<int>(size), data));
    188 }
    189 
    190 bool BufferedDataSource::GetSize(int64* size_out) {
    191   if (total_bytes_ != kPositionNotSpecified) {
    192     *size_out = total_bytes_;
    193     return true;
    194   }
    195   *size_out = 0;
    196   return false;
    197 }
    198 
    199 bool BufferedDataSource::IsStreaming() {
    200   return streaming_;
    201 }
    202 
    203 bool BufferedDataSource::HasSingleOrigin() {
    204   DCHECK(MessageLoop::current() == render_loop_);
    205   return loader_.get() ? loader_->HasSingleOrigin() : true;
    206 }
    207 
    208 void BufferedDataSource::Abort() {
    209   DCHECK(MessageLoop::current() == render_loop_);
    210 
    211   CleanupTask();
    212   frame_ = NULL;
    213 }
    214 
    215 /////////////////////////////////////////////////////////////////////////////
    216 // Render thread tasks.
    217 void BufferedDataSource::InitializeTask() {
    218   DCHECK(MessageLoop::current() == render_loop_);
    219   DCHECK(!loader_.get());
    220   if (stopped_on_render_loop_ || !initialize_callback_.get())
    221     return;
    222 
    223   // Kick starts the watch dog task that will handle connection timeout.
    224   // We run the watch dog 2 times faster the actual timeout so as to catch
    225   // the timeout more accurately.
    226   watch_dog_timer_.Start(
    227       GetTimeoutMilliseconds() / 2,
    228       this,
    229       &BufferedDataSource::WatchDogTask);
    230 
    231   if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
    232     // Do an unbounded range request starting at the beginning.  If the server
    233     // responds with 200 instead of 206 we'll fall back into a streaming mode.
    234     loader_ = CreateResourceLoader(0, kPositionNotSpecified);
    235     loader_->Start(
    236         NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
    237         NewCallback(this, &BufferedDataSource::NetworkEventCallback),
    238         frame_);
    239   } else {
    240     // For all other protocols, assume they support range request. We fetch
    241     // the full range of the resource to obtain the instance size because
    242     // we won't be served HTTP headers.
    243     loader_ = CreateResourceLoader(kPositionNotSpecified,
    244                                    kPositionNotSpecified);
    245     loader_->Start(
    246         NewCallback(this, &BufferedDataSource::NonHttpInitialStartCallback),
    247         NewCallback(this, &BufferedDataSource::NetworkEventCallback),
    248         frame_);
    249   }
    250 }
    251 
    252 void BufferedDataSource::ReadTask(
    253     int64 position,
    254     int read_size,
    255     uint8* buffer) {
    256   DCHECK(MessageLoop::current() == render_loop_);
    257   {
    258     base::AutoLock auto_lock(lock_);
    259     if (stopped_on_render_loop_)
    260       return;
    261 
    262     DCHECK(read_callback_.get());
    263   }
    264 
    265   // Saves the read parameters.
    266   read_position_ = position;
    267   read_size_ = read_size;
    268   read_buffer_ = buffer;
    269   read_submitted_time_ = base::Time::Now();
    270   read_attempts_ = 0;
    271 
    272   // Call to read internal to perform the actual read.
    273   ReadInternal();
    274 }
    275 
    276 void BufferedDataSource::CleanupTask() {
    277   DCHECK(MessageLoop::current() == render_loop_);
    278 
    279   {
    280     base::AutoLock auto_lock(lock_);
    281     if (stopped_on_render_loop_)
    282       return;
    283 
    284     // Signal that stop task has finished execution.
    285     // NOTE: it's vital that this be set under lock, as that's how Read() tests
    286     // before registering a new |read_callback_| (which is cleared below).
    287     stopped_on_render_loop_ = true;
    288 
    289     if (read_callback_.get())
    290       DoneRead_Locked(net::ERR_FAILED);
    291   }
    292 
    293   // Stop the watch dog.
    294   watch_dog_timer_.Stop();
    295 
    296   // We just need to stop the loader, so it stops activity.
    297   if (loader_.get())
    298     loader_->Stop();
    299 
    300   // Reset the parameters of the current read request.
    301   read_position_ = 0;
    302   read_size_ = 0;
    303   read_buffer_ = 0;
    304   read_submitted_time_ = base::Time();
    305   read_attempts_ = 0;
    306 }
    307 
    308 void BufferedDataSource::RestartLoadingTask() {
    309   DCHECK(MessageLoop::current() == render_loop_);
    310   if (stopped_on_render_loop_)
    311     return;
    312 
    313   {
    314     // If there's no outstanding read then return early.
    315     base::AutoLock auto_lock(lock_);
    316     if (!read_callback_.get())
    317       return;
    318   }
    319 
    320   loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
    321   BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
    322   loader_->UpdateDeferStrategy(strategy);
    323   loader_->Start(
    324       NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
    325       NewCallback(this, &BufferedDataSource::NetworkEventCallback),
    326       frame_);
    327 }
    328 
    329 void BufferedDataSource::WatchDogTask() {
    330   DCHECK(MessageLoop::current() == render_loop_);
    331   if (stopped_on_render_loop_)
    332     return;
    333 
    334   // We only care if there is an active read request.
    335   {
    336     base::AutoLock auto_lock(lock_);
    337     if (!read_callback_.get())
    338       return;
    339   }
    340 
    341   DCHECK(loader_.get());
    342   base::TimeDelta delta = base::Time::Now() - read_submitted_time_;
    343   if (delta < GetTimeoutMilliseconds())
    344     return;
    345 
    346   // TODO(hclam): Maybe raise an error here. But if an error is reported
    347   // the whole pipeline may get destroyed...
    348   if (read_attempts_ >= kReadTrials)
    349     return;
    350 
    351   ++read_attempts_;
    352   read_submitted_time_ = base::Time::Now();
    353 
    354   // Stops the current loader and creates a new resource loader and
    355   // retry the request.
    356   loader_->Stop();
    357   loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
    358   BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
    359   loader_->UpdateDeferStrategy(strategy);
    360   loader_->Start(
    361       NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
    362       NewCallback(this, &BufferedDataSource::NetworkEventCallback),
    363       frame_);
    364 }
    365 
    366 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
    367   DCHECK(MessageLoop::current() == render_loop_);
    368   DCHECK(loader_.get());
    369 
    370   bool previously_paused = media_is_paused_;
    371   media_is_paused_ = (playback_rate == 0.0);
    372 
    373   if (!media_has_played_ && previously_paused && !media_is_paused_)
    374     media_has_played_ = true;
    375 
    376   BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
    377   loader_->UpdateDeferStrategy(strategy);
    378 }
    379 
    380 void BufferedDataSource::SetPreloadTask(media::Preload preload) {
    381   DCHECK(MessageLoop::current() == render_loop_);
    382   preload_ = preload;
    383 }
    384 
    385 BufferedResourceLoader::DeferStrategy
    386 BufferedDataSource::ChooseDeferStrategy() {
    387   // If the user indicates preload=metadata, then just load exactly
    388   // what is needed for starting the pipeline and prerolling frames.
    389   if (preload_ == media::METADATA && !media_has_played_)
    390     return BufferedResourceLoader::kReadThenDefer;
    391 
    392   // In general, we want to try to buffer the entire video when the video
    393   // is paused. But we don't want to do this if the video hasn't played yet
    394   // and preload!=auto.
    395   if (media_is_paused_ &&
    396       (preload_ == media::AUTO || media_has_played_)) {
    397     return BufferedResourceLoader::kNeverDefer;
    398   }
    399 
    400   // When the video is playing, regardless of preload state, we buffer up
    401   // to a hard limit and enable/disable deferring when the buffer is
    402   // depleted/full.
    403   return BufferedResourceLoader::kThresholdDefer;
    404 }
    405 
    406 // This method is the place where actual read happens, |loader_| must be valid
    407 // prior to make this method call.
    408 void BufferedDataSource::ReadInternal() {
    409   DCHECK(MessageLoop::current() == render_loop_);
    410   DCHECK(loader_);
    411 
    412   // First we prepare the intermediate read buffer for BufferedResourceLoader
    413   // to write to.
    414   if (read_size_ > intermediate_read_buffer_size_) {
    415     intermediate_read_buffer_.reset(new uint8[read_size_]);
    416   }
    417 
    418   // Perform the actual read with BufferedResourceLoader.
    419   loader_->Read(read_position_, read_size_, intermediate_read_buffer_.get(),
    420                 NewCallback(this, &BufferedDataSource::ReadCallback));
    421 }
    422 
    423 // Method to report the results of the current read request. Also reset all
    424 // the read parameters.
    425 void BufferedDataSource::DoneRead_Locked(int error) {
    426   DCHECK(MessageLoop::current() == render_loop_);
    427   DCHECK(read_callback_.get());
    428   lock_.AssertAcquired();
    429 
    430   if (error >= 0) {
    431     read_callback_->RunWithParams(Tuple1<size_t>(error));
    432   } else {
    433     read_callback_->RunWithParams(
    434         Tuple1<size_t>(static_cast<size_t>(media::DataSource::kReadError)));
    435   }
    436 
    437   read_callback_.reset();
    438   read_position_ = 0;
    439   read_size_ = 0;
    440   read_buffer_ = 0;
    441 }
    442 
    443 void BufferedDataSource::DoneInitialization_Locked(
    444     media::PipelineStatus status) {
    445   DCHECK(MessageLoop::current() == render_loop_);
    446   DCHECK(initialize_callback_.get());
    447   lock_.AssertAcquired();
    448 
    449   scoped_ptr<media::PipelineStatusCallback> initialize_callback(
    450       initialize_callback_.release());
    451   initialize_callback->Run(status);
    452 }
    453 
    454 /////////////////////////////////////////////////////////////////////////////
    455 // BufferedResourceLoader callback methods.
    456 void BufferedDataSource::HttpInitialStartCallback(int error) {
    457   DCHECK(MessageLoop::current() == render_loop_);
    458   DCHECK(loader_.get());
    459 
    460   int64 instance_size = loader_->instance_size();
    461   bool success = error == net::OK;
    462 
    463   if (!initialize_callback_.get()) {
    464     loader_->Stop();
    465     return;
    466   }
    467 
    468   if (success) {
    469     // TODO(hclam): Needs more thinking about supporting servers without range
    470     // request or their partial response is not complete.
    471     total_bytes_ = instance_size;
    472     loaded_ = false;
    473     streaming_ = (instance_size == kPositionNotSpecified) ||
    474         !loader_->range_supported();
    475   } else {
    476     // TODO(hclam): In case of failure, we can retry several times.
    477     loader_->Stop();
    478   }
    479 
    480   if (error == net::ERR_INVALID_RESPONSE && using_range_request_) {
    481     // Assuming that the Range header was causing the problem. Retry without
    482     // the Range header.
    483     using_range_request_ = false;
    484     loader_ = CreateResourceLoader(kPositionNotSpecified,
    485                                    kPositionNotSpecified);
    486     loader_->Start(
    487         NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
    488         NewCallback(this, &BufferedDataSource::NetworkEventCallback),
    489         frame_);
    490     return;
    491   }
    492 
    493   // Reference to prevent destruction while inside the |initialize_callback_|
    494   // call. This is a temporary fix to prevent crashes caused by holding the
    495   // lock and running the destructor.
    496   // TODO: Review locking in this class and figure out a way to run the callback
    497   //       w/o the lock.
    498   scoped_refptr<BufferedDataSource> destruction_guard(this);
    499   {
    500     // We need to prevent calling to filter host and running the callback if
    501     // we have received the stop signal. We need to lock down the whole callback
    502     // method to prevent bad things from happening. The reason behind this is
    503     // that we cannot guarantee tasks on render thread have completely stopped
    504     // when we receive the Stop() method call. The only way to solve this is to
    505     // let tasks on render thread to run but make sure they don't call outside
    506     // this object when Stop() method is ever called. Locking this method is
    507     // safe because |lock_| is only acquired in tasks on render thread.
    508     base::AutoLock auto_lock(lock_);
    509     if (stop_signal_received_)
    510       return;
    511 
    512     if (!success) {
    513       DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
    514       return;
    515     }
    516 
    517     UpdateHostState();
    518     DoneInitialization_Locked(media::PIPELINE_OK);
    519   }
    520 }
    521 
    522 void BufferedDataSource::NonHttpInitialStartCallback(int error) {
    523   DCHECK(MessageLoop::current() == render_loop_);
    524   DCHECK(loader_.get());
    525 
    526   if (!initialize_callback_.get()) {
    527     loader_->Stop();
    528     return;
    529   }
    530 
    531   int64 instance_size = loader_->instance_size();
    532   bool success = error == net::OK && instance_size != kPositionNotSpecified;
    533 
    534   if (success) {
    535     total_bytes_ = instance_size;
    536     buffered_bytes_ = total_bytes_;
    537     loaded_ = true;
    538   } else {
    539     loader_->Stop();
    540   }
    541 
    542   // Reference to prevent destruction while inside the |initialize_callback_|
    543   // call. This is a temporary fix to prevent crashes caused by holding the
    544   // lock and running the destructor.
    545   // TODO: Review locking in this class and figure out a way to run the callback
    546   //       w/o the lock.
    547   scoped_refptr<BufferedDataSource> destruction_guard(this);
    548   {
    549     // We need to prevent calling to filter host and running the callback if
    550     // we have received the stop signal. We need to lock down the whole callback
    551     // method to prevent bad things from happening. The reason behind this is
    552     // that we cannot guarantee tasks on render thread have completely stopped
    553     // when we receive the Stop() method call. The only way to solve this is to
    554     // let tasks on render thread to run but make sure they don't call outside
    555     // this object when Stop() method is ever called. Locking this method is
    556     // safe because |lock_| is only acquired in tasks on render thread.
    557     base::AutoLock auto_lock(lock_);
    558     if (stop_signal_received_ || !initialize_callback_.get())
    559       return;
    560 
    561     if (!success) {
    562       DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
    563       return;
    564     }
    565 
    566     UpdateHostState();
    567     DoneInitialization_Locked(media::PIPELINE_OK);
    568   }
    569 }
    570 
    571 void BufferedDataSource::PartialReadStartCallback(int error) {
    572   DCHECK(MessageLoop::current() == render_loop_);
    573   DCHECK(loader_.get());
    574 
    575   if (error == net::OK) {
    576     // Once the request has started successfully, we can proceed with
    577     // reading from it.
    578     ReadInternal();
    579     return;
    580   }
    581 
    582   // Stop the resource loader since we have received an error.
    583   loader_->Stop();
    584 
    585   // We need to prevent calling to filter host and running the callback if
    586   // we have received the stop signal. We need to lock down the whole callback
    587   // method to prevent bad things from happening. The reason behind this is
    588   // that we cannot guarantee tasks on render thread have completely stopped
    589   // when we receive the Stop() method call. So only way to solve this is to
    590   // let tasks on render thread to run but make sure they don't call outside
    591   // this object when Stop() method is ever called. Locking this method is
    592   // safe because |lock_| is only acquired in tasks on render thread.
    593   base::AutoLock auto_lock(lock_);
    594   if (stop_signal_received_)
    595     return;
    596   DoneRead_Locked(net::ERR_INVALID_RESPONSE);
    597 }
    598 
    599 void BufferedDataSource::ReadCallback(int error) {
    600   DCHECK(MessageLoop::current() == render_loop_);
    601 
    602   if (error < 0) {
    603     DCHECK(loader_.get());
    604 
    605     // Stop the resource load if it failed.
    606     loader_->Stop();
    607 
    608     if (error == net::ERR_CACHE_MISS) {
    609       render_loop_->PostTask(FROM_HERE,
    610           NewRunnableMethod(this, &BufferedDataSource::RestartLoadingTask));
    611       return;
    612     }
    613   }
    614 
    615   // We need to prevent calling to filter host and running the callback if
    616   // we have received the stop signal. We need to lock down the whole callback
    617   // method to prevent bad things from happening. The reason behind this is
    618   // that we cannot guarantee tasks on render thread have completely stopped
    619   // when we receive the Stop() method call. So only way to solve this is to
    620   // let tasks on render thread to run but make sure they don't call outside
    621   // this object when Stop() method is ever called. Locking this method is safe
    622   // because |lock_| is only acquired in tasks on render thread.
    623   base::AutoLock auto_lock(lock_);
    624   if (stop_signal_received_)
    625     return;
    626 
    627   if (error > 0) {
    628     // If a position error code is received, read was successful. So copy
    629     // from intermediate read buffer to the target read buffer.
    630     memcpy(read_buffer_, intermediate_read_buffer_.get(), error);
    631   } else if (error == 0 && total_bytes_ == kPositionNotSpecified) {
    632     // We've reached the end of the file and we didn't know the total size
    633     // before. Update the total size so Read()s past the end of the file will
    634     // fail like they would if we had known the file size at the beginning.
    635     total_bytes_ = loader_->instance_size();
    636 
    637     if (host() && total_bytes_ != kPositionNotSpecified)
    638       host()->SetTotalBytes(total_bytes_);
    639   }
    640   DoneRead_Locked(error);
    641 }
    642 
    643 void BufferedDataSource::NetworkEventCallback() {
    644   DCHECK(MessageLoop::current() == render_loop_);
    645   DCHECK(loader_.get());
    646 
    647   // In case of non-HTTP request we don't need to report network events,
    648   // so return immediately.
    649   if (loaded_)
    650     return;
    651 
    652   bool network_activity = loader_->network_activity();
    653   int64 buffered_position = loader_->GetBufferedPosition();
    654 
    655   // If we get an unspecified value, return immediately.
    656   if (buffered_position == kPositionNotSpecified)
    657     return;
    658 
    659   // We need to prevent calling to filter host and running the callback if
    660   // we have received the stop signal. We need to lock down the whole callback
    661   // method to prevent bad things from happening. The reason behind this is
    662   // that we cannot guarantee tasks on render thread have completely stopped
    663   // when we receive the Stop() method call. So only way to solve this is to
    664   // let tasks on render thread to run but make sure they don't call outside
    665   // this object when Stop() method is ever called. Locking this method is safe
    666   // because |lock_| is only acquired in tasks on render thread.
    667   base::AutoLock auto_lock(lock_);
    668   if (stop_signal_received_)
    669     return;
    670 
    671   if (network_activity != network_activity_) {
    672     network_activity_ = network_activity;
    673     if (host())
    674       host()->SetNetworkActivity(network_activity);
    675   }
    676 
    677   buffered_bytes_ = buffered_position + 1;
    678   if (host())
    679     host()->SetBufferedBytes(buffered_bytes_);
    680 }
    681 
    682 void BufferedDataSource::UpdateHostState() {
    683   media::FilterHost* filter_host = host();
    684   if (!filter_host)
    685     return;
    686 
    687   filter_host->SetLoaded(loaded_);
    688 
    689   if (streaming_) {
    690     filter_host->SetStreaming(true);
    691   } else {
    692     filter_host->SetTotalBytes(total_bytes_);
    693     filter_host->SetBufferedBytes(buffered_bytes_);
    694   }
    695 }
    696 
    697 }  // namespace webkit_glue
    698