Home | History | Annotate | Download | only in media
      1 // Copyright 2013 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 "content/renderer/media/buffered_data_source.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback_helpers.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "media/base/media_log.h"
     11 #include "net/base/net_errors.h"
     12 
     13 using WebKit::WebFrame;
     14 
     15 namespace {
     16 
     17 // BufferedDataSource has an intermediate buffer, this value governs the initial
     18 // size of that buffer. It is set to 32KB because this is a typical read size
     19 // of FFmpeg.
     20 const int kInitialReadBufferSize = 32768;
     21 
     22 // Number of cache misses we allow for a single Read() before signaling an
     23 // error.
     24 const int kNumCacheMissRetries = 3;
     25 
     26 }  // namespace
     27 
     28 namespace content {
     29 
     30 class BufferedDataSource::ReadOperation {
     31  public:
     32   ReadOperation(int64 position, int size, uint8* data,
     33                 const media::DataSource::ReadCB& callback);
     34   ~ReadOperation();
     35 
     36   // Runs |callback_| with the given |result|, deleting the operation
     37   // afterwards.
     38   static void Run(scoped_ptr<ReadOperation> read_op, int result);
     39 
     40   // State for the number of times this read operation has been retried.
     41   int retries() { return retries_; }
     42   void IncrementRetries() { ++retries_; }
     43 
     44   int64 position() { return position_; }
     45   int size() { return size_; }
     46   uint8* data() { return data_; }
     47 
     48  private:
     49   int retries_;
     50 
     51   const int64 position_;
     52   const int size_;
     53   uint8* data_;
     54   media::DataSource::ReadCB callback_;
     55 
     56   DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
     57 };
     58 
     59 BufferedDataSource::ReadOperation::ReadOperation(
     60     int64 position, int size, uint8* data,
     61     const media::DataSource::ReadCB& callback)
     62     : retries_(0),
     63       position_(position),
     64       size_(size),
     65       data_(data),
     66       callback_(callback) {
     67   DCHECK(!callback_.is_null());
     68 }
     69 
     70 BufferedDataSource::ReadOperation::~ReadOperation() {
     71   DCHECK(callback_.is_null());
     72 }
     73 
     74 // static
     75 void BufferedDataSource::ReadOperation::Run(
     76     scoped_ptr<ReadOperation> read_op, int result) {
     77   base::ResetAndReturn(&read_op->callback_).Run(result);
     78 }
     79 
     80 BufferedDataSource::BufferedDataSource(
     81     const scoped_refptr<base::MessageLoopProxy>& render_loop,
     82     WebFrame* frame,
     83     media::MediaLog* media_log,
     84     const DownloadingCB& downloading_cb)
     85     : weak_factory_(this),
     86       weak_this_(weak_factory_.GetWeakPtr()),
     87       cors_mode_(BufferedResourceLoader::kUnspecified),
     88       total_bytes_(kPositionNotSpecified),
     89       assume_fully_buffered_(false),
     90       streaming_(false),
     91       frame_(frame),
     92       intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
     93       intermediate_read_buffer_size_(kInitialReadBufferSize),
     94       render_loop_(render_loop),
     95       stop_signal_received_(false),
     96       media_has_played_(false),
     97       preload_(AUTO),
     98       bitrate_(0),
     99       playback_rate_(0.0),
    100       media_log_(media_log),
    101       downloading_cb_(downloading_cb) {
    102   DCHECK(!downloading_cb_.is_null());
    103 }
    104 
    105 BufferedDataSource::~BufferedDataSource() {}
    106 
    107 // A factory method to create BufferedResourceLoader using the read parameters.
    108 // This method can be overridden to inject mock BufferedResourceLoader object
    109 // for testing purpose.
    110 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
    111     int64 first_byte_position, int64 last_byte_position) {
    112   DCHECK(render_loop_->BelongsToCurrentThread());
    113 
    114   BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ?
    115       BufferedResourceLoader::kReadThenDefer :
    116       BufferedResourceLoader::kCapacityDefer;
    117 
    118   return new BufferedResourceLoader(url_,
    119                                     cors_mode_,
    120                                     first_byte_position,
    121                                     last_byte_position,
    122                                     strategy,
    123                                     bitrate_,
    124                                     playback_rate_,
    125                                     media_log_.get());
    126 }
    127 
    128 void BufferedDataSource::set_host(media::DataSourceHost* host) {
    129   DataSource::set_host(host);
    130 
    131   if (loader_) {
    132     base::AutoLock auto_lock(lock_);
    133     UpdateHostState_Locked();
    134   }
    135 }
    136 
    137 void BufferedDataSource::Initialize(
    138     const GURL& url,
    139     BufferedResourceLoader::CORSMode cors_mode,
    140     const InitializeCB& init_cb) {
    141   DCHECK(render_loop_->BelongsToCurrentThread());
    142   DCHECK(!init_cb.is_null());
    143   DCHECK(!loader_.get());
    144   url_ = url;
    145   cors_mode_ = cors_mode;
    146 
    147   init_cb_ = init_cb;
    148 
    149   if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
    150     // Do an unbounded range request starting at the beginning.  If the server
    151     // responds with 200 instead of 206 we'll fall back into a streaming mode.
    152     loader_.reset(CreateResourceLoader(0, kPositionNotSpecified));
    153   } else {
    154     // For all other protocols, assume they support range request. We fetch
    155     // the full range of the resource to obtain the instance size because
    156     // we won't be served HTTP headers.
    157     loader_.reset(CreateResourceLoader(kPositionNotSpecified,
    158                                        kPositionNotSpecified));
    159     assume_fully_buffered_ = true;
    160   }
    161 
    162   loader_->Start(
    163       base::Bind(&BufferedDataSource::StartCallback, weak_this_),
    164       base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this_),
    165       base::Bind(&BufferedDataSource::ProgressCallback, weak_this_),
    166       frame_);
    167 }
    168 
    169 void BufferedDataSource::SetPreload(Preload preload) {
    170   DCHECK(render_loop_->BelongsToCurrentThread());
    171   preload_ = preload;
    172 }
    173 
    174 bool BufferedDataSource::HasSingleOrigin() {
    175   DCHECK(render_loop_->BelongsToCurrentThread());
    176   DCHECK(init_cb_.is_null() && loader_.get())
    177       << "Initialize() must complete before calling HasSingleOrigin()";
    178   return loader_->HasSingleOrigin();
    179 }
    180 
    181 bool BufferedDataSource::DidPassCORSAccessCheck() const {
    182   return loader_.get() && loader_->DidPassCORSAccessCheck();
    183 }
    184 
    185 void BufferedDataSource::Abort() {
    186   DCHECK(render_loop_->BelongsToCurrentThread());
    187   {
    188     base::AutoLock auto_lock(lock_);
    189     StopInternal_Locked();
    190   }
    191   StopLoader();
    192   frame_ = NULL;
    193 }
    194 
    195 /////////////////////////////////////////////////////////////////////////////
    196 // media::DataSource implementation.
    197 void BufferedDataSource::Stop(const base::Closure& closure) {
    198   {
    199     base::AutoLock auto_lock(lock_);
    200     StopInternal_Locked();
    201   }
    202   closure.Run();
    203 
    204   render_loop_->PostTask(FROM_HERE,
    205       base::Bind(&BufferedDataSource::StopLoader, weak_this_));
    206 }
    207 
    208 void BufferedDataSource::SetPlaybackRate(float playback_rate) {
    209   render_loop_->PostTask(FROM_HERE, base::Bind(
    210       &BufferedDataSource::SetPlaybackRateTask, weak_this_, playback_rate));
    211 }
    212 
    213 void BufferedDataSource::SetBitrate(int bitrate) {
    214   render_loop_->PostTask(FROM_HERE, base::Bind(
    215       &BufferedDataSource::SetBitrateTask, weak_this_, bitrate));
    216 }
    217 
    218 void BufferedDataSource::Read(
    219     int64 position, int size, uint8* data,
    220     const media::DataSource::ReadCB& read_cb) {
    221   DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
    222   DCHECK(!read_cb.is_null());
    223 
    224   {
    225     base::AutoLock auto_lock(lock_);
    226     DCHECK(!read_op_);
    227 
    228     if (stop_signal_received_) {
    229       read_cb.Run(kReadError);
    230       return;
    231     }
    232 
    233     read_op_.reset(new ReadOperation(position, size, data, read_cb));
    234   }
    235 
    236   render_loop_->PostTask(FROM_HERE, base::Bind(
    237       &BufferedDataSource::ReadTask, weak_this_));
    238 }
    239 
    240 bool BufferedDataSource::GetSize(int64* size_out) {
    241   if (total_bytes_ != kPositionNotSpecified) {
    242     *size_out = total_bytes_;
    243     return true;
    244   }
    245   *size_out = 0;
    246   return false;
    247 }
    248 
    249 bool BufferedDataSource::IsStreaming() {
    250   return streaming_;
    251 }
    252 
    253 /////////////////////////////////////////////////////////////////////////////
    254 // Render thread tasks.
    255 void BufferedDataSource::ReadTask() {
    256   DCHECK(render_loop_->BelongsToCurrentThread());
    257   ReadInternal();
    258 }
    259 
    260 void BufferedDataSource::StopInternal_Locked() {
    261   lock_.AssertAcquired();
    262   if (stop_signal_received_)
    263     return;
    264 
    265   stop_signal_received_ = true;
    266 
    267   // Initialize() isn't part of the DataSource interface so don't call it in
    268   // response to Stop().
    269   init_cb_.Reset();
    270 
    271   if (read_op_)
    272     ReadOperation::Run(read_op_.Pass(), kReadError);
    273 }
    274 
    275 void BufferedDataSource::StopLoader() {
    276   DCHECK(render_loop_->BelongsToCurrentThread());
    277 
    278   if (loader_)
    279     loader_->Stop();
    280 }
    281 
    282 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
    283   DCHECK(render_loop_->BelongsToCurrentThread());
    284   DCHECK(loader_.get());
    285 
    286   if (playback_rate != 0)
    287     media_has_played_ = true;
    288 
    289   playback_rate_ = playback_rate;
    290   loader_->SetPlaybackRate(playback_rate);
    291 
    292   if (!loader_->range_supported()) {
    293     // 200 responses end up not being reused to satisfy future range requests,
    294     // and we don't want to get too far ahead of the read-head (and thus require
    295     // a restart), so keep to the thresholds.
    296     loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
    297   } else if (media_has_played_ && playback_rate == 0) {
    298     // If the playback has started (at which point the preload value is ignored)
    299     // and we're paused, then try to load as much as possible (the loader will
    300     // fall back to kCapacityDefer if it knows the current response won't be
    301     // useful from the cache in the future).
    302     loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer);
    303   } else {
    304     // If media is currently playing or the page indicated preload=auto,
    305     // use threshold strategy to enable/disable deferring when the buffer
    306     // is full/depleted.
    307     loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer);
    308   }
    309 }
    310 
    311 void BufferedDataSource::SetBitrateTask(int bitrate) {
    312   DCHECK(render_loop_->BelongsToCurrentThread());
    313   DCHECK(loader_.get());
    314 
    315   bitrate_ = bitrate;
    316   loader_->SetBitrate(bitrate);
    317 }
    318 
    319 // This method is the place where actual read happens, |loader_| must be valid
    320 // prior to make this method call.
    321 void BufferedDataSource::ReadInternal() {
    322   DCHECK(render_loop_->BelongsToCurrentThread());
    323   int64 position = 0;
    324   int size = 0;
    325   {
    326     base::AutoLock auto_lock(lock_);
    327     if (stop_signal_received_)
    328       return;
    329 
    330     position = read_op_->position();
    331     size = read_op_->size();
    332   }
    333 
    334   // First we prepare the intermediate read buffer for BufferedResourceLoader
    335   // to write to.
    336   if (size > intermediate_read_buffer_size_) {
    337     intermediate_read_buffer_.reset(new uint8[size]);
    338   }
    339 
    340   // Perform the actual read with BufferedResourceLoader.
    341   loader_->Read(
    342       position, size, intermediate_read_buffer_.get(),
    343       base::Bind(&BufferedDataSource::ReadCallback, weak_this_));
    344 }
    345 
    346 
    347 /////////////////////////////////////////////////////////////////////////////
    348 // BufferedResourceLoader callback methods.
    349 void BufferedDataSource::StartCallback(
    350     BufferedResourceLoader::Status status) {
    351   DCHECK(render_loop_->BelongsToCurrentThread());
    352   DCHECK(loader_.get());
    353 
    354   bool init_cb_is_null = false;
    355   {
    356     base::AutoLock auto_lock(lock_);
    357     init_cb_is_null = init_cb_.is_null();
    358   }
    359   if (init_cb_is_null) {
    360     loader_->Stop();
    361     return;
    362   }
    363 
    364   // All responses must be successful. Resources that are assumed to be fully
    365   // buffered must have a known content length.
    366   bool success = status == BufferedResourceLoader::kOk &&
    367       (!assume_fully_buffered_ ||
    368        loader_->instance_size() != kPositionNotSpecified);
    369 
    370   if (success) {
    371     total_bytes_ = loader_->instance_size();
    372     streaming_ = !assume_fully_buffered_ &&
    373         (total_bytes_ == kPositionNotSpecified || !loader_->range_supported());
    374   } else {
    375     loader_->Stop();
    376   }
    377 
    378   // TODO(scherkus): we shouldn't have to lock to signal host(), see
    379   // http://crbug.com/113712 for details.
    380   base::AutoLock auto_lock(lock_);
    381   if (stop_signal_received_)
    382     return;
    383 
    384   if (success)
    385     UpdateHostState_Locked();
    386 
    387   base::ResetAndReturn(&init_cb_).Run(success);
    388 }
    389 
    390 void BufferedDataSource::PartialReadStartCallback(
    391     BufferedResourceLoader::Status status) {
    392   DCHECK(render_loop_->BelongsToCurrentThread());
    393   DCHECK(loader_.get());
    394 
    395   if (status == BufferedResourceLoader::kOk) {
    396     // Once the request has started successfully, we can proceed with
    397     // reading from it.
    398     ReadInternal();
    399     return;
    400   }
    401 
    402   // Stop the resource loader since we have received an error.
    403   loader_->Stop();
    404 
    405   // TODO(scherkus): we shouldn't have to lock to signal host(), see
    406   // http://crbug.com/113712 for details.
    407   base::AutoLock auto_lock(lock_);
    408   if (stop_signal_received_)
    409     return;
    410   ReadOperation::Run(read_op_.Pass(), kReadError);
    411 }
    412 
    413 void BufferedDataSource::ReadCallback(
    414     BufferedResourceLoader::Status status,
    415     int bytes_read) {
    416   DCHECK(render_loop_->BelongsToCurrentThread());
    417 
    418   // TODO(scherkus): we shouldn't have to lock to signal host(), see
    419   // http://crbug.com/113712 for details.
    420   base::AutoLock auto_lock(lock_);
    421   if (stop_signal_received_)
    422     return;
    423 
    424   if (status != BufferedResourceLoader::kOk) {
    425     // Stop the resource load if it failed.
    426     loader_->Stop();
    427 
    428     if (status == BufferedResourceLoader::kCacheMiss &&
    429         read_op_->retries() < kNumCacheMissRetries) {
    430       read_op_->IncrementRetries();
    431 
    432       // Recreate a loader starting from where we last left off until the
    433       // end of the resource.
    434       loader_.reset(CreateResourceLoader(
    435           read_op_->position(), kPositionNotSpecified));
    436       loader_->Start(
    437           base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this_),
    438           base::Bind(&BufferedDataSource::LoadingStateChangedCallback,
    439                      weak_this_),
    440           base::Bind(&BufferedDataSource::ProgressCallback, weak_this_),
    441           frame_);
    442       return;
    443     }
    444 
    445     ReadOperation::Run(read_op_.Pass(), kReadError);
    446     return;
    447   }
    448 
    449   if (bytes_read > 0) {
    450     memcpy(read_op_->data(), intermediate_read_buffer_.get(), bytes_read);
    451   } else if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) {
    452     // We've reached the end of the file and we didn't know the total size
    453     // before. Update the total size so Read()s past the end of the file will
    454     // fail like they would if we had known the file size at the beginning.
    455     total_bytes_ = loader_->instance_size();
    456 
    457     if (host() && total_bytes_ != kPositionNotSpecified) {
    458       host()->SetTotalBytes(total_bytes_);
    459       host()->AddBufferedByteRange(loader_->first_byte_position(),
    460                                    total_bytes_);
    461     }
    462   }
    463   ReadOperation::Run(read_op_.Pass(), bytes_read);
    464 }
    465 
    466 void BufferedDataSource::LoadingStateChangedCallback(
    467     BufferedResourceLoader::LoadingState state) {
    468   DCHECK(render_loop_->BelongsToCurrentThread());
    469 
    470   if (assume_fully_buffered_)
    471     return;
    472 
    473   bool is_downloading_data;
    474   switch (state) {
    475     case BufferedResourceLoader::kLoading:
    476       is_downloading_data = true;
    477       break;
    478     case BufferedResourceLoader::kLoadingDeferred:
    479     case BufferedResourceLoader::kLoadingFinished:
    480       is_downloading_data = false;
    481       break;
    482 
    483     // TODO(scherkus): we don't signal network activity changes when loads
    484     // fail to preserve existing behaviour when deferring is toggled, however
    485     // we should consider changing DownloadingCB to also propagate loading
    486     // state. For example there isn't any signal today to notify the client that
    487     // loading has failed (we only get errors on subsequent reads).
    488     case BufferedResourceLoader::kLoadingFailed:
    489       return;
    490   }
    491 
    492   downloading_cb_.Run(is_downloading_data);
    493 }
    494 
    495 void BufferedDataSource::ProgressCallback(int64 position) {
    496   DCHECK(render_loop_->BelongsToCurrentThread());
    497 
    498   if (assume_fully_buffered_)
    499     return;
    500 
    501   // TODO(scherkus): we shouldn't have to lock to signal host(), see
    502   // http://crbug.com/113712 for details.
    503   base::AutoLock auto_lock(lock_);
    504   if (stop_signal_received_)
    505     return;
    506 
    507   ReportOrQueueBufferedBytes(loader_->first_byte_position(), position);
    508 }
    509 
    510 void BufferedDataSource::ReportOrQueueBufferedBytes(int64 start, int64 end) {
    511   if (host())
    512     host()->AddBufferedByteRange(start, end);
    513   else
    514     queued_buffered_byte_ranges_.Add(start, end);
    515 }
    516 
    517 void BufferedDataSource::UpdateHostState_Locked() {
    518   lock_.AssertAcquired();
    519 
    520   if (!host())
    521     return;
    522 
    523   for (size_t i = 0; i < queued_buffered_byte_ranges_.size(); ++i) {
    524     host()->AddBufferedByteRange(queued_buffered_byte_ranges_.start(i),
    525                                  queued_buffered_byte_ranges_.end(i));
    526   }
    527   queued_buffered_byte_ranges_.clear();
    528 
    529   if (total_bytes_ == kPositionNotSpecified)
    530     return;
    531 
    532   host()->SetTotalBytes(total_bytes_);
    533 
    534   if (assume_fully_buffered_)
    535     host()->AddBufferedByteRange(0, total_bytes_);
    536 }
    537 
    538 }  // namespace content
    539