| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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/renderers/video_renderer_impl.h" | 5 #include "media/renderers/video_renderer_impl.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/callback.h" | 10 #include "base/callback.h" |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 296 if (sink_started_ == time_progressing_) | 296 if (sink_started_ == time_progressing_) |
| 297 return; | 297 return; |
| 298 | 298 |
| 299 if (time_progressing_) { | 299 if (time_progressing_) { |
| 300 // If only an EOS frame came in after a seek, the renderer may not have | 300 // If only an EOS frame came in after a seek, the renderer may not have |
| 301 // received the ended event yet though we've posted it. | 301 // received the ended event yet though we've posted it. |
| 302 if (!rendered_end_of_stream_) | 302 if (!rendered_end_of_stream_) |
| 303 StartSink(); | 303 StartSink(); |
| 304 } else { | 304 } else { |
| 305 StopSink(); | 305 StopSink(); |
| 306 |
| 307 // Make sure we expire everything we can if we can't read anymore currently, |
| 308 // otherwise playback may hang indefinitely. Note: There are no effective |
| 309 // frames queued at this point, otherwise FrameReady() would have canceled |
| 310 // the underflow state before reaching this point. |
| 311 if (buffering_state_ == BUFFERING_HAVE_NOTHING) |
| 312 RemoveFramesForUnderflowOrBackgroundRendering(); |
| 306 } | 313 } |
| 307 } | 314 } |
| 308 | 315 |
| 309 void VideoRendererImpl::FrameReadyForCopyingToGpuMemoryBuffers( | 316 void VideoRendererImpl::FrameReadyForCopyingToGpuMemoryBuffers( |
| 310 VideoFrameStream::Status status, | 317 VideoFrameStream::Status status, |
| 311 const scoped_refptr<VideoFrame>& frame) { | 318 const scoped_refptr<VideoFrame>& frame) { |
| 312 if (status != VideoFrameStream::OK || IsBeforeStartTime(frame->timestamp())) { | 319 if (status != VideoFrameStream::OK || IsBeforeStartTime(frame->timestamp())) { |
| 313 VideoRendererImpl::FrameReady(sequence_token_, status, frame); | 320 VideoRendererImpl::FrameReady(sequence_token_, status, frame); |
| 314 return; | 321 return; |
| 315 } | 322 } |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 375 } else { | 382 } else { |
| 376 // If the sink hasn't been started, we still have time to release less | 383 // If the sink hasn't been started, we still have time to release less |
| 377 // than ideal frames prior to startup. We don't use IsBeforeStartTime() | 384 // than ideal frames prior to startup. We don't use IsBeforeStartTime() |
| 378 // here since it's based on a duration estimate and we can be exact here. | 385 // here since it's based on a duration estimate and we can be exact here. |
| 379 if (!sink_started_ && frame->timestamp() <= start_timestamp_) | 386 if (!sink_started_ && frame->timestamp() <= start_timestamp_) |
| 380 algorithm_->Reset(); | 387 algorithm_->Reset(); |
| 381 | 388 |
| 382 AddReadyFrame_Locked(frame); | 389 AddReadyFrame_Locked(frame); |
| 383 } | 390 } |
| 384 | 391 |
| 385 // Background rendering updates may not be ticking fast enough by itself to | 392 // Attempt to purge bad frames in case of underflow or backgrounding. |
| 386 // remove expired frames, so give it a boost here by ensuring we don't exit | 393 RemoveFramesForUnderflowOrBackgroundRendering(); |
| 387 // the decoding cycle too early. | |
| 388 // | |
| 389 // Similarly, if we've paused for underflow, remove all frames which are | |
| 390 // before the current media time. | |
| 391 // | |
| 392 // If we're paused for prerolling (current time is 0), don't expire any | |
| 393 // frames. It's possible that during preroll |have_nothing_and_paused| is | |
| 394 // false while |was_background_rendering_| is true. We differentiate this | |
| 395 // from actual background rendering by checking if current time is 0. | |
| 396 const bool have_nothing = buffering_state_ != BUFFERING_HAVE_ENOUGH; | |
| 397 const bool have_nothing_and_paused = have_nothing && !sink_started_; | |
| 398 if (was_background_rendering_ || | |
| 399 (have_nothing_and_paused && drop_frames_)) { | |
| 400 base::TimeTicks current_time = GetCurrentMediaTimeAsWallClockTime(); | |
| 401 if (!current_time.is_null()) { | |
| 402 if (have_nothing_and_paused) { | |
| 403 // Use the current media wall clock time plus the frame duration since | |
| 404 // RemoveExpiredFrames() is expecting the end point of an interval (it | |
| 405 // will subtract from the given value). | |
| 406 frames_dropped_ += algorithm_->RemoveExpiredFrames( | |
| 407 current_time + algorithm_->average_frame_duration()); | |
| 408 } else { | |
| 409 // Don't count dropped frames when background rendering. | |
| 410 algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks()); | |
| 411 } | |
| 412 } | |
| 413 } | |
| 414 | 394 |
| 415 // Signal buffering state if we've met our conditions for having enough | 395 // Signal buffering state if we've met our conditions. |
| 416 // data. | 396 if (buffering_state_ == BUFFERING_HAVE_NOTHING && HaveEnoughData_Locked()) { |
| 417 if (have_nothing && HaveEnoughData_Locked()) { | |
| 418 TransitionToHaveEnough_Locked(); | 397 TransitionToHaveEnough_Locked(); |
| 419 if (!sink_started_ && | 398 if (!sink_started_ && !rendered_end_of_stream_) { |
| 420 !rendered_end_of_stream_) { | |
| 421 start_sink = true; | 399 start_sink = true; |
| 422 render_first_frame_and_stop_ = true; | 400 render_first_frame_and_stop_ = true; |
| 423 posted_maybe_stop_after_first_paint_ = false; | 401 posted_maybe_stop_after_first_paint_ = false; |
| 424 } | 402 } |
| 425 } | 403 } |
| 426 | 404 |
| 427 // Always request more decoded video if we have capacity. This serves two | 405 // Always request more decoded video if we have capacity. This serves two |
| 428 // purposes: | 406 // purposes: |
| 429 // 1) Prerolling while paused | 407 // 1) Prerolling while paused |
| 430 // 2) Keeps decoding going if video rendering thread starts falling behind | 408 // 2) Keeps decoding going if video rendering thread starts falling behind |
| 431 AttemptRead_Locked(); | 409 AttemptRead_Locked(); |
| 432 } | 410 } |
| 433 | 411 |
| 434 // If time is progressing, the sink has already been started; this may be true | 412 // If time is progressing, the sink has already been started; this may be true |
| 435 // if we have previously underflowed, yet weren't stopped because of audio. | 413 // if we have previously underflowed, yet weren't stopped because of audio. |
| 436 if (start_sink) { | 414 if (start_sink) { |
| 437 DCHECK(!sink_started_); | 415 DCHECK(!sink_started_); |
| 438 StartSink(); | 416 StartSink(); |
| 439 } | 417 } |
| 440 } | 418 } |
| 441 | 419 |
| 442 bool VideoRendererImpl::HaveEnoughData_Locked() { | 420 bool VideoRendererImpl::HaveEnoughData_Locked() { |
| 443 DCHECK_EQ(state_, kPlaying); | 421 DCHECK_EQ(state_, kPlaying); |
| 422 lock_.AssertAcquired(); |
| 444 | 423 |
| 445 if (received_end_of_stream_ || !video_frame_stream_->CanReadWithoutStalling()) | 424 if (received_end_of_stream_) |
| 446 return true; | 425 return true; |
| 447 | 426 |
| 448 if (HaveReachedBufferingCap()) | 427 if (HaveReachedBufferingCap()) |
| 449 return true; | 428 return true; |
| 450 | 429 |
| 451 if (was_background_rendering_ && frames_decoded_) { | 430 if (was_background_rendering_ && frames_decoded_) |
| 452 return true; | 431 return true; |
| 453 } | |
| 454 | 432 |
| 455 if (!low_delay_) | 433 if (!low_delay_ && video_frame_stream_->CanReadWithoutStalling()) |
| 456 return false; | 434 return false; |
| 457 | 435 |
| 458 return algorithm_->frames_queued() > 0; | 436 return algorithm_->effective_frames_queued() > 0; |
| 459 } | 437 } |
| 460 | 438 |
| 461 void VideoRendererImpl::TransitionToHaveEnough_Locked() { | 439 void VideoRendererImpl::TransitionToHaveEnough_Locked() { |
| 462 DCHECK(task_runner_->BelongsToCurrentThread()); | 440 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 463 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); | 441 DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING); |
| 442 lock_.AssertAcquired(); |
| 464 | 443 |
| 465 buffering_state_ = BUFFERING_HAVE_ENOUGH; | 444 buffering_state_ = BUFFERING_HAVE_ENOUGH; |
| 466 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); | 445 buffering_state_cb_.Run(BUFFERING_HAVE_ENOUGH); |
| 467 } | 446 } |
| 468 | 447 |
| 469 void VideoRendererImpl::TransitionToHaveNothing() { | 448 void VideoRendererImpl::TransitionToHaveNothing() { |
| 470 DCHECK(task_runner_->BelongsToCurrentThread()); | 449 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 471 | 450 |
| 472 base::AutoLock auto_lock(lock_); | 451 base::AutoLock auto_lock(lock_); |
| 473 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked()) | 452 if (buffering_state_ != BUFFERING_HAVE_ENOUGH || HaveEnoughData_Locked()) |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 632 base::TimeTicks VideoRendererImpl::GetCurrentMediaTimeAsWallClockTime() { | 611 base::TimeTicks VideoRendererImpl::GetCurrentMediaTimeAsWallClockTime() { |
| 633 std::vector<base::TimeTicks> current_time; | 612 std::vector<base::TimeTicks> current_time; |
| 634 wall_clock_time_cb_.Run(std::vector<base::TimeDelta>(), ¤t_time); | 613 wall_clock_time_cb_.Run(std::vector<base::TimeDelta>(), ¤t_time); |
| 635 return current_time[0]; | 614 return current_time[0]; |
| 636 } | 615 } |
| 637 | 616 |
| 638 bool VideoRendererImpl::IsBeforeStartTime(base::TimeDelta timestamp) { | 617 bool VideoRendererImpl::IsBeforeStartTime(base::TimeDelta timestamp) { |
| 639 return timestamp + video_frame_stream_->AverageDuration() < start_timestamp_; | 618 return timestamp + video_frame_stream_->AverageDuration() < start_timestamp_; |
| 640 } | 619 } |
| 641 | 620 |
| 621 void VideoRendererImpl::RemoveFramesForUnderflowOrBackgroundRendering() { |
| 622 // Nothing to do if we're not underflowing, background rendering, or frame |
| 623 // dropping is disabled (test only). |
| 624 const bool have_nothing = buffering_state_ == BUFFERING_HAVE_NOTHING; |
| 625 if (!was_background_rendering_ && !have_nothing && !drop_frames_) |
| 626 return; |
| 627 |
| 628 // If there are no frames to remove, nothing can be done. |
| 629 if (!algorithm_->frames_queued()) |
| 630 return; |
| 631 |
| 632 // If we're paused for prerolling (current time is 0), don't expire any |
| 633 // frames. It's possible that during preroll |have_nothing| is false while |
| 634 // |was_background_rendering_| is true. We differentiate this from actual |
| 635 // background rendering by checking if current time is 0. |
| 636 const base::TimeTicks current_time = GetCurrentMediaTimeAsWallClockTime(); |
| 637 if (current_time.is_null()) |
| 638 return; |
| 639 |
| 640 // Background rendering updates may not be ticking fast enough to remove |
| 641 // expired frames, so provide a boost here by ensuring we don't exit the |
| 642 // decoding cycle too early. Dropped frames are not counted in this case. |
| 643 if (was_background_rendering_) { |
| 644 algorithm_->RemoveExpiredFrames(tick_clock_->NowTicks()); |
| 645 return; |
| 646 } |
| 647 |
| 648 // Use the current media wall clock time plus the frame duration since |
| 649 // RemoveExpiredFrames() is expecting the end point of an interval (it will |
| 650 // subtract from the given value). It's important to always call this so |
| 651 // that frame statistics are updated correctly. |
| 652 frames_dropped_ += algorithm_->RemoveExpiredFrames( |
| 653 current_time + algorithm_->average_frame_duration()); |
| 654 |
| 655 // If we've paused for underflow, and still have no effective frames, clear |
| 656 // the entire queue. Note: this may cause slight inaccuracies in the number |
| 657 // of dropped frames since the frame may have been rendered before. |
| 658 if (!sink_started_ && !algorithm_->effective_frames_queued()) { |
| 659 frames_dropped_ += algorithm_->frames_queued(); |
| 660 algorithm_->Reset( |
| 661 VideoRendererAlgorithm::ResetFlag::kPreserveNextFrameEstimates); |
| 662 } |
| 663 } |
| 664 |
| 642 } // namespace media | 665 } // namespace media |
| OLD | NEW |