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