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 "media/filters/video_renderer_base.h" | 5 #include "media/filters/video_renderer_base.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
10 #include "base/threading/platform_thread.h" | 10 #include "base/threading/platform_thread.h" |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 void VideoRendererBase::Preroll(base::TimeDelta time, | 97 void VideoRendererBase::Preroll(base::TimeDelta time, |
98 const PipelineStatusCB& cb) { | 98 const PipelineStatusCB& cb) { |
99 base::AutoLock auto_lock(lock_); | 99 base::AutoLock auto_lock(lock_); |
100 DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling."; | 100 DCHECK_EQ(state_, kFlushed) << "Must flush prior to prerolling."; |
101 DCHECK(!cb.is_null()); | 101 DCHECK(!cb.is_null()); |
102 DCHECK(preroll_cb_.is_null()); | 102 DCHECK(preroll_cb_.is_null()); |
103 | 103 |
104 state_ = kPrerolling; | 104 state_ = kPrerolling; |
105 preroll_cb_ = cb; | 105 preroll_cb_ = cb; |
106 preroll_timestamp_ = time; | 106 preroll_timestamp_ = time; |
| 107 prerolling_delayed_frame_ = NULL; |
107 AttemptRead_Locked(); | 108 AttemptRead_Locked(); |
108 } | 109 } |
109 | 110 |
110 void VideoRendererBase::Initialize(const scoped_refptr<VideoDecoder>& decoder, | 111 void VideoRendererBase::Initialize(const scoped_refptr<VideoDecoder>& decoder, |
111 const PipelineStatusCB& init_cb, | 112 const PipelineStatusCB& init_cb, |
112 const StatisticsCB& statistics_cb, | 113 const StatisticsCB& statistics_cb, |
113 const TimeCB& time_cb, | 114 const TimeCB& time_cb, |
114 const NaturalSizeChangedCB& size_changed_cb, | 115 const NaturalSizeChangedCB& size_changed_cb, |
115 const base::Closure& ended_cb, | 116 const base::Closure& ended_cb, |
116 const PipelineStatusCB& error_cb, | 117 const PipelineStatusCB& error_cb, |
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
414 // Abort preroll early for a NULL frame because we won't get more frames. | 415 // Abort preroll early for a NULL frame because we won't get more frames. |
415 // A new preroll will be requested after this one completes so there is no | 416 // A new preroll will be requested after this one completes so there is no |
416 // point trying to collect more frames. | 417 // point trying to collect more frames. |
417 state_ = kPrerolled; | 418 state_ = kPrerolled; |
418 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); | 419 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); |
419 return; | 420 return; |
420 } | 421 } |
421 | 422 |
422 // Discard frames until we reach our desired preroll timestamp. | 423 // Discard frames until we reach our desired preroll timestamp. |
423 if (state_ == kPrerolling && !frame->IsEndOfStream() && | 424 if (state_ == kPrerolling && !frame->IsEndOfStream() && |
424 (frame->GetTimestamp() + frame->GetDuration()) <= preroll_timestamp_) { | 425 frame->GetTimestamp() <= preroll_timestamp_) { |
| 426 prerolling_delayed_frame_ = frame; |
425 AttemptRead_Locked(); | 427 AttemptRead_Locked(); |
426 return; | 428 return; |
427 } | 429 } |
428 | 430 |
429 // Adjust the incoming frame if its rendering stop time is past the duration | 431 if (prerolling_delayed_frame_) { |
430 // of the video itself. This is typically the last frame of the video and | 432 DCHECK_EQ(state_, kPrerolling); |
431 // occurs if the container specifies a duration that isn't a multiple of the | 433 AddReadyFrame(prerolling_delayed_frame_); |
432 // frame rate. Another way for this to happen is for the container to state a | 434 prerolling_delayed_frame_ = NULL; |
433 // smaller duration than the largest packet timestamp. | |
434 if (!frame->IsEndOfStream()) { | |
435 base::TimeDelta duration = get_duration_cb_.Run(); | |
436 if (frame->GetTimestamp() > duration) | |
437 frame->SetTimestamp(duration); | |
438 if ((frame->GetTimestamp() + frame->GetDuration()) > duration) | |
439 frame->SetDuration(duration - frame->GetTimestamp()); | |
440 } | 435 } |
441 | 436 |
442 // This one's a keeper! Place it in the ready queue. | 437 AddReadyFrame(frame); |
443 ready_frames_.push_back(frame); | |
444 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); | |
445 if (!frame->IsEndOfStream()) | |
446 time_cb_.Run(frame->GetTimestamp() + frame->GetDuration()); | |
447 frame_available_.Signal(); | |
448 | |
449 PipelineStatistics statistics; | 438 PipelineStatistics statistics; |
450 statistics.video_frames_decoded = 1; | 439 statistics.video_frames_decoded = 1; |
451 statistics_cb_.Run(statistics); | 440 statistics_cb_.Run(statistics); |
452 | 441 |
453 // Always request more decoded video if we have capacity. This serves two | 442 // Always request more decoded video if we have capacity. This serves two |
454 // purposes: | 443 // purposes: |
455 // 1) Prerolling while paused | 444 // 1) Prerolling while paused |
456 // 2) Keeps decoding going if video rendering thread starts falling behind | 445 // 2) Keeps decoding going if video rendering thread starts falling behind |
457 if (NumFrames_Locked() < limits::kMaxVideoFrames && !frame->IsEndOfStream()) { | 446 if (NumFrames_Locked() < limits::kMaxVideoFrames && !frame->IsEndOfStream()) { |
458 AttemptRead_Locked(); | 447 AttemptRead_Locked(); |
(...skipping 16 matching lines...) Expand all Loading... |
475 | 464 |
476 // ...and we're done prerolling! | 465 // ...and we're done prerolling! |
477 DCHECK(!preroll_cb_.is_null()); | 466 DCHECK(!preroll_cb_.is_null()); |
478 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); | 467 base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK); |
479 | 468 |
480 base::AutoUnlock ul(lock_); | 469 base::AutoUnlock ul(lock_); |
481 paint_cb_.Run(); | 470 paint_cb_.Run(); |
482 } | 471 } |
483 } | 472 } |
484 | 473 |
| 474 void VideoRendererBase::AddReadyFrame(const scoped_refptr<VideoFrame>& frame) { |
| 475 // Adjust the incoming frame if its rendering stop time is past the duration |
| 476 // of the video itself. This is typically the last frame of the video and |
| 477 // occurs if the container specifies a duration that isn't a multiple of the |
| 478 // frame rate. Another way for this to happen is for the container to state a |
| 479 // smaller duration than the largest packet timestamp. |
| 480 base::TimeDelta duration = get_duration_cb_.Run(); |
| 481 if (frame->GetTimestamp() > duration || frame->IsEndOfStream()) { |
| 482 frame->SetTimestamp(duration); |
| 483 } |
| 484 |
| 485 ready_frames_.push_back(frame); |
| 486 DCHECK_LE(NumFrames_Locked(), limits::kMaxVideoFrames); |
| 487 time_cb_.Run(frame->GetTimestamp()); |
| 488 frame_available_.Signal(); |
| 489 } |
| 490 |
485 void VideoRendererBase::AttemptRead_Locked() { | 491 void VideoRendererBase::AttemptRead_Locked() { |
486 lock_.AssertAcquired(); | 492 lock_.AssertAcquired(); |
487 DCHECK_NE(kEnded, state_); | 493 DCHECK_NE(kEnded, state_); |
488 | 494 |
489 if (pending_read_ || | 495 if (pending_read_ || |
490 NumFrames_Locked() == limits::kMaxVideoFrames || | 496 NumFrames_Locked() == limits::kMaxVideoFrames || |
491 (!ready_frames_.empty() && ready_frames_.back()->IsEndOfStream()) || | 497 (!ready_frames_.empty() && ready_frames_.back()->IsEndOfStream()) || |
492 state_ == kFlushingDecoder || | 498 state_ == kFlushingDecoder || |
493 state_ == kFlushing) { | 499 state_ == kFlushing) { |
494 return; | 500 return; |
495 } | 501 } |
496 | 502 |
497 pending_read_ = true; | 503 pending_read_ = true; |
498 decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, this)); | 504 decoder_->Read(base::Bind(&VideoRendererBase::FrameReady, this)); |
499 } | 505 } |
500 | 506 |
501 void VideoRendererBase::OnDecoderFlushDone() { | 507 void VideoRendererBase::OnDecoderFlushDone() { |
502 base::AutoLock auto_lock(lock_); | 508 base::AutoLock auto_lock(lock_); |
503 DCHECK_EQ(kFlushingDecoder, state_); | 509 DCHECK_EQ(kFlushingDecoder, state_); |
504 DCHECK(!pending_read_); | 510 DCHECK(!pending_read_); |
505 | 511 |
506 state_ = kFlushing; | 512 state_ = kFlushing; |
507 AttemptFlush_Locked(); | 513 AttemptFlush_Locked(); |
508 } | 514 } |
509 | 515 |
510 void VideoRendererBase::AttemptFlush_Locked() { | 516 void VideoRendererBase::AttemptFlush_Locked() { |
511 lock_.AssertAcquired(); | 517 lock_.AssertAcquired(); |
512 DCHECK_EQ(kFlushing, state_); | 518 DCHECK_EQ(kFlushing, state_); |
513 | 519 |
514 // Get rid of any ready frames. | 520 prerolling_delayed_frame_ = NULL; |
515 ready_frames_.clear(); | 521 ready_frames_.clear(); |
516 | 522 |
517 if (!pending_paint_ && !pending_read_) { | 523 if (!pending_paint_ && !pending_read_) { |
518 state_ = kFlushed; | 524 state_ = kFlushed; |
519 current_frame_ = NULL; | 525 current_frame_ = NULL; |
520 base::ResetAndReturn(&flush_cb_).Run(); | 526 base::ResetAndReturn(&flush_cb_).Run(); |
521 } | 527 } |
522 } | 528 } |
523 | 529 |
524 base::TimeDelta VideoRendererBase::CalculateSleepDuration( | 530 base::TimeDelta VideoRendererBase::CalculateSleepDuration( |
525 const scoped_refptr<VideoFrame>& next_frame, | 531 const scoped_refptr<VideoFrame>& next_frame, |
526 float playback_rate) { | 532 float playback_rate) { |
527 // Determine the current and next presentation timestamps. | 533 // Determine the current and next presentation timestamps. |
528 base::TimeDelta now = get_time_cb_.Run(); | 534 base::TimeDelta now = get_time_cb_.Run(); |
529 base::TimeDelta this_pts = current_frame_->GetTimestamp(); | 535 base::TimeDelta next_pts = next_frame->GetTimestamp(); |
530 base::TimeDelta next_pts; | |
531 if (!next_frame->IsEndOfStream()) { | |
532 next_pts = next_frame->GetTimestamp(); | |
533 } else { | |
534 next_pts = this_pts + current_frame_->GetDuration(); | |
535 } | |
536 | 536 |
537 // Scale our sleep based on the playback rate. | 537 // Scale our sleep based on the playback rate. |
538 base::TimeDelta sleep = next_pts - now; | 538 base::TimeDelta sleep = next_pts - now; |
539 return base::TimeDelta::FromMicroseconds( | 539 return base::TimeDelta::FromMicroseconds( |
540 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); | 540 static_cast<int64>(sleep.InMicroseconds() / playback_rate)); |
541 } | 541 } |
542 | 542 |
543 void VideoRendererBase::DoStopOrError_Locked() { | 543 void VideoRendererBase::DoStopOrError_Locked() { |
544 DCHECK(!pending_paint_); | 544 DCHECK(!pending_paint_); |
545 DCHECK(!pending_paint_with_last_available_); | 545 DCHECK(!pending_paint_with_last_available_); |
546 lock_.AssertAcquired(); | 546 lock_.AssertAcquired(); |
547 current_frame_ = NULL; | 547 current_frame_ = NULL; |
548 last_available_frame_ = NULL; | 548 last_available_frame_ = NULL; |
549 ready_frames_.clear(); | 549 ready_frames_.clear(); |
550 } | 550 } |
551 | 551 |
552 int VideoRendererBase::NumFrames_Locked() const { | 552 int VideoRendererBase::NumFrames_Locked() const { |
553 lock_.AssertAcquired(); | 553 lock_.AssertAcquired(); |
554 int outstanding_frames = | 554 int outstanding_frames = |
555 (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + | 555 (current_frame_ ? 1 : 0) + (last_available_frame_ ? 1 : 0) + |
556 (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); | 556 (current_frame_ && (current_frame_ == last_available_frame_) ? -1 : 0); |
557 return ready_frames_.size() + outstanding_frames; | 557 return ready_frames_.size() + outstanding_frames; |
558 } | 558 } |
559 | 559 |
560 } // namespace media | 560 } // namespace media |
OLD | NEW |