Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Side by Side Diff: media/blink/buffered_data_source.cc

Issue 2272163002: Delete now deprecated BufferedDataSource and friends. (Closed)
Patch Set: Delete BufferedResourceLoader too. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/blink/buffered_data_source.h ('k') | media/blink/buffered_data_source_host_impl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « media/blink/buffered_data_source.h ('k') | media/blink/buffered_data_source_host_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698