OLD | NEW |
| (Empty) |
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/buffered_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/single_thread_task_runner.h" | |
14 #include "build/build_config.h" | |
15 #include "media/base/media_log.h" | |
16 #include "net/base/net_errors.h" | |
17 | |
18 using blink::WebFrame; | |
19 | |
20 namespace { | |
21 | |
22 // BufferedDataSource has an intermediate buffer, this value governs the initial | |
23 // size of that buffer. It is set to 32KB because this is a typical read size | |
24 // of FFmpeg. | |
25 const int kInitialReadBufferSize = 32768; | |
26 | |
27 // The number of milliseconds to wait before retrying a failed load. | |
28 const int kLoaderFailedRetryDelayMs = 250; | |
29 | |
30 // Each retry, add this many MS to the delay. | |
31 // total delay is: | |
32 // (kLoaderPartialRetryDelayMs + | |
33 // kAdditionalDelayPerRetryMs * (kMaxRetries - 1) / 2) * kLoaderRetries | |
34 // = 29250 ms | |
35 const int kAdditionalDelayPerRetryMs = 50; | |
36 | |
37 } // namespace | |
38 | |
39 namespace media { | |
40 | |
41 class BufferedDataSource::ReadOperation { | |
42 public: | |
43 ReadOperation(int64_t position, | |
44 int size, | |
45 uint8_t* data, | |
46 const DataSource::ReadCB& callback); | |
47 ~ReadOperation(); | |
48 | |
49 // Runs |callback_| with the given |result|, deleting the operation | |
50 // afterwards. | |
51 static void Run(std::unique_ptr<ReadOperation> read_op, int result); | |
52 | |
53 // State for the number of times this read operation has been retried. | |
54 int retries() { return retries_; } | |
55 void IncrementRetries() { ++retries_; } | |
56 | |
57 int64_t position() { return position_; } | |
58 int size() { return size_; } | |
59 uint8_t* data() { return data_; } | |
60 | |
61 private: | |
62 int retries_; | |
63 | |
64 const int64_t position_; | |
65 const int size_; | |
66 uint8_t* data_; | |
67 DataSource::ReadCB callback_; | |
68 | |
69 DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation); | |
70 }; | |
71 | |
72 BufferedDataSource::ReadOperation::ReadOperation( | |
73 int64_t position, | |
74 int size, | |
75 uint8_t* data, | |
76 const DataSource::ReadCB& callback) | |
77 : retries_(0), | |
78 position_(position), | |
79 size_(size), | |
80 data_(data), | |
81 callback_(callback) { | |
82 DCHECK(!callback_.is_null()); | |
83 } | |
84 | |
85 BufferedDataSource::ReadOperation::~ReadOperation() { | |
86 DCHECK(callback_.is_null()); | |
87 } | |
88 | |
89 // static | |
90 void BufferedDataSource::ReadOperation::Run( | |
91 std::unique_ptr<ReadOperation> read_op, | |
92 int result) { | |
93 base::ResetAndReturn(&read_op->callback_).Run(result); | |
94 } | |
95 | |
96 BufferedDataSource::BufferedDataSource( | |
97 const GURL& url, | |
98 BufferedResourceLoader::CORSMode cors_mode, | |
99 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | |
100 WebFrame* frame, | |
101 MediaLog* media_log, | |
102 BufferedDataSourceHost* host, | |
103 const DownloadingCB& downloading_cb) | |
104 : url_(url), | |
105 cors_mode_(cors_mode), | |
106 total_bytes_(kPositionNotSpecified), | |
107 streaming_(false), | |
108 frame_(frame), | |
109 intermediate_read_buffer_(kInitialReadBufferSize), | |
110 render_task_runner_(task_runner), | |
111 stop_signal_received_(false), | |
112 media_has_played_(false), | |
113 buffering_strategy_(BUFFERING_STRATEGY_NORMAL), | |
114 preload_(AUTO), | |
115 bitrate_(0), | |
116 playback_rate_(0.0), | |
117 media_log_(media_log), | |
118 host_(host), | |
119 downloading_cb_(downloading_cb), | |
120 weak_factory_(this) { | |
121 weak_ptr_ = weak_factory_.GetWeakPtr(); | |
122 DCHECK(host_); | |
123 DCHECK(!downloading_cb_.is_null()); | |
124 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
125 } | |
126 | |
127 BufferedDataSource::~BufferedDataSource() { | |
128 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
129 } | |
130 | |
131 bool BufferedDataSource::media_has_played() const { | |
132 return media_has_played_; | |
133 } | |
134 | |
135 bool BufferedDataSource::assume_fully_buffered() { | |
136 return !url_.SchemeIsHTTPOrHTTPS(); | |
137 } | |
138 | |
139 // A factory method to create BufferedResourceLoader using the read parameters. | |
140 // This method can be overridden to inject mock BufferedResourceLoader object | |
141 // for testing purpose. | |
142 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader( | |
143 int64_t first_byte_position, | |
144 int64_t last_byte_position) { | |
145 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
146 | |
147 BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ? | |
148 BufferedResourceLoader::kReadThenDefer : | |
149 BufferedResourceLoader::kCapacityDefer; | |
150 | |
151 return new BufferedResourceLoader(url_, | |
152 cors_mode_, | |
153 first_byte_position, | |
154 last_byte_position, | |
155 strategy, | |
156 bitrate_, | |
157 playback_rate_, | |
158 media_log_.get()); | |
159 } | |
160 | |
161 void BufferedDataSource::Initialize(const InitializeCB& init_cb) { | |
162 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
163 DCHECK(!init_cb.is_null()); | |
164 DCHECK(!loader_.get()); | |
165 | |
166 init_cb_ = init_cb; | |
167 | |
168 if (url_.SchemeIsHTTPOrHTTPS()) { | |
169 // Do an unbounded range request starting at the beginning. If the server | |
170 // responds with 200 instead of 206 we'll fall back into a streaming mode. | |
171 loader_.reset(CreateResourceLoader(0, kPositionNotSpecified)); | |
172 } else { | |
173 // For all other protocols, assume they support range request. We fetch | |
174 // the full range of the resource to obtain the instance size because | |
175 // we won't be served HTTP headers. | |
176 loader_.reset(CreateResourceLoader(kPositionNotSpecified, | |
177 kPositionNotSpecified)); | |
178 } | |
179 | |
180 base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr(); | |
181 loader_->Start( | |
182 base::Bind(&BufferedDataSource::StartCallback, weak_this), | |
183 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this), | |
184 base::Bind(&BufferedDataSource::ProgressCallback, weak_this), | |
185 frame_); | |
186 } | |
187 | |
188 void BufferedDataSource::SetPreload(Preload preload) { | |
189 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
190 preload_ = preload; | |
191 } | |
192 | |
193 void BufferedDataSource::SetBufferingStrategy( | |
194 BufferingStrategy buffering_strategy) { | |
195 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
196 buffering_strategy_ = buffering_strategy; | |
197 UpdateDeferStrategy(); | |
198 } | |
199 | |
200 bool BufferedDataSource::HasSingleOrigin() { | |
201 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
202 DCHECK(init_cb_.is_null() && loader_.get()) | |
203 << "Initialize() must complete before calling HasSingleOrigin()"; | |
204 return loader_->HasSingleOrigin(); | |
205 } | |
206 | |
207 bool BufferedDataSource::DidPassCORSAccessCheck() const { | |
208 return loader_.get() && loader_->DidPassCORSAccessCheck(); | |
209 } | |
210 | |
211 void BufferedDataSource::Abort() { | |
212 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
213 { | |
214 base::AutoLock auto_lock(lock_); | |
215 StopInternal_Locked(); | |
216 } | |
217 StopLoader(); | |
218 frame_ = NULL; | |
219 } | |
220 | |
221 void BufferedDataSource::MediaPlaybackRateChanged(double playback_rate) { | |
222 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
223 DCHECK(loader_.get()); | |
224 | |
225 if (playback_rate < 0.0) | |
226 return; | |
227 | |
228 playback_rate_ = playback_rate; | |
229 loader_->SetPlaybackRate(playback_rate); | |
230 } | |
231 | |
232 void BufferedDataSource::MediaIsPlaying() { | |
233 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
234 media_has_played_ = true; | |
235 UpdateDeferStrategy(); | |
236 } | |
237 | |
238 ///////////////////////////////////////////////////////////////////////////// | |
239 // DataSource implementation. | |
240 void BufferedDataSource::Stop() { | |
241 { | |
242 base::AutoLock auto_lock(lock_); | |
243 StopInternal_Locked(); | |
244 } | |
245 | |
246 render_task_runner_->PostTask( | |
247 FROM_HERE, | |
248 base::Bind(&BufferedDataSource::StopLoader, weak_factory_.GetWeakPtr())); | |
249 } | |
250 | |
251 void BufferedDataSource::SetBitrate(int bitrate) { | |
252 render_task_runner_->PostTask(FROM_HERE, | |
253 base::Bind(&BufferedDataSource::SetBitrateTask, | |
254 weak_factory_.GetWeakPtr(), | |
255 bitrate)); | |
256 } | |
257 | |
258 void BufferedDataSource::OnBufferingHaveEnough(bool always_cancel) { | |
259 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
260 if (loader_ && (always_cancel || (preload_ == METADATA && | |
261 !media_has_played_ && !IsStreaming()))) { | |
262 loader_->CancelUponDeferral(); | |
263 } | |
264 } | |
265 | |
266 int64_t BufferedDataSource::GetMemoryUsage() const { | |
267 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
268 return loader_ ? loader_->GetMemoryUsage() : 0; | |
269 } | |
270 | |
271 GURL BufferedDataSource::GetUrlAfterRedirects() const { | |
272 return response_original_url_; | |
273 } | |
274 | |
275 void BufferedDataSource::Read(int64_t position, | |
276 int size, | |
277 uint8_t* data, | |
278 const DataSource::ReadCB& read_cb) { | |
279 DVLOG(1) << "Read: " << position << " offset, " << size << " bytes"; | |
280 DCHECK(!read_cb.is_null()); | |
281 | |
282 { | |
283 base::AutoLock auto_lock(lock_); | |
284 DCHECK(!read_op_); | |
285 | |
286 if (stop_signal_received_) { | |
287 read_cb.Run(kReadError); | |
288 return; | |
289 } | |
290 | |
291 read_op_.reset(new ReadOperation(position, size, data, read_cb)); | |
292 } | |
293 | |
294 render_task_runner_->PostTask( | |
295 FROM_HERE, | |
296 base::Bind(&BufferedDataSource::ReadTask, weak_factory_.GetWeakPtr())); | |
297 } | |
298 | |
299 bool BufferedDataSource::GetSize(int64_t* size_out) { | |
300 if (total_bytes_ != kPositionNotSpecified) { | |
301 *size_out = total_bytes_; | |
302 return true; | |
303 } | |
304 *size_out = 0; | |
305 return false; | |
306 } | |
307 | |
308 bool BufferedDataSource::IsStreaming() { | |
309 return streaming_; | |
310 } | |
311 | |
312 ///////////////////////////////////////////////////////////////////////////// | |
313 // Render thread tasks. | |
314 void BufferedDataSource::ReadTask() { | |
315 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
316 ReadInternal(); | |
317 } | |
318 | |
319 void BufferedDataSource::StopInternal_Locked() { | |
320 lock_.AssertAcquired(); | |
321 if (stop_signal_received_) | |
322 return; | |
323 | |
324 stop_signal_received_ = true; | |
325 | |
326 // Initialize() isn't part of the DataSource interface so don't call it in | |
327 // response to Stop(). | |
328 init_cb_.Reset(); | |
329 | |
330 if (read_op_) | |
331 ReadOperation::Run(std::move(read_op_), kReadError); | |
332 } | |
333 | |
334 void BufferedDataSource::StopLoader() { | |
335 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
336 | |
337 if (loader_) | |
338 loader_->Stop(); | |
339 } | |
340 | |
341 void BufferedDataSource::SetBitrateTask(int bitrate) { | |
342 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
343 DCHECK(loader_.get()); | |
344 | |
345 bitrate_ = bitrate; | |
346 loader_->SetBitrate(bitrate); | |
347 } | |
348 | |
349 // This method is the place where actual read happens, |loader_| must be valid | |
350 // prior to make this method call. | |
351 void BufferedDataSource::ReadInternal() { | |
352 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
353 int64_t position = 0; | |
354 int size = 0; | |
355 { | |
356 base::AutoLock auto_lock(lock_); | |
357 if (stop_signal_received_) | |
358 return; | |
359 | |
360 position = read_op_->position(); | |
361 size = read_op_->size(); | |
362 } | |
363 | |
364 // First we prepare the intermediate read buffer for BufferedResourceLoader | |
365 // to write to. | |
366 if (static_cast<int>(intermediate_read_buffer_.size()) < size) | |
367 intermediate_read_buffer_.resize(size); | |
368 | |
369 // Perform the actual read with BufferedResourceLoader. | |
370 DCHECK(!intermediate_read_buffer_.empty()); | |
371 loader_->Read(position, | |
372 size, | |
373 &intermediate_read_buffer_[0], | |
374 base::Bind(&BufferedDataSource::ReadCallback, | |
375 weak_factory_.GetWeakPtr())); | |
376 } | |
377 | |
378 | |
379 ///////////////////////////////////////////////////////////////////////////// | |
380 // BufferedResourceLoader callback methods. | |
381 void BufferedDataSource::StartCallback( | |
382 BufferedResourceLoader::Status status) { | |
383 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
384 DCHECK(loader_.get()); | |
385 | |
386 bool init_cb_is_null = false; | |
387 { | |
388 base::AutoLock auto_lock(lock_); | |
389 init_cb_is_null = init_cb_.is_null(); | |
390 } | |
391 if (init_cb_is_null) { | |
392 loader_->Stop(); | |
393 return; | |
394 } | |
395 | |
396 response_original_url_ = loader_->response_original_url(); | |
397 #if defined(OS_ANDROID) | |
398 // The response original url is the URL of this resource after following | |
399 // redirects. Update |url_| to this so that we only follow redirects once. | |
400 // We do this on Android only to preserve the behavior we had before the | |
401 // unified media pipeline. This behavior will soon exist on all platforms | |
402 // as we switch to MultiBufferDataSource (http://crbug.com/514719). | |
403 // If the response URL is empty (which happens when it's from a Service | |
404 // Worker), keep the original one. | |
405 if (!response_original_url_.is_empty()) | |
406 url_ = response_original_url_; | |
407 #endif // defined(OS_ANDROID) | |
408 | |
409 // All responses must be successful. Resources that are assumed to be fully | |
410 // buffered must have a known content length. | |
411 bool success = status == BufferedResourceLoader::kOk && | |
412 (!assume_fully_buffered() || | |
413 loader_->instance_size() != kPositionNotSpecified); | |
414 | |
415 if (success) { | |
416 total_bytes_ = loader_->instance_size(); | |
417 streaming_ = | |
418 !assume_fully_buffered() && | |
419 (total_bytes_ == kPositionNotSpecified || !loader_->range_supported()); | |
420 | |
421 media_log_->SetDoubleProperty("total_bytes", | |
422 static_cast<double>(total_bytes_)); | |
423 media_log_->SetBooleanProperty("streaming", streaming_); | |
424 } else { | |
425 loader_->Stop(); | |
426 } | |
427 | |
428 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
429 // http://crbug.com/113712 for details. | |
430 base::AutoLock auto_lock(lock_); | |
431 if (stop_signal_received_) | |
432 return; | |
433 | |
434 if (success) { | |
435 if (total_bytes_ != kPositionNotSpecified) { | |
436 host_->SetTotalBytes(total_bytes_); | |
437 if (assume_fully_buffered()) | |
438 host_->AddBufferedByteRange(0, total_bytes_); | |
439 } | |
440 | |
441 media_log_->SetBooleanProperty("single_origin", loader_->HasSingleOrigin()); | |
442 media_log_->SetBooleanProperty("passed_cors_access_check", | |
443 loader_->DidPassCORSAccessCheck()); | |
444 media_log_->SetBooleanProperty("range_header_supported", | |
445 loader_->range_supported()); | |
446 } | |
447 | |
448 render_task_runner_->PostTask( | |
449 FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success)); | |
450 } | |
451 | |
452 void BufferedDataSource::PartialReadStartCallback( | |
453 BufferedResourceLoader::Status status) { | |
454 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
455 DCHECK(loader_.get()); | |
456 if (status == BufferedResourceLoader::kOk && | |
457 CheckPartialResponseURL(loader_->response_original_url())) { | |
458 // Once the request has started successfully, we can proceed with | |
459 // reading from it. | |
460 ReadInternal(); | |
461 return; | |
462 } | |
463 | |
464 // Stop the resource loader since we have received an error. | |
465 loader_->Stop(); | |
466 | |
467 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
468 // http://crbug.com/113712 for details. | |
469 base::AutoLock auto_lock(lock_); | |
470 if (stop_signal_received_) | |
471 return; | |
472 ReadOperation::Run(std::move(read_op_), kReadError); | |
473 } | |
474 | |
475 bool BufferedDataSource::CheckPartialResponseURL( | |
476 const GURL& partial_response_original_url) const { | |
477 // We check the redirected URL of partial responses in case malicious | |
478 // attackers scan the bytes of other origin resources by mixing their | |
479 // generated bytes and the target response. See http://crbug.com/489060#c32 | |
480 // for details. | |
481 // If the origin of the new response is different from the first response we | |
482 // deny the redirected response unless the crossorigin attribute has been set. | |
483 if ((response_original_url_.GetOrigin() == | |
484 partial_response_original_url.GetOrigin()) || | |
485 DidPassCORSAccessCheck()) { | |
486 return true; | |
487 } | |
488 | |
489 MEDIA_LOG(ERROR, media_log_) << "BufferedDataSource: origin has changed"; | |
490 return false; | |
491 } | |
492 | |
493 void BufferedDataSource::ReadCallback( | |
494 BufferedResourceLoader::Status status, | |
495 int bytes_read) { | |
496 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
497 | |
498 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
499 // http://crbug.com/113712 for details. | |
500 base::AutoLock auto_lock(lock_); | |
501 if (stop_signal_received_) | |
502 return; | |
503 | |
504 if (status != BufferedResourceLoader::kOk) { | |
505 // Stop the resource load if it failed. | |
506 loader_->Stop(); | |
507 | |
508 if (read_op_->retries() < kLoaderRetries) { | |
509 // Allow some resiliency against sporadic network failures or intentional | |
510 // cancellations due to a system suspend / resume. Here we treat failed | |
511 // reads as a cache miss so long as we haven't exceeded max retries. | |
512 if (status == BufferedResourceLoader::kFailed) { | |
513 render_task_runner_->PostDelayedTask( | |
514 FROM_HERE, base::Bind(&BufferedDataSource::ReadCallback, | |
515 weak_factory_.GetWeakPtr(), | |
516 BufferedResourceLoader::kCacheMiss, 0), | |
517 base::TimeDelta::FromMilliseconds(kLoaderFailedRetryDelayMs + | |
518 read_op_->retries() * | |
519 kAdditionalDelayPerRetryMs)); | |
520 return; | |
521 } | |
522 | |
523 read_op_->IncrementRetries(); | |
524 | |
525 // Recreate a loader starting from where we last left off until the | |
526 // end of the resource. | |
527 loader_.reset(CreateResourceLoader( | |
528 read_op_->position(), kPositionNotSpecified)); | |
529 | |
530 base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr(); | |
531 loader_->Start( | |
532 base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this), | |
533 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, | |
534 weak_this), | |
535 base::Bind(&BufferedDataSource::ProgressCallback, weak_this), | |
536 frame_); | |
537 return; | |
538 } | |
539 | |
540 ReadOperation::Run(std::move(read_op_), kReadError); | |
541 return; | |
542 } | |
543 | |
544 if (bytes_read > 0) { | |
545 DCHECK(!intermediate_read_buffer_.empty()); | |
546 memcpy(read_op_->data(), &intermediate_read_buffer_[0], bytes_read); | |
547 } else if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) { | |
548 // We've reached the end of the file and we didn't know the total size | |
549 // before. Update the total size so Read()s past the end of the file will | |
550 // fail like they would if we had known the file size at the beginning. | |
551 total_bytes_ = loader_->instance_size(); | |
552 | |
553 if (total_bytes_ != kPositionNotSpecified) { | |
554 host_->SetTotalBytes(total_bytes_); | |
555 host_->AddBufferedByteRange(loader_->first_byte_position(), | |
556 total_bytes_); | |
557 } | |
558 } | |
559 ReadOperation::Run(std::move(read_op_), bytes_read); | |
560 } | |
561 | |
562 void BufferedDataSource::LoadingStateChangedCallback( | |
563 BufferedResourceLoader::LoadingState state) { | |
564 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
565 | |
566 if (assume_fully_buffered()) | |
567 return; | |
568 | |
569 bool is_downloading_data = false; | |
570 switch (state) { | |
571 case BufferedResourceLoader::kLoading: | |
572 is_downloading_data = true; | |
573 break; | |
574 case BufferedResourceLoader::kLoadingDeferred: | |
575 case BufferedResourceLoader::kLoadingFinished: | |
576 is_downloading_data = false; | |
577 break; | |
578 | |
579 // TODO(scherkus): we don't signal network activity changes when loads | |
580 // fail to preserve existing behaviour when deferring is toggled, however | |
581 // we should consider changing DownloadingCB to also propagate loading | |
582 // state. For example there isn't any signal today to notify the client that | |
583 // loading has failed (we only get errors on subsequent reads). | |
584 case BufferedResourceLoader::kLoadingFailed: | |
585 return; | |
586 } | |
587 | |
588 downloading_cb_.Run(is_downloading_data); | |
589 } | |
590 | |
591 void BufferedDataSource::ProgressCallback(int64_t position) { | |
592 DCHECK(render_task_runner_->BelongsToCurrentThread()); | |
593 | |
594 if (assume_fully_buffered()) | |
595 return; | |
596 | |
597 // TODO(scherkus): we shouldn't have to lock to signal host(), see | |
598 // http://crbug.com/113712 for details. | |
599 base::AutoLock auto_lock(lock_); | |
600 if (stop_signal_received_) | |
601 return; | |
602 | |
603 host_->AddBufferedByteRange(loader_->first_byte_position(), position); | |
604 } | |
605 | |
606 void BufferedDataSource::UpdateDeferStrategy() { | |
607 if (!loader_) | |
608 return; | |
609 | |
610 // No need to aggressively buffer when we are assuming the resource is fully | |
611 // buffered. | |
612 if (assume_fully_buffered()) { | |
613 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer); | |
614 return; | |
615 } | |
616 | |
617 // If the playback has started (at which point the preload value is ignored) | |
618 // and the strategy is aggressive, then try to load as much as possible (the | |
619 // loader will fall back to kCapacityDefer if it knows the current response | |
620 // won't be useful from the cache in the future). | |
621 bool aggressive = (buffering_strategy_ == BUFFERING_STRATEGY_AGGRESSIVE); | |
622 if (media_has_played_ && aggressive && loader_->range_supported()) { | |
623 loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer); | |
624 return; | |
625 } | |
626 | |
627 // If media is currently playing or the page indicated preload=auto or the | |
628 // the server does not support the byte range request or we do not want to go | |
629 // too far ahead of the read head, use threshold strategy to enable/disable | |
630 // deferring when the buffer is full/depleted. | |
631 loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer); | |
632 } | |
633 | |
634 } // namespace media | |
OLD | NEW |