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