OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/media/buffered_data_source.h" | 5 #include "webkit/media/buffered_data_source.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
9 #include "media/base/media_log.h" | 9 #include "media/base/media_log.h" |
10 #include "net/base/net_errors.h" | 10 #include "net/base/net_errors.h" |
11 | 11 |
12 using WebKit::WebFrame; | 12 using WebKit::WebFrame; |
13 | 13 |
14 namespace { | 14 namespace { |
15 | 15 |
16 // BufferedDataSource has an intermediate buffer, this value governs the initial | 16 // BufferedDataSource has an intermediate buffer, this value governs the initial |
17 // size of that buffer. It is set to 32KB because this is a typical read size | 17 // size of that buffer. It is set to 32KB because this is a typical read size |
18 // of FFmpeg. | 18 // of FFmpeg. |
19 const int kInitialReadBufferSize = 32768; | 19 const int kInitialReadBufferSize = 32768; |
20 | 20 |
21 // Number of cache misses we allow for a single Read() before signaling an | 21 // Number of cache misses we allow for a single Read() before signaling an |
22 // error. | 22 // error. |
23 const int kNumCacheMissRetries = 3; | 23 const int kNumCacheMissRetries = 3; |
24 | 24 |
25 } // namespace | 25 } // namespace |
26 | 26 |
27 namespace webkit_media { | 27 namespace webkit_media { |
28 | 28 |
29 // Non-HTTP resources are assumed to be fully loaded so we ignore any | |
30 // loading/progress related callbacks. | |
31 static void NonHttpLoadingStateChangedCallback( | |
32 BufferedResourceLoader::LoadingState) { | |
33 } | |
34 static void NonHttpProgressCallback(int64) {} | |
35 | |
36 BufferedDataSource::BufferedDataSource( | 29 BufferedDataSource::BufferedDataSource( |
37 MessageLoop* render_loop, | 30 MessageLoop* render_loop, |
38 WebFrame* frame, | 31 WebFrame* frame, |
39 media::MediaLog* media_log, | 32 media::MediaLog* media_log, |
40 const DownloadingCB& downloading_cb) | 33 const DownloadingCB& downloading_cb) |
41 : cors_mode_(BufferedResourceLoader::kUnspecified), | 34 : cors_mode_(BufferedResourceLoader::kUnspecified), |
42 total_bytes_(kPositionNotSpecified), | 35 total_bytes_(kPositionNotSpecified), |
43 assume_fully_buffered_(false), | 36 assume_fully_buffered_(false), |
44 streaming_(false), | 37 streaming_(false), |
45 frame_(frame), | 38 frame_(frame), |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 DCHECK(!loader_.get()); | 95 DCHECK(!loader_.get()); |
103 url_ = url; | 96 url_ = url; |
104 cors_mode_ = cors_mode; | 97 cors_mode_ = cors_mode; |
105 | 98 |
106 initialize_cb_ = initialize_cb; | 99 initialize_cb_ = initialize_cb; |
107 | 100 |
108 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) { | 101 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) { |
109 // Do an unbounded range request starting at the beginning. If the server | 102 // Do an unbounded range request starting at the beginning. If the server |
110 // responds with 200 instead of 206 we'll fall back into a streaming mode. | 103 // responds with 200 instead of 206 we'll fall back into a streaming mode. |
111 loader_.reset(CreateResourceLoader(0, kPositionNotSpecified)); | 104 loader_.reset(CreateResourceLoader(0, kPositionNotSpecified)); |
112 loader_->Start( | 105 } else { |
113 base::Bind(&BufferedDataSource::HttpInitialStartCallback, this), | 106 // For all other protocols, assume they support range request. We fetch |
114 base::Bind(&BufferedDataSource::HttpLoadingStateChangedCallback, this), | 107 // the full range of the resource to obtain the instance size because |
115 base::Bind(&BufferedDataSource::HttpProgressCallback, this), | 108 // we won't be served HTTP headers. |
116 frame_); | 109 loader_.reset(CreateResourceLoader(kPositionNotSpecified, |
117 return; | 110 kPositionNotSpecified)); |
| 111 assume_fully_buffered_ = true; |
118 } | 112 } |
119 | 113 |
120 // For all other protocols, assume they support range request. We fetch | |
121 // the full range of the resource to obtain the instance size because | |
122 // we won't be served HTTP headers. | |
123 loader_.reset(CreateResourceLoader(kPositionNotSpecified, | |
124 kPositionNotSpecified)); | |
125 loader_->Start( | 114 loader_->Start( |
126 base::Bind(&BufferedDataSource::NonHttpInitialStartCallback, this), | 115 base::Bind(&BufferedDataSource::StartCallback, this), |
127 base::Bind(&NonHttpLoadingStateChangedCallback), | 116 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, this), |
128 base::Bind(&NonHttpProgressCallback), | 117 base::Bind(&BufferedDataSource::ProgressCallback, this), |
129 frame_); | 118 frame_); |
130 } | 119 } |
131 | 120 |
132 void BufferedDataSource::SetPreload(Preload preload) { | 121 void BufferedDataSource::SetPreload(Preload preload) { |
133 DCHECK(MessageLoop::current() == render_loop_); | 122 DCHECK(MessageLoop::current() == render_loop_); |
134 preload_ = preload; | 123 preload_ = preload; |
135 } | 124 } |
136 | 125 |
137 bool BufferedDataSource::HasSingleOrigin() { | 126 bool BufferedDataSource::HasSingleOrigin() { |
138 DCHECK(MessageLoop::current() == render_loop_); | 127 DCHECK(MessageLoop::current() == render_loop_); |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
271 return; | 260 return; |
272 | 261 |
273 { | 262 { |
274 // If there's no outstanding read then return early. | 263 // If there's no outstanding read then return early. |
275 base::AutoLock auto_lock(lock_); | 264 base::AutoLock auto_lock(lock_); |
276 if (read_cb_.is_null()) | 265 if (read_cb_.is_null()) |
277 return; | 266 return; |
278 } | 267 } |
279 | 268 |
280 // Start reading from where we last left off until the end of the resource. | 269 // Start reading from where we last left off until the end of the resource. |
281 loader_.reset( | 270 loader_.reset(CreateResourceLoader(last_read_start_, kPositionNotSpecified)); |
282 CreateResourceLoader(last_read_start_, kPositionNotSpecified)); | 271 loader_->Start( |
283 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) { | 272 base::Bind(&BufferedDataSource::PartialReadStartCallback, this), |
284 loader_->Start( | 273 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, this), |
285 base::Bind(&BufferedDataSource::PartialReadStartCallback, this), | 274 base::Bind(&BufferedDataSource::ProgressCallback, this), |
286 base::Bind(&BufferedDataSource::HttpLoadingStateChangedCallback, this), | 275 frame_); |
287 base::Bind(&BufferedDataSource::HttpProgressCallback, this), | |
288 frame_); | |
289 } else { | |
290 loader_->Start( | |
291 base::Bind(&BufferedDataSource::PartialReadStartCallback, this), | |
292 base::Bind(&NonHttpLoadingStateChangedCallback), | |
293 base::Bind(&NonHttpProgressCallback), | |
294 frame_); | |
295 } | |
296 } | 276 } |
297 | 277 |
298 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) { | 278 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) { |
299 DCHECK(MessageLoop::current() == render_loop_); | 279 DCHECK(MessageLoop::current() == render_loop_); |
300 DCHECK(loader_.get()); | 280 DCHECK(loader_.get()); |
301 | 281 |
302 if (playback_rate != 0) | 282 if (playback_rate != 0) |
303 media_has_played_ = true; | 283 media_has_played_ = true; |
304 | 284 |
305 playback_rate_ = playback_rate; | 285 playback_rate_ = playback_rate; |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 DCHECK(MessageLoop::current() == render_loop_); | 348 DCHECK(MessageLoop::current() == render_loop_); |
369 DCHECK(!initialize_cb_.is_null()); | 349 DCHECK(!initialize_cb_.is_null()); |
370 lock_.AssertAcquired(); | 350 lock_.AssertAcquired(); |
371 | 351 |
372 initialize_cb_.Run(status); | 352 initialize_cb_.Run(status); |
373 initialize_cb_.Reset(); | 353 initialize_cb_.Reset(); |
374 } | 354 } |
375 | 355 |
376 ///////////////////////////////////////////////////////////////////////////// | 356 ///////////////////////////////////////////////////////////////////////////// |
377 // BufferedResourceLoader callback methods. | 357 // BufferedResourceLoader callback methods. |
378 void BufferedDataSource::HttpInitialStartCallback( | 358 void BufferedDataSource::StartCallback( |
379 BufferedResourceLoader::Status status) { | 359 BufferedResourceLoader::Status status) { |
380 DCHECK(MessageLoop::current() == render_loop_); | 360 DCHECK(MessageLoop::current() == render_loop_); |
381 DCHECK(loader_.get()); | 361 DCHECK(loader_.get()); |
382 | 362 |
383 bool initialize_cb_is_null = false; | 363 bool initialize_cb_is_null = false; |
384 { | 364 { |
385 base::AutoLock auto_lock(lock_); | 365 base::AutoLock auto_lock(lock_); |
386 initialize_cb_is_null = initialize_cb_.is_null(); | 366 initialize_cb_is_null = initialize_cb_.is_null(); |
387 } | 367 } |
388 if (initialize_cb_is_null) { | 368 if (initialize_cb_is_null) { |
389 loader_->Stop(); | 369 loader_->Stop(); |
390 return; | 370 return; |
391 } | 371 } |
392 | 372 |
393 bool success = status == BufferedResourceLoader::kOk; | 373 // All responses must be successful. Resources that are assumed to be fully |
| 374 // buffered must have a known content length. |
| 375 bool success = status == BufferedResourceLoader::kOk && |
| 376 (!assume_fully_buffered_ || |
| 377 loader_->instance_size() != kPositionNotSpecified); |
| 378 |
394 if (success) { | 379 if (success) { |
395 // TODO(hclam): Needs more thinking about supporting servers without range | |
396 // request or their partial response is not complete. | |
397 total_bytes_ = loader_->instance_size(); | 380 total_bytes_ = loader_->instance_size(); |
398 streaming_ = (total_bytes_ == kPositionNotSpecified) || | 381 streaming_ = !assume_fully_buffered_ && |
399 !loader_->range_supported(); | 382 (total_bytes_ == kPositionNotSpecified || !loader_->range_supported()); |
400 } else { | 383 } else { |
401 // TODO(hclam): In case of failure, we can retry several times. | |
402 loader_->Stop(); | 384 loader_->Stop(); |
403 } | 385 } |
404 | 386 |
405 // Reference to prevent destruction while inside the |initialize_cb_| | 387 // TODO(scherkus): we shouldn't have to lock to signal host(), see |
406 // call. This is a temporary fix to prevent crashes caused by holding the | 388 // http://crbug.com/113712 for details. |
407 // lock and running the destructor. | |
408 // TODO: Review locking in this class and figure out a way to run the callback | |
409 // w/o the lock. | |
410 scoped_refptr<BufferedDataSource> destruction_guard(this); | 389 scoped_refptr<BufferedDataSource> destruction_guard(this); |
411 { | 390 { |
412 // We need to prevent calling to filter host and running the callback if | |
413 // we have received the stop signal. We need to lock down the whole callback | |
414 // method to prevent bad things from happening. The reason behind this is | |
415 // that we cannot guarantee tasks on render thread have completely stopped | |
416 // when we receive the Stop() method call. The only way to solve this is to | |
417 // let tasks on render thread to run but make sure they don't call outside | |
418 // this object when Stop() method is ever called. Locking this method is | |
419 // safe because |lock_| is only acquired in tasks on render thread. | |
420 base::AutoLock auto_lock(lock_); | 391 base::AutoLock auto_lock(lock_); |
421 if (stop_signal_received_) | 392 if (stop_signal_received_) |
422 return; | 393 return; |
423 | 394 |
424 if (!success) { | 395 if (!success) { |
425 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK); | |
426 return; | |
427 } | |
428 | |
429 UpdateHostState_Locked(); | |
430 DoneInitialization_Locked(media::PIPELINE_OK); | |
431 } | |
432 } | |
433 | |
434 void BufferedDataSource::NonHttpInitialStartCallback( | |
435 BufferedResourceLoader::Status status) { | |
436 DCHECK(MessageLoop::current() == render_loop_); | |
437 DCHECK(loader_.get()); | |
438 | |
439 bool initialize_cb_is_null = false; | |
440 { | |
441 base::AutoLock auto_lock(lock_); | |
442 initialize_cb_is_null = initialize_cb_.is_null(); | |
443 } | |
444 if (initialize_cb_is_null) { | |
445 loader_->Stop(); | |
446 return; | |
447 } | |
448 | |
449 int64 instance_size = loader_->instance_size(); | |
450 bool success = status == BufferedResourceLoader::kOk && | |
451 instance_size != kPositionNotSpecified; | |
452 | |
453 if (success) { | |
454 total_bytes_ = instance_size; | |
455 assume_fully_buffered_ = true; | |
456 } else { | |
457 loader_->Stop(); | |
458 } | |
459 | |
460 // Reference to prevent destruction while inside the |initialize_cb_| | |
461 // call. This is a temporary fix to prevent crashes caused by holding the | |
462 // lock and running the destructor. | |
463 // TODO: Review locking in this class and figure out a way to run the callback | |
464 // w/o the lock. | |
465 scoped_refptr<BufferedDataSource> destruction_guard(this); | |
466 { | |
467 // We need to prevent calling to filter host and running the callback if | |
468 // we have received the stop signal. We need to lock down the whole callback | |
469 // method to prevent bad things from happening. The reason behind this is | |
470 // that we cannot guarantee tasks on render thread have completely stopped | |
471 // when we receive the Stop() method call. The only way to solve this is to | |
472 // let tasks on render thread to run but make sure they don't call outside | |
473 // this object when Stop() method is ever called. Locking this method is | |
474 // safe because |lock_| is only acquired in tasks on render thread. | |
475 base::AutoLock auto_lock(lock_); | |
476 if (stop_signal_received_ || initialize_cb_.is_null()) | |
477 return; | |
478 | |
479 if (!success) { | |
480 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK); | 396 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK); |
481 return; | 397 return; |
482 } | 398 } |
483 | 399 |
484 UpdateHostState_Locked(); | 400 UpdateHostState_Locked(); |
485 DoneInitialization_Locked(media::PIPELINE_OK); | 401 DoneInitialization_Locked(media::PIPELINE_OK); |
486 } | 402 } |
487 } | 403 } |
488 | 404 |
489 void BufferedDataSource::PartialReadStartCallback( | 405 void BufferedDataSource::PartialReadStartCallback( |
490 BufferedResourceLoader::Status status) { | 406 BufferedResourceLoader::Status status) { |
491 DCHECK(MessageLoop::current() == render_loop_); | 407 DCHECK(MessageLoop::current() == render_loop_); |
492 DCHECK(loader_.get()); | 408 DCHECK(loader_.get()); |
493 | 409 |
494 if (status == BufferedResourceLoader::kOk) { | 410 if (status == BufferedResourceLoader::kOk) { |
495 // Once the request has started successfully, we can proceed with | 411 // Once the request has started successfully, we can proceed with |
496 // reading from it. | 412 // reading from it. |
497 ReadInternal(); | 413 ReadInternal(); |
498 return; | 414 return; |
499 } | 415 } |
500 | 416 |
501 // Stop the resource loader since we have received an error. | 417 // Stop the resource loader since we have received an error. |
502 loader_->Stop(); | 418 loader_->Stop(); |
503 | 419 |
504 // We need to prevent calling to filter host and running the callback if | 420 // TODO(scherkus): we shouldn't have to lock to signal host(), see |
505 // we have received the stop signal. We need to lock down the whole callback | 421 // http://crbug.com/113712 for details. |
506 // method to prevent bad things from happening. The reason behind this is | |
507 // that we cannot guarantee tasks on render thread have completely stopped | |
508 // when we receive the Stop() method call. So only way to solve this is to | |
509 // let tasks on render thread to run but make sure they don't call outside | |
510 // this object when Stop() method is ever called. Locking this method is | |
511 // safe because |lock_| is only acquired in tasks on render thread. | |
512 base::AutoLock auto_lock(lock_); | 422 base::AutoLock auto_lock(lock_); |
513 if (stop_signal_received_) | 423 if (stop_signal_received_) |
514 return; | 424 return; |
515 DoneRead_Locked(kReadError); | 425 DoneRead_Locked(kReadError); |
516 } | 426 } |
517 | 427 |
518 void BufferedDataSource::ReadCallback( | 428 void BufferedDataSource::ReadCallback( |
519 BufferedResourceLoader::Status status, | 429 BufferedResourceLoader::Status status, |
520 int bytes_read) { | 430 int bytes_read) { |
521 DCHECK(MessageLoop::current() == render_loop_); | 431 DCHECK(MessageLoop::current() == render_loop_); |
(...skipping 30 matching lines...) Expand all Loading... |
552 | 462 |
553 if (host() && total_bytes_ != kPositionNotSpecified) { | 463 if (host() && total_bytes_ != kPositionNotSpecified) { |
554 host()->SetTotalBytes(total_bytes_); | 464 host()->SetTotalBytes(total_bytes_); |
555 host()->AddBufferedByteRange(loader_->first_byte_position(), | 465 host()->AddBufferedByteRange(loader_->first_byte_position(), |
556 total_bytes_); | 466 total_bytes_); |
557 } | 467 } |
558 } | 468 } |
559 DoneRead_Locked(bytes_read); | 469 DoneRead_Locked(bytes_read); |
560 } | 470 } |
561 | 471 |
562 void BufferedDataSource::HttpLoadingStateChangedCallback( | 472 void BufferedDataSource::LoadingStateChangedCallback( |
563 BufferedResourceLoader::LoadingState state) { | 473 BufferedResourceLoader::LoadingState state) { |
564 DCHECK(MessageLoop::current() == render_loop_); | 474 DCHECK(MessageLoop::current() == render_loop_); |
565 DCHECK(url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)); | 475 |
| 476 if (assume_fully_buffered_) |
| 477 return; |
566 | 478 |
567 bool is_downloading_data; | 479 bool is_downloading_data; |
568 switch (state) { | 480 switch (state) { |
569 case BufferedResourceLoader::kLoading: | 481 case BufferedResourceLoader::kLoading: |
570 is_downloading_data = true; | 482 is_downloading_data = true; |
571 break; | 483 break; |
572 case BufferedResourceLoader::kLoadingDeferred: | 484 case BufferedResourceLoader::kLoadingDeferred: |
573 is_downloading_data = false; | 485 is_downloading_data = false; |
574 break; | 486 break; |
575 | 487 |
576 // TODO(scherkus): we don't signal network activity changes when loads | 488 // TODO(scherkus): we don't signal network activity changes when loads |
577 // complete or fail to preserve existing behaviour when deferring is | 489 // complete or fail to preserve existing behaviour when deferring is |
578 // toggled, however we considering changing DownloadingCB to also | 490 // toggled, however we considering changing DownloadingCB to also |
579 // propagate loading state. For example there isn't any signal today | 491 // propagate loading state. For example there isn't any signal today |
580 // to notify the client that loading has failed/finished (we only get | 492 // to notify the client that loading has failed/finished (we only get |
581 // errors on subsequent reads). | 493 // errors on subsequent reads). |
582 case BufferedResourceLoader::kLoadingFailed: | 494 case BufferedResourceLoader::kLoadingFailed: |
583 case BufferedResourceLoader::kLoadingFinished: | 495 case BufferedResourceLoader::kLoadingFinished: |
584 return; | 496 return; |
585 } | 497 } |
586 | 498 |
587 downloading_cb_.Run(is_downloading_data); | 499 downloading_cb_.Run(is_downloading_data); |
588 } | 500 } |
589 | 501 |
590 void BufferedDataSource::HttpProgressCallback(int64 position) { | 502 void BufferedDataSource::ProgressCallback(int64 position) { |
591 DCHECK(MessageLoop::current() == render_loop_); | 503 DCHECK(MessageLoop::current() == render_loop_); |
592 DCHECK(url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)); | 504 |
| 505 if (assume_fully_buffered_) |
| 506 return; |
593 | 507 |
594 // TODO(scherkus): we shouldn't have to lock to signal host(), see | 508 // TODO(scherkus): we shouldn't have to lock to signal host(), see |
595 // http://crbug.com/113712 for details. | 509 // http://crbug.com/113712 for details. |
596 base::AutoLock auto_lock(lock_); | 510 base::AutoLock auto_lock(lock_); |
597 if (stop_signal_received_) | 511 if (stop_signal_received_) |
598 return; | 512 return; |
599 | 513 |
600 if (host() && position > last_read_start_) | 514 if (host() && position > last_read_start_) |
601 host()->AddBufferedByteRange(last_read_start_, position); | 515 host()->AddBufferedByteRange(last_read_start_, position); |
602 } | 516 } |
603 | 517 |
604 void BufferedDataSource::UpdateHostState_Locked() { | 518 void BufferedDataSource::UpdateHostState_Locked() { |
605 lock_.AssertAcquired(); | 519 lock_.AssertAcquired(); |
606 | 520 |
607 if (!host()) | 521 if (!host()) |
608 return; | 522 return; |
609 | 523 |
610 if (total_bytes_ == kPositionNotSpecified) | 524 if (total_bytes_ == kPositionNotSpecified) |
611 return; | 525 return; |
612 | 526 |
613 host()->SetTotalBytes(total_bytes_); | 527 host()->SetTotalBytes(total_bytes_); |
614 | 528 |
615 if (assume_fully_buffered_) | 529 if (assume_fully_buffered_) |
616 host()->AddBufferedByteRange(0, total_bytes_); | 530 host()->AddBufferedByteRange(0, total_bytes_); |
617 } | 531 } |
618 | 532 |
619 } // namespace webkit_media | 533 } // namespace webkit_media |
OLD | NEW |