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 "media/blink/multibuffer_data_source.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/location.h"
12 #include "base/macros.h"
13 #include "base/numerics/ranges.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "base/single_thread_task_runner.h"
16 #include "media/base/media_log.h"
17 #include "media/blink/buffered_data_source_host_impl.h"
18 #include "media/blink/multibuffer_reader.h"
19 #include "net/base/net_errors.h"
20 
21 namespace {
22 
23 // Minimum preload buffer.
24 const int64_t kMinBufferPreload = 2 << 20;  // 2 Mb
25 // Maxmimum preload buffer.
26 const int64_t kMaxBufferPreload = 50 << 20;  // 50 Mb
27 
28 // If preload_ == METADATA, preloading size will be
29 // shifted down this many bits. This shift turns
30 // one Mb into one 32k block.
31 // This seems to be the smallest amount of preload we can do without
32 // ending up repeatedly closing and re-opening the connection
33 // due to read calls after OnBufferingHaveEnough have been called.
34 const int64_t kMetadataShift = 6;
35 
36 // Preload this much extra, then stop preloading until we fall below the
37 // kTargetSecondsBufferedAhead.
38 const int64_t kPreloadHighExtra = 1 << 20;  // 1 Mb
39 
40 // Default pin region size.
41 // Note that we go over this if preload is calculated high enough.
42 const int64_t kDefaultPinSize = 25 << 20;  // 25 Mb
43 
44 // If bitrate is not known, use this.
45 const int64_t kDefaultBitrate = 200 * 8 << 10;  // 200 Kbps.
46 
47 // Maximum bitrate for buffer calculations.
48 const int64_t kMaxBitrate = 20 * 8 << 20;  // 20 Mbps.
49 
50 // Maximum playback rate for buffer calculations.
51 const double kMaxPlaybackRate = 25.0;
52 
53 // Preload this many seconds of data by default.
54 const int64_t kTargetSecondsBufferedAhead = 10;
55 
56 // Keep this many seconds of data for going back by default.
57 const int64_t kTargetSecondsBufferedBehind = 2;
58 
59 // Extra buffer accumulation speed, in terms of download buffer.
60 const int kSlowPreloadPercentage = 10;
61 
62 // Update buffer sizes every 32 progress updates.
63 const int kUpdateBufferSizeFrequency = 32;
64 
65 // How long to we delay a seek after a read?
66 constexpr base::TimeDelta kSeekDelay = base::TimeDelta::FromMilliseconds(20);
67 
68 }  // namespace
69 
70 namespace media {
71 
72 class MultibufferDataSource::ReadOperation {
73  public:
74   ReadOperation(int64_t position,
75                 int size,
76                 uint8_t* data,
77                 DataSource::ReadCB callback);
78   ~ReadOperation();
79 
80   // Runs |callback_| with the given |result|, deleting the operation
81   // afterwards.
82   static void Run(std::unique_ptr<ReadOperation> read_op, int result);
83 
position()84   int64_t position() { return position_; }
size()85   int size() { return size_; }
data()86   uint8_t* data() { return data_; }
87 
88  private:
89   const int64_t position_;
90   const int size_;
91   uint8_t* data_;
92   DataSource::ReadCB callback_;
93 
94   DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
95 };
96 
ReadOperation(int64_t position,int size,uint8_t * data,DataSource::ReadCB callback)97 MultibufferDataSource::ReadOperation::ReadOperation(int64_t position,
98                                                     int size,
99                                                     uint8_t* data,
100                                                     DataSource::ReadCB callback)
101     : position_(position),
102       size_(size),
103       data_(data),
104       callback_(std::move(callback)) {
105   DCHECK(!callback_.is_null());
106 }
107 
~ReadOperation()108 MultibufferDataSource::ReadOperation::~ReadOperation() {
109   DCHECK(callback_.is_null());
110 }
111 
112 // static
Run(std::unique_ptr<ReadOperation> read_op,int result)113 void MultibufferDataSource::ReadOperation::Run(
114     std::unique_ptr<ReadOperation> read_op,
115     int result) {
116   std::move(read_op->callback_).Run(result);
117 }
118 
MultibufferDataSource(const scoped_refptr<base::SingleThreadTaskRunner> & task_runner,scoped_refptr<UrlData> url_data_arg,MediaLog * media_log,BufferedDataSourceHost * host,DownloadingCB downloading_cb)119 MultibufferDataSource::MultibufferDataSource(
120     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
121     scoped_refptr<UrlData> url_data_arg,
122     MediaLog* media_log,
123     BufferedDataSourceHost* host,
124     DownloadingCB downloading_cb)
125     : total_bytes_(kPositionNotSpecified),
126       streaming_(false),
127       loading_(false),
128       failed_(false),
129       render_task_runner_(task_runner),
130       url_data_(std::move(url_data_arg)),
131       stop_signal_received_(false),
132       media_has_played_(false),
133       single_origin_(true),
134       cancel_on_defer_(false),
135       preload_(AUTO),
136       bitrate_(0),
137       playback_rate_(0.0),
138       media_log_(media_log),
139       host_(host),
140       downloading_cb_(std::move(downloading_cb)) {
141   weak_ptr_ = weak_factory_.GetWeakPtr();
142   DCHECK(host_);
143   DCHECK(downloading_cb_);
144   DCHECK(render_task_runner_->BelongsToCurrentThread());
145   DCHECK(url_data_.get());
146   url_data_->Use();
147   url_data_->OnRedirect(
148       base::BindOnce(&MultibufferDataSource::OnRedirect, weak_ptr_));
149 }
150 
~MultibufferDataSource()151 MultibufferDataSource::~MultibufferDataSource() {
152   DCHECK(render_task_runner_->BelongsToCurrentThread());
153 }
154 
media_has_played() const155 bool MultibufferDataSource::media_has_played() const {
156   return media_has_played_;
157 }
158 
AssumeFullyBuffered() const159 bool MultibufferDataSource::AssumeFullyBuffered() const {
160   DCHECK(url_data_);
161   return !url_data_->url().SchemeIsHTTPOrHTTPS();
162 }
163 
SetReader(MultiBufferReader * reader)164 void MultibufferDataSource::SetReader(MultiBufferReader* reader) {
165   DCHECK(render_task_runner_->BelongsToCurrentThread());
166   base::AutoLock auto_lock(lock_);
167   reader_.reset(reader);
168 }
169 
CreateResourceLoader(int64_t first_byte_position,int64_t last_byte_position)170 void MultibufferDataSource::CreateResourceLoader(int64_t first_byte_position,
171                                                  int64_t last_byte_position) {
172   DCHECK(render_task_runner_->BelongsToCurrentThread());
173 
174   SetReader(new MultiBufferReader(
175       url_data_->multibuffer(), first_byte_position, last_byte_position,
176       base::BindRepeating(&MultibufferDataSource::ProgressCallback,
177                           weak_ptr_)));
178   reader_->SetIsClientAudioElement(is_client_audio_element_);
179   UpdateBufferSizes();
180 }
181 
CreateResourceLoader_Locked(int64_t first_byte_position,int64_t last_byte_position)182 void MultibufferDataSource::CreateResourceLoader_Locked(
183     int64_t first_byte_position,
184     int64_t last_byte_position) {
185   DCHECK(render_task_runner_->BelongsToCurrentThread());
186   lock_.AssertAcquired();
187 
188   reader_ = std::make_unique<MultiBufferReader>(
189       url_data_->multibuffer(), first_byte_position, last_byte_position,
190       base::BindRepeating(&MultibufferDataSource::ProgressCallback, weak_ptr_));
191   UpdateBufferSizes();
192 }
193 
Initialize(InitializeCB init_cb)194 void MultibufferDataSource::Initialize(InitializeCB init_cb) {
195   DCHECK(render_task_runner_->BelongsToCurrentThread());
196   DCHECK(init_cb);
197   DCHECK(!reader_.get());
198 
199   init_cb_ = std::move(init_cb);
200 
201   CreateResourceLoader(0, kPositionNotSpecified);
202 
203   // We're not allowed to call Wait() if data is already available.
204   if (reader_->Available()) {
205     render_task_runner_->PostTask(
206         FROM_HERE,
207         base::BindOnce(&MultibufferDataSource::StartCallback, weak_ptr_));
208 
209     // When the entire file is already in the cache, we won't get any more
210     // progress callbacks, which breaks some expectations. Post a task to
211     // make sure that the client gets at least one call each for the progress
212     // and loading callbacks.
213     render_task_runner_->PostTask(
214         FROM_HERE, base::BindOnce(&MultibufferDataSource::UpdateProgress,
215                                   weak_factory_.GetWeakPtr()));
216   } else {
217     reader_->Wait(
218         1, base::BindOnce(&MultibufferDataSource::StartCallback, weak_ptr_));
219   }
220 }
221 
OnRedirect(const scoped_refptr<UrlData> & destination)222 void MultibufferDataSource::OnRedirect(
223     const scoped_refptr<UrlData>& destination) {
224   if (!destination) {
225     // A failure occured.
226     failed_ = true;
227     if (init_cb_) {
228       render_task_runner_->PostTask(
229           FROM_HERE,
230           base::BindOnce(&MultibufferDataSource::StartCallback, weak_ptr_));
231     } else {
232       base::AutoLock auto_lock(lock_);
233       StopInternal_Locked();
234     }
235     StopLoader();
236     return;
237   }
238   if (url_data_->url().GetOrigin() != destination->url().GetOrigin()) {
239     single_origin_ = false;
240   }
241   SetReader(nullptr);
242   url_data_ = std::move(destination);
243 
244   if (url_data_) {
245     url_data_->OnRedirect(
246         base::BindOnce(&MultibufferDataSource::OnRedirect, weak_ptr_));
247 
248     if (init_cb_) {
249       CreateResourceLoader(0, kPositionNotSpecified);
250       if (reader_->Available()) {
251         render_task_runner_->PostTask(
252             FROM_HERE,
253             base::BindOnce(&MultibufferDataSource::StartCallback, weak_ptr_));
254       } else {
255         reader_->Wait(1, base::BindOnce(&MultibufferDataSource::StartCallback,
256                                         weak_ptr_));
257       }
258     } else if (read_op_) {
259       CreateResourceLoader(read_op_->position(), kPositionNotSpecified);
260       if (reader_->Available()) {
261         render_task_runner_->PostTask(
262             FROM_HERE,
263             base::BindOnce(&MultibufferDataSource::ReadTask, weak_ptr_));
264       } else {
265         reader_->Wait(
266             1, base::BindOnce(&MultibufferDataSource::ReadTask, weak_ptr_));
267       }
268     }
269   }
270 }
271 
SetPreload(Preload preload)272 void MultibufferDataSource::SetPreload(Preload preload) {
273   DVLOG(1) << __func__ << "(" << preload << ")";
274   DCHECK(render_task_runner_->BelongsToCurrentThread());
275   preload_ = preload;
276   UpdateBufferSizes();
277 }
278 
HasSingleOrigin()279 bool MultibufferDataSource::HasSingleOrigin() {
280   DCHECK(render_task_runner_->BelongsToCurrentThread());
281   // Before initialization completes there is no risk of leaking data. Callers
282   // are required to order checks such that this isn't a race.
283   return single_origin_;
284 }
285 
IsCorsCrossOrigin() const286 bool MultibufferDataSource::IsCorsCrossOrigin() const {
287   return url_data_->is_cors_cross_origin();
288 }
289 
HasAccessControl() const290 bool MultibufferDataSource::HasAccessControl() const {
291   return url_data_->has_access_control();
292 }
293 
cors_mode() const294 UrlData::CorsMode MultibufferDataSource::cors_mode() const {
295   return url_data_->cors_mode();
296 }
297 
MediaPlaybackRateChanged(double playback_rate)298 void MultibufferDataSource::MediaPlaybackRateChanged(double playback_rate) {
299   DCHECK(render_task_runner_->BelongsToCurrentThread());
300   if (playback_rate < 0 || playback_rate == playback_rate_)
301     return;
302 
303   playback_rate_ = playback_rate;
304   cancel_on_defer_ = false;
305   UpdateBufferSizes();
306 }
307 
MediaIsPlaying()308 void MultibufferDataSource::MediaIsPlaying() {
309   DCHECK(render_task_runner_->BelongsToCurrentThread());
310 
311   // Always clear this since it can be set by OnBufferingHaveEnough() calls at
312   // any point in time.
313   cancel_on_defer_ = false;
314 
315   if (media_has_played_)
316     return;
317 
318   media_has_played_ = true;
319 
320   // Once we start playing, we need preloading.
321   preload_ = AUTO;
322   UpdateBufferSizes();
323 }
324 
325 /////////////////////////////////////////////////////////////////////////////
326 // DataSource implementation.
Stop()327 void MultibufferDataSource::Stop() {
328   {
329     base::AutoLock auto_lock(lock_);
330     StopInternal_Locked();
331 
332     // Cleanup resources immediately if we're already on the right thread.
333     if (render_task_runner_->BelongsToCurrentThread()) {
334       reader_.reset();
335       url_data_.reset();
336       return;
337     }
338   }
339 
340   render_task_runner_->PostTask(
341       FROM_HERE, base::BindOnce(&MultibufferDataSource::StopLoader,
342                                 weak_factory_.GetWeakPtr()));
343 }
344 
Abort()345 void MultibufferDataSource::Abort() {
346   base::AutoLock auto_lock(lock_);
347   DCHECK(!init_cb_);
348   if (read_op_)
349     ReadOperation::Run(std::move(read_op_), kAborted);
350 
351   // Abort does not call StopLoader() since it is typically called prior to a
352   // seek or suspend. Let the loader logic make the decision about whether a new
353   // loader is necessary upon the seek or resume.
354 }
355 
SetBitrate(int bitrate)356 void MultibufferDataSource::SetBitrate(int bitrate) {
357   render_task_runner_->PostTask(
358       FROM_HERE, base::BindOnce(&MultibufferDataSource::SetBitrateTask,
359                                 weak_factory_.GetWeakPtr(), bitrate));
360 }
361 
OnBufferingHaveEnough(bool always_cancel)362 void MultibufferDataSource::OnBufferingHaveEnough(bool always_cancel) {
363   DCHECK(render_task_runner_->BelongsToCurrentThread());
364   if (reader_ && (always_cancel || (preload_ == METADATA &&
365                                     !media_has_played_ && !IsStreaming()))) {
366     cancel_on_defer_ = true;
367     if (!loading_) {
368       base::AutoLock auto_lock(lock_);
369       if (read_op_) {
370         // We can't destroy the reader if a read operation is pending.
371         // UpdateLoadingState_Locked will take care of it after the
372         // operation is done.
373         return;
374       }
375       // Already locked, no need to use SetReader().
376       reader_.reset(nullptr);
377     }
378   }
379 }
380 
GetMemoryUsage()381 int64_t MultibufferDataSource::GetMemoryUsage() {
382   // TODO(hubbe): Make more accurate when url_data_ is shared.
383   return base::checked_cast<int64_t>(url_data_->CachedSize())
384          << url_data_->multibuffer()->block_size_shift();
385 }
386 
GetUrlAfterRedirects() const387 GURL MultibufferDataSource::GetUrlAfterRedirects() const {
388   return url_data_->url();
389 }
390 
Read(int64_t position,int size,uint8_t * data,DataSource::ReadCB read_cb)391 void MultibufferDataSource::Read(int64_t position,
392                                  int size,
393                                  uint8_t* data,
394                                  DataSource::ReadCB read_cb) {
395   DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
396   // Reading is not allowed until after initialization.
397   DCHECK(!init_cb_);
398   DCHECK(read_cb);
399 
400   {
401     base::AutoLock auto_lock(lock_);
402     DCHECK(!read_op_);
403 
404     if (stop_signal_received_) {
405       std::move(read_cb).Run(kReadError);
406       return;
407     }
408 
409     // Optimization: Try reading from the cache here to get back to
410     // muxing as soon as possible. This works because TryReadAt is
411     // thread-safe.
412     if (reader_) {
413       int bytes_read = reader_->TryReadAt(position, data, size);
414       if (bytes_read > 0) {
415         bytes_read_ += bytes_read;
416         seek_positions_.push_back(position + bytes_read);
417         if (seek_positions_.size() == 1) {
418           render_task_runner_->PostDelayedTask(
419               FROM_HERE,
420               base::BindOnce(&MultibufferDataSource::SeekTask,
421                              weak_factory_.GetWeakPtr()),
422               kSeekDelay);
423         }
424 
425         std::move(read_cb).Run(bytes_read);
426         return;
427       }
428     }
429     read_op_ = std::make_unique<ReadOperation>(position, size, data,
430                                                std::move(read_cb));
431   }
432 
433   render_task_runner_->PostTask(FROM_HERE,
434                                 base::BindOnce(&MultibufferDataSource::ReadTask,
435                                                weak_factory_.GetWeakPtr()));
436 }
437 
GetSize(int64_t * size_out)438 bool MultibufferDataSource::GetSize(int64_t* size_out) {
439   base::AutoLock auto_lock(lock_);
440   if (total_bytes_ != kPositionNotSpecified) {
441     *size_out = total_bytes_;
442     return true;
443   }
444   *size_out = 0;
445   return false;
446 }
447 
IsStreaming()448 bool MultibufferDataSource::IsStreaming() {
449   return streaming_;
450 }
451 
452 /////////////////////////////////////////////////////////////////////////////
453 // This method is the place where actual read happens,
ReadTask()454 void MultibufferDataSource::ReadTask() {
455   DCHECK(render_task_runner_->BelongsToCurrentThread());
456 
457   base::AutoLock auto_lock(lock_);
458   int bytes_read = 0;
459   if (stop_signal_received_ || !read_op_)
460     return;
461   DCHECK(read_op_->size());
462 
463   if (!reader_)
464     CreateResourceLoader_Locked(read_op_->position(), kPositionNotSpecified);
465 
466   int64_t available = reader_->AvailableAt(read_op_->position());
467   if (available < 0) {
468     // A failure has occured.
469     ReadOperation::Run(std::move(read_op_), kReadError);
470     return;
471   }
472   if (available) {
473     bytes_read =
474         static_cast<int>(std::min<int64_t>(available, read_op_->size()));
475     bytes_read =
476         reader_->TryReadAt(read_op_->position(), read_op_->data(), bytes_read);
477 
478     bytes_read_ += bytes_read;
479     seek_positions_.push_back(read_op_->position() + bytes_read);
480 
481     if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) {
482       // We've reached the end of the file and we didn't know the total size
483       // before. Update the total size so Read()s past the end of the file will
484       // fail like they would if we had known the file size at the beginning.
485       total_bytes_ = read_op_->position() + bytes_read;
486       if (total_bytes_ != kPositionNotSpecified)
487         host_->SetTotalBytes(total_bytes_);
488     }
489 
490     ReadOperation::Run(std::move(read_op_), bytes_read);
491 
492     SeekTask_Locked();
493   } else {
494     reader_->Seek(read_op_->position());
495     reader_->Wait(1, base::BindOnce(&MultibufferDataSource::ReadTask,
496                                     weak_factory_.GetWeakPtr()));
497     UpdateLoadingState_Locked(false);
498   }
499 }
500 
SeekTask()501 void MultibufferDataSource::SeekTask() {
502   DCHECK(render_task_runner_->BelongsToCurrentThread());
503   base::AutoLock auto_lock(lock_);
504   SeekTask_Locked();
505 }
506 
SeekTask_Locked()507 void MultibufferDataSource::SeekTask_Locked() {
508   DCHECK(render_task_runner_->BelongsToCurrentThread());
509   lock_.AssertAcquired();
510 
511   if (stop_signal_received_)
512     return;
513 
514   // A read operation is pending, which will call SeekTask_Locked when
515   // it's done. We'll defer any seeking until the read op is done.
516   if (read_op_)
517     return;
518 
519   url_data_->AddBytesRead(bytes_read_);
520   bytes_read_ = 0;
521 
522   if (reader_) {
523     // If we're seeking to a new location, (not just slightly further
524     // in the file) and we have more data buffered in that new location
525     // than in our current location, then we don't actually seek anywhere.
526     // Instead we keep preloading at the old location a while longer.
527 
528     int64_t pos = reader_->Tell();
529     int64_t available = reader_->Available();
530 
531     // Iterate backwards, because if two positions have the same
532     // amount of buffered data, we probably want to prefer the latest
533     // one in the array.
534     for (auto i = seek_positions_.rbegin(); i != seek_positions_.rend(); ++i) {
535       int64_t new_pos = *i;
536       int64_t available_at_new_pos = reader_->AvailableAt(new_pos);
537 
538       if (total_bytes_ != kPositionNotSpecified) {
539         if (new_pos + available_at_new_pos >= total_bytes_) {
540           // Buffer reaches end of file, no need to seek here.
541           continue;
542         }
543       }
544       if (available_at_new_pos < available) {
545         pos = new_pos;
546         available = available_at_new_pos;
547       }
548     }
549     reader_->Seek(pos);
550   }
551   seek_positions_.clear();
552 
553   UpdateLoadingState_Locked(false);
554 }
555 
StopInternal_Locked()556 void MultibufferDataSource::StopInternal_Locked() {
557   lock_.AssertAcquired();
558   if (stop_signal_received_)
559     return;
560 
561   stop_signal_received_ = true;
562 
563   // Initialize() isn't part of the DataSource interface so don't call it in
564   // response to Stop().
565   init_cb_.Reset();
566 
567   if (read_op_)
568     ReadOperation::Run(std::move(read_op_), kReadError);
569 }
570 
StopLoader()571 void MultibufferDataSource::StopLoader() {
572   DCHECK(render_task_runner_->BelongsToCurrentThread());
573   SetReader(nullptr);
574 }
575 
SetBitrateTask(int bitrate)576 void MultibufferDataSource::SetBitrateTask(int bitrate) {
577   DCHECK(render_task_runner_->BelongsToCurrentThread());
578 
579   bitrate_ = bitrate;
580   UpdateBufferSizes();
581 }
582 
583 /////////////////////////////////////////////////////////////////////////////
584 // BufferedResourceLoader callback methods.
StartCallback()585 void MultibufferDataSource::StartCallback() {
586   DCHECK(render_task_runner_->BelongsToCurrentThread());
587 
588   if (!init_cb_) {
589     SetReader(nullptr);
590     return;
591   }
592 
593   // All responses must be successful. Resources that are assumed to be fully
594   // buffered must have a known content length.
595   bool success =
596       reader_ && reader_->Available() > 0 && url_data_ &&
597       (!AssumeFullyBuffered() || url_data_->length() != kPositionNotSpecified);
598 
599   if (success) {
600     {
601       base::AutoLock auto_lock(lock_);
602       total_bytes_ = url_data_->length();
603     }
604     streaming_ =
605         !AssumeFullyBuffered() && (total_bytes_ == kPositionNotSpecified ||
606                                    !url_data_->range_supported());
607 
608     media_log_->SetProperty<MediaLogProperty::kTotalBytes>(total_bytes_);
609     media_log_->SetProperty<MediaLogProperty::kIsStreaming>(streaming_);
610   } else {
611     SetReader(nullptr);
612   }
613 
614   // TODO(scherkus): we shouldn't have to lock to signal host(), see
615   // http://crbug.com/113712 for details.
616   base::AutoLock auto_lock(lock_);
617   if (stop_signal_received_)
618     return;
619 
620   if (success) {
621     if (total_bytes_ != kPositionNotSpecified) {
622       host_->SetTotalBytes(total_bytes_);
623       if (AssumeFullyBuffered())
624         host_->AddBufferedByteRange(0, total_bytes_);
625     }
626 
627     // Progress callback might be called after the start callback,
628     // make sure that we update single_origin_ now.
629     media_log_->SetProperty<MediaLogProperty::kIsSingleOrigin>(single_origin_);
630     media_log_->SetProperty<MediaLogProperty::kIsRangeHeaderSupported>(
631         url_data_->range_supported());
632   }
633 
634   render_task_runner_->PostTask(FROM_HERE,
635                                 base::BindOnce(std::move(init_cb_), success));
636 
637   UpdateBufferSizes();
638 
639   // Even if data is cached, say that we're loading at this point for
640   // compatibility.
641   UpdateLoadingState_Locked(true);
642 }
643 
ProgressCallback(int64_t begin,int64_t end)644 void MultibufferDataSource::ProgressCallback(int64_t begin, int64_t end) {
645   DVLOG(1) << __func__ << "(" << begin << ", " << end << ")";
646   DCHECK(render_task_runner_->BelongsToCurrentThread());
647 
648   base::AutoLock auto_lock(lock_);
649   if (stop_signal_received_)
650     return;
651 
652   if (AssumeFullyBuffered())
653     return;
654 
655   if (end > begin)
656     host_->AddBufferedByteRange(begin, end);
657 
658   if (buffer_size_update_counter_ > 0)
659     buffer_size_update_counter_--;
660   else
661     UpdateBufferSizes();
662 
663   UpdateLoadingState_Locked(false);
664 }
665 
UpdateLoadingState_Locked(bool force_loading)666 void MultibufferDataSource::UpdateLoadingState_Locked(bool force_loading) {
667   DVLOG(1) << __func__;
668   lock_.AssertAcquired();
669   if (AssumeFullyBuffered())
670     return;
671   // Update loading state.
672   bool is_loading = !!reader_ && reader_->IsLoading();
673   if (force_loading || is_loading != loading_) {
674     bool loading = is_loading || force_loading;
675 
676     if (!loading && cancel_on_defer_) {
677       if (read_op_) {
678         // We can't destroy the reader if a read operation is pending.
679         // UpdateLoadingState_Locked will be called again when the read
680         // operation is done.
681         return;
682       }
683       // Already locked, no need to use SetReader().
684       reader_.reset(nullptr);
685     }
686 
687     loading_ = loading;
688     downloading_cb_.Run(loading_);
689   }
690 }
691 
UpdateProgress()692 void MultibufferDataSource::UpdateProgress() {
693   DCHECK(render_task_runner_->BelongsToCurrentThread());
694   if (reader_) {
695     uint64_t available = reader_->Available();
696     uint64_t pos = reader_->Tell();
697     ProgressCallback(pos, pos + available);
698   }
699 }
700 
UpdateBufferSizes()701 void MultibufferDataSource::UpdateBufferSizes() {
702   DVLOG(1) << __func__;
703   if (!reader_)
704     return;
705 
706   buffer_size_update_counter_ = kUpdateBufferSizeFrequency;
707 
708   // Use a default bit rate if unknown and clamp to prevent overflow.
709   int64_t bitrate = base::ClampToRange<int64_t>(bitrate_, 0, kMaxBitrate);
710   if (bitrate == 0)
711     bitrate = kDefaultBitrate;
712 
713   // Only scale the buffer window for playback rates greater than 1.0 in
714   // magnitude and clamp to prevent overflow.
715   double playback_rate = playback_rate_;
716 
717   playback_rate = std::max(playback_rate, 1.0);
718   playback_rate = std::min(playback_rate, kMaxPlaybackRate);
719 
720   int64_t bytes_per_second = (bitrate / 8.0) * playback_rate;
721 
722   // Preload 10 seconds of data, clamped to some min/max value.
723   int64_t preload =
724       base::ClampToRange(kTargetSecondsBufferedAhead * bytes_per_second,
725                          kMinBufferPreload, kMaxBufferPreload);
726 
727   // Increase buffering slowly at a rate of 10% of data downloaded so
728   // far, maxing out at the preload size.
729   int64_t extra_buffer = std::min(
730       preload, url_data_->BytesReadFromCache() * kSlowPreloadPercentage / 100);
731 
732   // Add extra buffer to preload.
733   preload += extra_buffer;
734 
735   // We preload this much, then we stop unil we read |preload| before resuming.
736   int64_t preload_high = preload + kPreloadHighExtra;
737 
738   // We pin a few seconds of data behind the current reading position.
739   int64_t pin_backward =
740       base::ClampToRange(kTargetSecondsBufferedBehind * bytes_per_second,
741                          kMinBufferPreload, kMaxBufferPreload);
742 
743   // We always pin at least kDefaultPinSize ahead of the read position.
744   // Normally, the extra space between preload_high and kDefaultPinSize will
745   // not actually have any data in it, but if it does, we don't want to throw it
746   // away right before we need it.
747   int64_t pin_forward = std::max(preload_high, kDefaultPinSize);
748 
749   // Note that the buffer size is advisory as only non-pinned data is allowed
750   // to be thrown away. Most of the time we pin a region that is larger than
751   // |buffer_size|, which only makes sense because most of the time, some of
752   // the data in pinned region is not present in the cache.
753   int64_t buffer_size =
754       std::min((kTargetSecondsBufferedAhead + kTargetSecondsBufferedBehind) *
755                        bytes_per_second +
756                    extra_buffer * 3,
757                preload_high + pin_backward + extra_buffer);
758 
759   if (url_data_->FullyCached() ||
760       (url_data_->length() != kPositionNotSpecified &&
761        url_data_->length() < kDefaultPinSize)) {
762     // We just make pin_forwards/backwards big enough to encompass the
763     // whole file regardless of where we are, with some extra margins.
764     pin_forward = std::max(pin_forward, url_data_->length() * 2);
765     pin_backward = std::max(pin_backward, url_data_->length() * 2);
766     buffer_size = url_data_->length();
767   }
768 
769   reader_->SetMaxBuffer(buffer_size);
770   reader_->SetPinRange(pin_backward, pin_forward);
771 
772   if (preload_ == METADATA) {
773     preload_high >>= kMetadataShift;
774     preload >>= kMetadataShift;
775   }
776   reader_->SetPreload(preload_high, preload);
777 }
778 
779 }  // namespace media
780