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

Side by Side Diff: media/renderers/video_renderer_impl.cc

Issue 1863373002: Fix underflow and av/sync issues in low delay and stall presence. (Closed) Base URL: http://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moar tests. Created 4 years, 8 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
OLDNEW
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
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
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
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>(), &current_time); 613 wall_clock_time_cb_.Run(std::vector<base::TimeDelta>(), &current_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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698