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