Skip to content
This repository has been archived by the owner on Apr 3, 2020. It is now read-only.

Commit

Permalink
Merge to M44: "Fix deferred video underflow if audio underflows first."
Browse files Browse the repository at this point in the history
Looks like the new video rendering path caught a pre-existing bug
with how deferred video underflow is handled \o/

If audio underflowed first, the video renderer's underflow would
still be deferred.  If a Flush() comes in, the VideoRendererImpl
would think it's in the HAVE_NOTHING state and send no update to
its buffering state.

Once Flush() completed and playback was restarted the renderer
would start playback as soon as the audio renderer was ready since
the old buffering state for video was have enough.

The fix is to never defer video renderer underflow if audio has
already underflowed.  I've also added a DCHECK() to make it clear
that the audio renderer is expected to clear deferred underflow for
the video renderer during Flush().

BUG=485324
TEST=new unittest.
TBR=xhwang

Review URL: https://codereview.chromium.org/1148473003

Cr-Commit-Position: refs/heads/master@{#330463}
(cherry picked from commit 32315ab)

Review URL: https://codereview.chromium.org/1143223003

Cr-Commit-Position: refs/branch-heads/2403@{#45}
Cr-Branched-From: f54b809-refs/heads/master@{#330231}
  • Loading branch information
dalecurtis committed May 21, 2015
1 parent a77597f commit 4bc9cf1
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 3 deletions.
10 changes: 8 additions & 2 deletions media/renderers/renderer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,10 @@ void RendererImpl::OnAudioRendererFlushDone() {
DCHECK_EQ(state_, STATE_FLUSHING);
DCHECK(!flush_cb_.is_null());

// If we had a deferred video renderer underflow prior to the flush, it should
// have been cleared by the audio renderer changing to BUFFERING_HAVE_NOTHING.
DCHECK(deferred_underflow_cb_.IsCancelled());

DCHECK_EQ(audio_buffering_state_, BUFFERING_HAVE_NOTHING);
audio_ended_ = false;
FlushVideoRenderer();
Expand Down Expand Up @@ -457,10 +461,12 @@ void RendererImpl::OnBufferingStateChanged(BufferingState* buffering_state,

bool was_waiting_for_enough_data = WaitingForEnoughData();

// When audio is present, defer underflow callbacks for some time to avoid
// unnecessary glitches in audio; see http://crbug.com/144683#c53.
// When audio is present and has enough data, defer video underflow callbacks
// for some time to avoid unnecessary glitches in audio; see
// http://crbug.com/144683#c53.
if (audio_renderer_ && !is_audio && state_ == STATE_PLAYING) {
if (video_buffering_state_ == BUFFERING_HAVE_ENOUGH &&
audio_buffering_state_ == BUFFERING_HAVE_ENOUGH &&
new_buffering_state == BUFFERING_HAVE_NOTHING &&
deferred_underflow_cb_.IsCancelled()) {
deferred_underflow_cb_.Reset(base::Bind(
Expand Down
38 changes: 38 additions & 0 deletions media/renderers/renderer_impl_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -564,4 +564,42 @@ TEST_F(RendererImplTest, VideoAndAudioUnderflow) {
base::RunLoop().RunUntilIdle();
}

TEST_F(RendererImplTest, VideoUnderflowWithAudioFlush) {
InitializeWithAudioAndVideo();
Play();

// Set a massive threshold such that it shouldn't fire within this test.
renderer_impl_->set_video_underflow_threshold_for_testing(
base::TimeDelta::FromSeconds(100));

// Simulate the cases where audio underflows and then video underflows.
EXPECT_CALL(time_source_, StopTicking());
audio_buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
video_buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
Mock::VerifyAndClearExpectations(&time_source_);

// Flush the audio and video renderers, both think they're in an underflow
// state, but if the video renderer underflow was deferred, RendererImpl would
// think it still has enough data.
EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce(RunClosure<0>());
EXPECT_CALL(*video_renderer_, Flush(_)).WillOnce(RunClosure<0>());
EXPECT_CALL(callbacks_, OnFlushed());
renderer_impl_->Flush(
base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_)));
base::RunLoop().RunUntilIdle();

// Start playback after the flush, but never return BUFFERING_HAVE_ENOUGH from
// the video renderer (which simulates spool up time for the video renderer).
const base::TimeDelta kStartTime;
EXPECT_CALL(time_source_, SetMediaTime(kStartTime));
EXPECT_CALL(*audio_renderer_, StartPlaying())
.WillOnce(
SetBufferingState(&audio_buffering_state_cb_, BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*video_renderer_, StartPlayingFrom(kStartTime));
renderer_impl_->StartPlayingFrom(kStartTime);

// Nothing else should primed on the message loop.
base::RunLoop().RunUntilIdle();
}

} // namespace media
2 changes: 1 addition & 1 deletion media/renderers/video_renderer_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ bool VideoRendererImpl::HaveReachedBufferingCap() {

void VideoRendererImpl::StartSink() {
DCHECK(task_runner_->BelongsToCurrentThread());
CHECK_GT(algorithm_->frames_queued(), 0u);
DCHECK_GT(algorithm_->frames_queued(), 0u);
sink_->Start(this);
sink_started_ = true;
was_background_rendering_ = false;
Expand Down

0 comments on commit 4bc9cf1

Please sign in to comment.