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 buffered_bytes_(0), | 36 assume_fully_buffered_(false), |
44 streaming_(false), | 37 streaming_(false), |
45 frame_(frame), | 38 frame_(frame), |
46 read_size_(0), | 39 read_size_(0), |
47 read_buffer_(NULL), | 40 read_buffer_(NULL), |
48 last_read_start_(0), | 41 last_read_start_(0), |
49 intermediate_read_buffer_(new uint8[kInitialReadBufferSize]), | 42 intermediate_read_buffer_(new uint8[kInitialReadBufferSize]), |
50 intermediate_read_buffer_size_(kInitialReadBufferSize), | 43 intermediate_read_buffer_size_(kInitialReadBufferSize), |
51 render_loop_(render_loop), | 44 render_loop_(render_loop), |
52 stop_signal_received_(false), | 45 stop_signal_received_(false), |
53 stopped_on_render_loop_(false), | 46 stopped_on_render_loop_(false), |
(...skipping 48 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)); |
118 } | 111 } |
119 | 112 |
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( | 113 loader_->Start( |
126 base::Bind(&BufferedDataSource::NonHttpInitialStartCallback, this), | 114 base::Bind(&BufferedDataSource::StartCallback, this), |
127 base::Bind(&NonHttpLoadingStateChangedCallback), | 115 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, this), |
128 base::Bind(&NonHttpProgressCallback), | 116 base::Bind(&BufferedDataSource::ProgressCallback, this), |
129 frame_); | 117 frame_); |
130 } | 118 } |
131 | 119 |
132 void BufferedDataSource::SetPreload(Preload preload) { | 120 void BufferedDataSource::SetPreload(Preload preload) { |
133 DCHECK(MessageLoop::current() == render_loop_); | 121 DCHECK(MessageLoop::current() == render_loop_); |
134 preload_ = preload; | 122 preload_ = preload; |
135 } | 123 } |
136 | 124 |
137 bool BufferedDataSource::HasSingleOrigin() { | 125 bool BufferedDataSource::HasSingleOrigin() { |
138 DCHECK(MessageLoop::current() == render_loop_); | 126 DCHECK(MessageLoop::current() == render_loop_); |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
271 return; | 259 return; |
272 | 260 |
273 { | 261 { |
274 // If there's no outstanding read then return early. | 262 // If there's no outstanding read then return early. |
275 base::AutoLock auto_lock(lock_); | 263 base::AutoLock auto_lock(lock_); |
276 if (read_cb_.is_null()) | 264 if (read_cb_.is_null()) |
277 return; | 265 return; |
278 } | 266 } |
279 | 267 |
280 // Start reading from where we last left off until the end of the resource. | 268 // Start reading from where we last left off until the end of the resource. |
281 loader_.reset( | 269 loader_.reset(CreateResourceLoader(last_read_start_, kPositionNotSpecified)); |
282 CreateResourceLoader(last_read_start_, kPositionNotSpecified)); | 270 loader_->Start( |
283 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) { | 271 base::Bind(&BufferedDataSource::PartialReadStartCallback, this), |
284 loader_->Start( | 272 base::Bind(&BufferedDataSource::LoadingStateChangedCallback, this), |
285 base::Bind(&BufferedDataSource::PartialReadStartCallback, this), | 273 base::Bind(&BufferedDataSource::ProgressCallback, this), |
286 base::Bind(&BufferedDataSource::HttpLoadingStateChangedCallback, this), | 274 frame_); |
287 base::Bind(&BufferedDataSource::HttpProgressCallback, this), | |
288 frame_); | |
289 } else { | |
290 loader_->Start( | |
291 base::Bind(&BufferedDataSource::NonHttpInitialStartCallback, this), | |
292 base::Bind(&NonHttpLoadingStateChangedCallback), | |
293 base::Bind(&NonHttpProgressCallback), | |
294 frame_); | |
295 } | |
296 } | 275 } |
297 | 276 |
298 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) { | 277 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) { |
299 DCHECK(MessageLoop::current() == render_loop_); | 278 DCHECK(MessageLoop::current() == render_loop_); |
300 DCHECK(loader_.get()); | 279 DCHECK(loader_.get()); |
301 | 280 |
302 if (playback_rate != 0) | 281 if (playback_rate != 0) |
303 media_has_played_ = true; | 282 media_has_played_ = true; |
304 | 283 |
305 playback_rate_ = playback_rate; | 284 playback_rate_ = playback_rate; |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
368 DCHECK(MessageLoop::current() == render_loop_); | 347 DCHECK(MessageLoop::current() == render_loop_); |
369 DCHECK(!initialize_cb_.is_null()); | 348 DCHECK(!initialize_cb_.is_null()); |
370 lock_.AssertAcquired(); | 349 lock_.AssertAcquired(); |
371 | 350 |
372 initialize_cb_.Run(status); | 351 initialize_cb_.Run(status); |
373 initialize_cb_.Reset(); | 352 initialize_cb_.Reset(); |
374 } | 353 } |
375 | 354 |
376 ///////////////////////////////////////////////////////////////////////////// | 355 ///////////////////////////////////////////////////////////////////////////// |
377 // BufferedResourceLoader callback methods. | 356 // BufferedResourceLoader callback methods. |
378 void BufferedDataSource::HttpInitialStartCallback( | 357 void BufferedDataSource::StartCallback( |
379 BufferedResourceLoader::Status status) { | 358 BufferedResourceLoader::Status status) { |
380 DCHECK(MessageLoop::current() == render_loop_); | 359 DCHECK(MessageLoop::current() == render_loop_); |
381 DCHECK(loader_.get()); | 360 DCHECK(loader_.get()); |
382 | 361 |
383 bool initialize_cb_is_null = false; | 362 bool initialize_cb_is_null = false; |
384 { | 363 { |
385 base::AutoLock auto_lock(lock_); | 364 base::AutoLock auto_lock(lock_); |
386 initialize_cb_is_null = initialize_cb_.is_null(); | 365 initialize_cb_is_null = initialize_cb_.is_null(); |
387 } | 366 } |
388 if (initialize_cb_is_null) { | 367 if (initialize_cb_is_null) { |
389 loader_->Stop(); | 368 loader_->Stop(); |
390 return; | 369 return; |
391 } | 370 } |
392 | 371 |
393 bool success = status == BufferedResourceLoader::kOk; | 372 // All responses must be successful. HTTP responses can have unknown content |
373 // lengths (i.e., chunked transfer mode) but non-HTTP responses must have a | |
374 // known content length. | |
375 bool is_http = url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme); | |
376 bool success = (status == BufferedResourceLoader::kOk) && | |
377 (is_http || 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 |
399 !loader_->range_supported(); | 382 if (is_http) { |
383 streaming_ = (total_bytes_ == kPositionNotSpecified) || | |
384 !loader_->range_supported(); | |
385 } else { | |
386 assume_fully_buffered_ = true; | |
Ami GONE FROM CHROMIUM
2012/07/11 00:56:41
you want this for ftp??
scherkus (not reviewing)
2012/07/11 18:39:02
Not ideally but this patch doesn't change that fac
| |
387 } | |
400 } else { | 388 } else { |
401 // TODO(hclam): In case of failure, we can retry several times. | |
402 loader_->Stop(); | 389 loader_->Stop(); |
403 } | 390 } |
404 | 391 |
405 // Reference to prevent destruction while inside the |initialize_cb_| | 392 // 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 | 393 // 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); | 394 scoped_refptr<BufferedDataSource> destruction_guard(this); |
411 { | 395 { |
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_); | 396 base::AutoLock auto_lock(lock_); |
421 if (stop_signal_received_) | 397 if (stop_signal_received_) |
422 return; | 398 return; |
423 | 399 |
424 if (!success) { | 400 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 buffered_bytes_ = total_bytes_; | |
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); | 401 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK); |
481 return; | 402 return; |
482 } | 403 } |
483 | 404 |
484 UpdateHostState_Locked(); | 405 UpdateHostState_Locked(); |
485 DoneInitialization_Locked(media::PIPELINE_OK); | 406 DoneInitialization_Locked(media::PIPELINE_OK); |
486 } | 407 } |
487 } | 408 } |
488 | 409 |
489 void BufferedDataSource::PartialReadStartCallback( | 410 void BufferedDataSource::PartialReadStartCallback( |
490 BufferedResourceLoader::Status status) { | 411 BufferedResourceLoader::Status status) { |
491 DCHECK(MessageLoop::current() == render_loop_); | 412 DCHECK(MessageLoop::current() == render_loop_); |
492 DCHECK(loader_.get()); | 413 DCHECK(loader_.get()); |
493 | 414 |
494 if (status == BufferedResourceLoader::kOk) { | 415 if (status == BufferedResourceLoader::kOk) { |
495 // Once the request has started successfully, we can proceed with | 416 // Once the request has started successfully, we can proceed with |
496 // reading from it. | 417 // reading from it. |
497 ReadInternal(); | 418 ReadInternal(); |
498 return; | 419 return; |
499 } | 420 } |
500 | 421 |
501 // Stop the resource loader since we have received an error. | 422 // Stop the resource loader since we have received an error. |
502 loader_->Stop(); | 423 loader_->Stop(); |
503 | 424 |
504 // We need to prevent calling to filter host and running the callback if | 425 // 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 | 426 // 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_); | 427 base::AutoLock auto_lock(lock_); |
513 if (stop_signal_received_) | 428 if (stop_signal_received_) |
514 return; | 429 return; |
515 DoneRead_Locked(kReadError); | 430 DoneRead_Locked(kReadError); |
516 } | 431 } |
517 | 432 |
518 void BufferedDataSource::ReadCallback( | 433 void BufferedDataSource::ReadCallback( |
519 BufferedResourceLoader::Status status, | 434 BufferedResourceLoader::Status status, |
520 int bytes_read) { | 435 int bytes_read) { |
521 DCHECK(MessageLoop::current() == render_loop_); | 436 DCHECK(MessageLoop::current() == render_loop_); |
(...skipping 30 matching lines...) Expand all Loading... | |
552 | 467 |
553 if (host() && total_bytes_ != kPositionNotSpecified) { | 468 if (host() && total_bytes_ != kPositionNotSpecified) { |
554 host()->SetTotalBytes(total_bytes_); | 469 host()->SetTotalBytes(total_bytes_); |
555 host()->AddBufferedByteRange(loader_->first_byte_position(), | 470 host()->AddBufferedByteRange(loader_->first_byte_position(), |
556 total_bytes_); | 471 total_bytes_); |
557 } | 472 } |
558 } | 473 } |
559 DoneRead_Locked(bytes_read); | 474 DoneRead_Locked(bytes_read); |
560 } | 475 } |
561 | 476 |
562 void BufferedDataSource::HttpLoadingStateChangedCallback( | 477 void BufferedDataSource::LoadingStateChangedCallback( |
563 BufferedResourceLoader::LoadingState state) { | 478 BufferedResourceLoader::LoadingState state) { |
564 DCHECK(MessageLoop::current() == render_loop_); | 479 DCHECK(MessageLoop::current() == render_loop_); |
565 DCHECK(url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)); | 480 |
481 if (assume_fully_buffered_) | |
482 return; | |
566 | 483 |
567 bool is_downloading_data; | 484 bool is_downloading_data; |
568 switch (state) { | 485 switch (state) { |
569 case BufferedResourceLoader::kLoading: | 486 case BufferedResourceLoader::kLoading: |
570 is_downloading_data = true; | 487 is_downloading_data = true; |
571 break; | 488 break; |
572 case BufferedResourceLoader::kLoadingDeferred: | 489 case BufferedResourceLoader::kLoadingDeferred: |
573 is_downloading_data = false; | 490 is_downloading_data = false; |
574 break; | 491 break; |
575 | 492 |
576 // TODO(scherkus): we don't signal network activity changes when loads | 493 // TODO(scherkus): we don't signal network activity changes when loads |
577 // complete or fail to preserve existing behaviour when deferring is | 494 // complete or fail to preserve existing behaviour when deferring is |
578 // toggled, however we considering changing DownloadingCB to also | 495 // toggled, however we considering changing DownloadingCB to also |
579 // propagate loading state. For example there isn't any signal today | 496 // propagate loading state. For example there isn't any signal today |
580 // to notify the client that loading has failed/finished (we only get | 497 // to notify the client that loading has failed/finished (we only get |
581 // errors on subsequent reads). | 498 // errors on subsequent reads). |
582 case BufferedResourceLoader::kLoadingFailed: | 499 case BufferedResourceLoader::kLoadingFailed: |
583 case BufferedResourceLoader::kLoadingFinished: | 500 case BufferedResourceLoader::kLoadingFinished: |
584 return; | 501 return; |
585 } | 502 } |
586 | 503 |
587 downloading_cb_.Run(is_downloading_data); | 504 downloading_cb_.Run(is_downloading_data); |
588 } | 505 } |
589 | 506 |
590 void BufferedDataSource::HttpProgressCallback(int64 position) { | 507 void BufferedDataSource::ProgressCallback(int64 position) { |
591 DCHECK(MessageLoop::current() == render_loop_); | 508 DCHECK(MessageLoop::current() == render_loop_); |
592 DCHECK(url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)); | 509 DCHECK(url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)); |
593 | 510 |
511 if (assume_fully_buffered_) | |
512 return; | |
513 | |
594 // TODO(scherkus): we shouldn't have to lock to signal host(), see | 514 // TODO(scherkus): we shouldn't have to lock to signal host(), see |
595 // http://crbug.com/113712 for details. | 515 // http://crbug.com/113712 for details. |
596 base::AutoLock auto_lock(lock_); | 516 base::AutoLock auto_lock(lock_); |
597 if (stop_signal_received_) | 517 if (stop_signal_received_) |
598 return; | 518 return; |
599 | 519 |
600 if (host() && position > last_read_start_) | 520 if (host() && position > last_read_start_) |
601 host()->AddBufferedByteRange(last_read_start_, position); | 521 host()->AddBufferedByteRange(last_read_start_, position); |
602 } | 522 } |
603 | 523 |
604 void BufferedDataSource::UpdateHostState_Locked() { | 524 void BufferedDataSource::UpdateHostState_Locked() { |
605 lock_.AssertAcquired(); | 525 lock_.AssertAcquired(); |
606 | 526 |
607 if (!host()) | 527 if (!host()) |
608 return; | 528 return; |
609 | 529 |
610 if (total_bytes_ != kPositionNotSpecified) | 530 if (total_bytes_ != kPositionNotSpecified) |
611 host()->SetTotalBytes(total_bytes_); | 531 host()->SetTotalBytes(total_bytes_); |
612 int64 start = loader_->first_byte_position(); | 532 |
613 if (buffered_bytes_ > start) | 533 if (assume_fully_buffered_) |
614 host()->AddBufferedByteRange(start, buffered_bytes_); | 534 host()->AddBufferedByteRange(0, total_bytes_); |
615 } | 535 } |
616 | 536 |
617 } // namespace webkit_media | 537 } // namespace webkit_media |
OLD | NEW |