diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/media/renderers/video_renderer_impl_unittest.cc | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) | |
download | qtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz |
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/media/renderers/video_renderer_impl_unittest.cc')
-rw-r--r-- | chromium/media/renderers/video_renderer_impl_unittest.cc | 697 |
1 files changed, 554 insertions, 143 deletions
diff --git a/chromium/media/renderers/video_renderer_impl_unittest.cc b/chromium/media/renderers/video_renderer_impl_unittest.cc index fd1a93ebd04..093dccbec18 100644 --- a/chromium/media/renderers/video_renderer_impl_unittest.cc +++ b/chromium/media/renderers/video_renderer_impl_unittest.cc @@ -39,6 +39,7 @@ using ::base::test::RunClosure; using ::base::test::RunOnceCallback; +using ::base::test::RunOnceClosure; using ::testing::_; using ::testing::AnyNumber; using ::testing::Combine; @@ -65,8 +66,11 @@ class VideoRendererImplTest : public testing::Test { std::vector<std::unique_ptr<VideoDecoder>> decoders; decoders.push_back(base::WrapUnique(decoder_)); ON_CALL(*decoder_, Initialize_(_, _, _, _, _, _)) - .WillByDefault(DoAll(SaveArg<4>(&output_cb_), - RunOnceCallback<3>(expect_init_success_))); + .WillByDefault(DoAll( + SaveArg<4>(&output_cb_), + RunOnceCallback<3>(expect_init_success_ + ? OkStatus() + : Status(StatusCode::kCodeOnlyForTesting)))); // Monitor decodes from the decoder. ON_CALL(*decoder_, Decode_(_, _)) .WillByDefault(Invoke(this, &VideoRendererImplTest::DecodeRequested)); @@ -77,7 +81,8 @@ class VideoRendererImplTest : public testing::Test { } VideoRendererImplTest() - : decoder_(nullptr), + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + decoder_(nullptr), demuxer_stream_(DemuxerStream::VIDEO), simulate_decode_delay_(false), expect_init_success_(true) { @@ -101,11 +106,25 @@ class VideoRendererImplTest : public testing::Test { demuxer_stream_.set_video_decoder_config(TestVideoConfig::Normal()); - // We expect these to be called but we don't care how/when. - EXPECT_CALL(demuxer_stream_, OnRead(_)) - .WillRepeatedly(RunOnceCallback<0>( - DemuxerStream::kOk, - scoped_refptr<DecoderBuffer>(new DecoderBuffer(0)))); + // We expect these to be called but we don't care how/when. Tests can + // customize the provided buffer returned via MakeDecoderBuffer(). + ON_CALL(demuxer_stream_, OnRead(_)) + .WillByDefault(Invoke(this, &VideoRendererImplTest::MakeDecoderBuffer)); + } + + void MakeDecoderBuffer(DemuxerStream::ReadCB& read_cb) { + scoped_refptr<DecoderBuffer> decoder_buffer(new DecoderBuffer(0)); + + // Set |decoder_buffer| timestamp such that it won't match any of the + // times provided to QueueFrames(). Otherwise the default timestamp of 0 may + // match some frames and not others, which causes non-uniform handling in + // DecoderStreamTraits. + decoder_buffer->set_timestamp(kNoTimestamp); + + // Test hook for to specify a custom buffer duration. + decoder_buffer->set_duration(buffer_duration_); + + std::move(read_cb).Run(DemuxerStream::kOk, decoder_buffer); } ~VideoRendererImplTest() override = default; @@ -132,7 +151,7 @@ class VideoRendererImplTest : public testing::Test { } void CallInitialize(MockDemuxerStream* demuxer_stream, - const PipelineStatusCB& status_cb, + PipelineStatusCallback status_cb, bool low_delay, bool expect_success) { if (low_delay) @@ -144,7 +163,7 @@ class VideoRendererImplTest : public testing::Test { demuxer_stream, nullptr, &mock_cb_, base::BindRepeating(&WallClockTimeSource::GetWallClockTimes, base::Unretained(&time_source_)), - status_cb); + std::move(status_cb)); } void StartPlayingFrom(int milliseconds) { @@ -221,10 +240,11 @@ class VideoRendererImplTest : public testing::Test { SCOPED_TRACE(base::StringPrintf("WaitForError(%d)", expected)); WaitableMessageLoopEvent event; - PipelineStatusCB error_cb = event.GetPipelineStatusCB(); + PipelineStatusCallback error_cb = event.GetPipelineStatusCB(); EXPECT_CALL(mock_cb_, OnError(_)) - .WillOnce(Invoke( - [error_cb](PipelineStatus status) { error_cb.Run(status); })); + .WillOnce(Invoke([cb = &error_cb](PipelineStatus status) { + std::move(*cb).Run(status); + })); event.RunAndWaitForStatus(expected); } @@ -232,7 +252,8 @@ class VideoRendererImplTest : public testing::Test { SCOPED_TRACE("WaitForEnded()"); WaitableMessageLoopEvent event; - EXPECT_CALL(mock_cb_, OnEnded()).WillOnce(RunClosure(event.GetClosure())); + EXPECT_CALL(mock_cb_, OnEnded()) + .WillOnce(RunOnceClosure(event.GetClosure())); event.RunAndWait(); } @@ -284,6 +305,8 @@ class VideoRendererImplTest : public testing::Test { FROM_HERE, base::BindOnce(std::move(decode_cb_), DecodeStatus::OK)); } + bool HasQueuedFrames() const { return decode_results_.size() > 0; } + void AdvanceWallclockTimeInMs(int time_ms) { EXPECT_TRUE( task_environment_.GetMainThreadTaskRunner()->BelongsToCurrentThread()); @@ -328,6 +351,9 @@ class VideoRendererImplTest : public testing::Test { WallClockTimeSource time_source_; + // Duration set on DecoderBuffers. See MakeDecoderBuffer(). + base::TimeDelta buffer_duration_; + private: void DecodeRequested(scoped_refptr<DecoderBuffer> buffer, VideoDecoder::DecodeCB& decode_cb) { @@ -413,9 +439,13 @@ TEST_F(VideoRendererImplTest, InitializeAndEndOfStream) { { SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); WaitableMessageLoopEvent event; - EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) - .WillOnce(RunClosure(event.GetClosure())); - EXPECT_CALL(mock_cb_, OnEnded()); + { + // Buffering state changes must happen before end of stream. + testing::InSequence in_sequence; + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) + .WillOnce(RunOnceClosure(event.GetClosure())); + EXPECT_CALL(mock_cb_, OnEnded()); + } SatisfyPendingDecodeWithEndOfStream(); event.RunAndWait(); } @@ -425,6 +455,37 @@ TEST_F(VideoRendererImplTest, InitializeAndEndOfStream) { Destroy(); } +TEST_F(VideoRendererImplTest, InitializeAndEndOfStreamOneStaleFrame) { + Initialize(); + StartPlayingFrom(10000); + QueueFrames("0"); + QueueFrame(DecodeStatus::OK, VideoFrame::CreateEOSFrame()); + WaitForPendingDecode(); + { + SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); + WaitableMessageLoopEvent event; + + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + + { + // Buffering state changes must happen before end of stream. + testing::InSequence in_sequence; + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) + .WillOnce(RunOnceClosure(event.GetClosure())); + EXPECT_CALL(mock_cb_, OnEnded()); + } + SatisfyPendingDecode(); + event.RunAndWait(); + } + // Firing a time state changed to true should be ignored... + renderer_->OnTimeProgressing(); + EXPECT_FALSE(null_video_sink_->is_started()); + Destroy(); +} + TEST_F(VideoRendererImplTest, ReinitializeForAnotherStream) { Initialize(); StartPlayingFrom(0); @@ -479,10 +540,10 @@ TEST_F(VideoRendererImplTest, FlushWithNothingBuffered) { // we should be able to call other renderer methods from the Flush callback. static void VideoRendererImplTest_FlushDoneCB(VideoRendererImplTest* test, VideoRenderer* renderer, - const base::Closure& success_cb) { + base::OnceClosure success_cb) { test->QueueFrames("0 10 20 30"); renderer->StartPlayingFrom(base::TimeDelta::FromSeconds(0)); - success_cb.Run(); + std::move(success_cb).Run(); } TEST_F(VideoRendererImplTest, FlushCallbackNoLock) { @@ -556,6 +617,7 @@ TEST_F(VideoRendererImplTest, StartPlayingFrom_RightBefore) { EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>(100))); StartPlayingFrom(59); Destroy(); } @@ -569,6 +631,7 @@ TEST_F(VideoRendererImplTest, StartPlayingFrom_RightAfter) { EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>(100))); StartPlayingFrom(61); Destroy(); } @@ -598,7 +661,7 @@ TEST_F(VideoRendererImplTest, StartPlayingFrom_LowDelay) { WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(20))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(20); event.RunAndWait(); @@ -638,7 +701,7 @@ TEST_F(VideoRendererImplTest, RenderingStopsAfterFirstFrame) { WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); StartPlayingFrom(0); EXPECT_TRUE(IsReadPending()); @@ -694,7 +757,7 @@ TEST_F(VideoRendererImplTest, RenderingStartedThenStopped) { { WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)) .Times(4) .WillRepeatedly(SaveArg<0>(&last_pipeline_statistics)); @@ -765,7 +828,7 @@ TEST_F(VideoRendererImplTest, UnderflowEvictionBeforeEOS) { SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); EXPECT_CALL(mock_cb_, FrameReceived(_)).Times(AnyNumber()); EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); @@ -778,7 +841,7 @@ TEST_F(VideoRendererImplTest, UnderflowEvictionBeforeEOS) { SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING"); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, _)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); renderer_->OnTimeProgressing(); time_source_.StartTicking(); // Jump time far enough forward that no frames are valid. @@ -807,7 +870,7 @@ TEST_F(VideoRendererImplTest, UnderflowEvictionWhileHaveEnough) { SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); EXPECT_CALL(mock_cb_, FrameReceived(_)).Times(AnyNumber()); EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); @@ -825,7 +888,7 @@ TEST_F(VideoRendererImplTest, UnderflowEvictionWhileHaveEnough) { AdvanceTimeInMs(1000); renderer_->OnTimeProgressing(); EXPECT_CALL(mock_cb_, FrameReceived(_)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); event.RunAndWait(); ASSERT_EQ(renderer_->effective_frames_queued_for_testing(), 0u); } @@ -835,7 +898,7 @@ TEST_F(VideoRendererImplTest, UnderflowEvictionWhileHaveEnough) { SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING"); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, _)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); renderer_->OnTimeStopped(); event.RunAndWait(); } @@ -850,7 +913,7 @@ TEST_F(VideoRendererImplTest, StartPlayingFromThenFlushThenEOS) { WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); @@ -897,7 +960,7 @@ TEST_F(VideoRendererImplTest, FramesAreNotExpiredDuringPreroll) { WaitableMessageLoopEvent event; // Frame "10" should not have been expired. EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(10))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(10); event.RunAndWait(); @@ -969,7 +1032,7 @@ TEST_F(VideoRendererImplTest, NaturalSizeChange) { EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(larger_size)); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(10))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(10); event.RunAndWait(); } @@ -977,7 +1040,7 @@ TEST_F(VideoRendererImplTest, NaturalSizeChange) { // Called is not fired because frame size does not change. WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(20))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(10); event.RunAndWait(); } @@ -986,7 +1049,7 @@ TEST_F(VideoRendererImplTest, NaturalSizeChange) { EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(initial_size)); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(30))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(10); event.RunAndWait(); } @@ -1034,7 +1097,7 @@ TEST_F(VideoRendererImplTest, OpacityChange) { // Callback is not fired because opacity does not change. WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(10))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(10); event.RunAndWait(); } @@ -1043,7 +1106,7 @@ TEST_F(VideoRendererImplTest, OpacityChange) { EXPECT_CALL(mock_cb_, OnVideoOpacityChange(true)); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(20))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(10); event.RunAndWait(); } @@ -1051,7 +1114,7 @@ TEST_F(VideoRendererImplTest, OpacityChange) { // Callback is not fired because opacity does not change. WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(30))) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); AdvanceTimeInMs(10); event.RunAndWait(); } @@ -1059,6 +1122,40 @@ TEST_F(VideoRendererImplTest, OpacityChange) { Destroy(); } +TEST_F(VideoRendererImplTest, VideoFrameRateChange) { + Initialize(); + + EXPECT_CALL(mock_cb_, FrameReceived(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(_, _)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + + // Send 50fps frames first. + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>(50))); + QueueFrames("0 20 40 60 80 100 120 140 160 180 200"); + QueueFrames("220 240 260 280 300 320 340 360 380 400"); + + // Also queue some frames that aren't at 50fps, so that we get an unknown fps. + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>())); + QueueFrames("500 600"); + + // Drain everything. + StartPlayingFrom(0); + renderer_->OnTimeProgressing(); + time_source_.StartTicking(); + // Send in all the frames we queued. + while (HasQueuedFrames()) { + AdvanceTimeInMs(20); + AdvanceWallclockTimeInMs(20); + // This runs the sink callbacks to consume frames. + task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(20)); + base::RunLoop().RunUntilIdle(); + } + + Destroy(); +} + class VideoRendererImplAsyncAddFrameReadyTest : public VideoRendererImplTest { public: void InitializeWithMockGpuMemoryBufferVideoFramePool() { @@ -1107,7 +1204,13 @@ TEST_F(VideoRendererImplAsyncAddFrameReadyTest, WeakFactoryDiscardsOneFrame) { Destroy(); } -enum class UnderflowTestType { NORMAL, LOW_DELAY, CANT_READ_WITHOUT_STALLING }; +enum class UnderflowTestType { + // Renderer will require a default amount of buffering to reach HAVE_ENOUGH. + NORMAL, + // Both of these require only a single frame to reach HAVE_ENOUGH. + LOW_DELAY, + CANT_READ_WITHOUT_STALLING +}; class UnderflowTest : public VideoRendererImplTest, @@ -1116,13 +1219,13 @@ class UnderflowTest protected: void SetUp() override { std::tie(test_type, underflow_type) = GetParam(); } - void BasicUnderflowTest(UnderflowTestType type, - BufferingStateChangeReason underflow_type) { - InitializeWithLowDelay(type == UnderflowTestType::LOW_DELAY); - if (type == UnderflowTestType::CANT_READ_WITHOUT_STALLING) + void TestBufferToHaveEnoughThenUnderflow() { + InitializeWithLowDelay(test_type == UnderflowTestType::LOW_DELAY); + + if (test_type == UnderflowTestType::CANT_READ_WITHOUT_STALLING) ON_CALL(*decoder_, CanReadWithoutStalling()).WillByDefault(Return(false)); - QueueFrames("0 30 60 90"); + QueueFrames("0 20 40 60"); { WaitableMessageLoopEvent event; @@ -1130,7 +1233,7 @@ class UnderflowTest EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, BUFFERING_CHANGE_REASON_UNKNOWN)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); @@ -1139,6 +1242,8 @@ class UnderflowTest Mock::VerifyAndClearExpectations(&mock_cb_); } + // Start playing. + time_source_.StartTicking(); renderer_->OnTimeProgressing(); // Advance time slightly, but enough to exceed the duration of the last @@ -1147,18 +1252,13 @@ class UnderflowTest { SCOPED_TRACE("Waiting for frame drops"); WaitableMessageLoopEvent event; + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(20))).Times(0); - // Note: Starting the TimeSource will cause the old VideoRendererImpl to - // start rendering frames on its own thread, so the first frame may be - // received. - time_source_.StartTicking(); - EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(30))).Times(0); - - EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(60))).Times(0); - EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(90))) - .WillOnce(RunClosure(event.GetClosure())); + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(40))).Times(0); + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(60))) + .WillOnce(RunOnceClosure(event.GetClosure())); EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); - AdvanceTimeInMs(91); + AdvanceTimeInMs(61); event.RunAndWait(); Mock::VerifyAndClearExpectations(&mock_cb_); @@ -1173,70 +1273,8 @@ class UnderflowTest .WillOnce(Return(underflow_type == DEMUXER_UNDERFLOW)); EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, underflow_type)) - .WillOnce(RunClosure(event.GetClosure())); - AdvanceTimeInMs(30); - event.RunAndWait(); - Mock::VerifyAndClearExpectations(&mock_cb_); - } - - // Simulate delayed buffering state callbacks. - renderer_->OnTimeStopped(); - renderer_->OnTimeProgressing(); - - // Receiving end of stream should signal having enough. - { - SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); - WaitableMessageLoopEvent event; - EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); - EXPECT_CALL(mock_cb_, - OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, - BUFFERING_CHANGE_REASON_UNKNOWN)) - .WillOnce(RunClosure(event.GetClosure())); - EXPECT_CALL(mock_cb_, OnEnded()); - SatisfyPendingDecodeWithEndOfStream(); - event.RunAndWait(); - } - - Destroy(); - } - - void UnderflowRecoveryTest(UnderflowTestType type, - BufferingStateChangeReason underflow_type) { - InitializeWithLowDelay(type == UnderflowTestType::LOW_DELAY); - if (type == UnderflowTestType::CANT_READ_WITHOUT_STALLING) - ON_CALL(*decoder_, CanReadWithoutStalling()).WillByDefault(Return(false)); - - QueueFrames("0 20 40 60"); - { - WaitableMessageLoopEvent event; - EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); - EXPECT_CALL(mock_cb_, - OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, - BUFFERING_CHANGE_REASON_UNKNOWN)) - .WillOnce(RunClosure(event.GetClosure())); - EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); - EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); - EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); - StartPlayingFrom(0); - event.RunAndWait(); - Mock::VerifyAndClearExpectations(&mock_cb_); - } - - renderer_->OnTimeProgressing(); - time_source_.StartTicking(); - - // Advance time, this should cause have nothing to be signaled. - { - SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING"); - WaitableMessageLoopEvent event; - EXPECT_CALL(demuxer_stream_, IsReadPending()) - .WillOnce(Return(underflow_type == DEMUXER_UNDERFLOW)); - EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, - underflow_type)) - .WillOnce(RunClosure(event.GetClosure())); - EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(60))).Times(1); - EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); - AdvanceTimeInMs(79); + .WillOnce(RunOnceClosure(event.GetClosure())); + AdvanceTimeInMs(18); event.RunAndWait(); Mock::VerifyAndClearExpectations(&mock_cb_); } @@ -1247,42 +1285,86 @@ class UnderflowTest EXPECT_EQ(0u, renderer_->frames_queued_for_testing()); ASSERT_TRUE(IsReadPending()); - // Queue some frames, satisfy reads, and make sure expired frames are gone - // when the renderer paints the first frame. - { - SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); - WaitableMessageLoopEvent event; - EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(80))).Times(1); - EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); - EXPECT_CALL(mock_cb_, - OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, - BUFFERING_CHANGE_REASON_UNKNOWN)) - .WillOnce(RunClosure(event.GetClosure())); - - // Note: In the normal underflow case we queue 5 frames here instead of - // four since the underflow increases the number of required frames to - // reach the have enough state. - if (type == UnderflowTestType::NORMAL) - QueueFrames("80 100 120 140 160"); - else - QueueFrames("40 60 80 90 100"); - SatisfyPendingDecode(); - event.RunAndWait(); + // Stopping time signals a confirmed underflow to VRI. Verify updates to + // buffering limits. + switch (test_type) { + // In the normal and cant_read modes, min and max buffered frames should + // always be equal, and both should increase upon underflow. + case UnderflowTestType::NORMAL: + case UnderflowTestType::CANT_READ_WITHOUT_STALLING: + EXPECT_EQ(renderer_->min_buffered_frames_for_testing(), + limits::kMaxVideoFrames + 1); + EXPECT_EQ(renderer_->max_buffered_frames_for_testing(), + limits::kMaxVideoFrames + 1); + break; + // In low_delay mode only the max should increase while min remains 1. + case UnderflowTestType::LOW_DELAY: + EXPECT_EQ(renderer_->min_buffered_frames_for_testing(), 1); + EXPECT_EQ(renderer_->max_buffered_frames_for_testing(), + limits::kMaxVideoFrames + 1); + break; } - - Destroy(); } UnderflowTestType test_type; BufferingStateChangeReason underflow_type; }; -TEST_P(UnderflowTest, BasicUnderflowTest) { - BasicUnderflowTest(test_type, underflow_type); +TEST_P(UnderflowTest, UnderflowAndEosTest) { + TestBufferToHaveEnoughThenUnderflow(); + + // Receiving end of stream should signal having enough. + { + SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); + WaitableMessageLoopEvent event; + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, + OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, + BUFFERING_CHANGE_REASON_UNKNOWN)) + .WillOnce(RunOnceClosure(event.GetClosure())); + EXPECT_CALL(mock_cb_, OnEnded()); + SatisfyPendingDecodeWithEndOfStream(); + event.RunAndWait(); + } + + Destroy(); } -TEST_P(UnderflowTest, UnderflowRecoveryTest) { - UnderflowRecoveryTest(test_type, underflow_type); +TEST_P(UnderflowTest, UnderflowAndRecoverTest) { + TestBufferToHaveEnoughThenUnderflow(); + + // Queue some frames, satisfy reads, and make sure expired frames are gone + // when the renderer paints the first frame. + { + SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH"); + WaitableMessageLoopEvent event; + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(80))).Times(1); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, + OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, + BUFFERING_CHANGE_REASON_UNKNOWN)) + .WillOnce(RunOnceClosure(event.GetClosure())); + + switch (test_type) { + // In the normal underflow case we queue 5 frames here instead of four + // since the underflow increases the number of required frames to reach + // the have enough state. + case UnderflowTestType::NORMAL: + QueueFrames("80 100 120 140 160"); + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>(50))); + break; + // In either of these modes the HAVE_ENOUGH transition should still + // occur with a single frame. + case UnderflowTestType::LOW_DELAY: + case UnderflowTestType::CANT_READ_WITHOUT_STALLING: + QueueFrames("80"); + break; + } + SatisfyPendingDecode(); + event.RunAndWait(); + } + + Destroy(); } INSTANTIATE_TEST_SUITE_P( @@ -1293,4 +1375,333 @@ INSTANTIATE_TEST_SUITE_P( UnderflowTestType::CANT_READ_WITHOUT_STALLING), Values(DEMUXER_UNDERFLOW, DECODER_UNDERFLOW))); +class VideoRendererLatencyHintTest : public VideoRendererImplTest { + public: + void VerifyDefaultRebufferingBehavior(int start_playing_from) { + // Keep it simple. Only call this if you're starting from empty. + DCHECK_EQ(renderer_->effective_frames_queued_for_testing(), 0u); + + // Initial frames should trigger various callbacks. + EXPECT_CALL(mock_cb_, + FrameReceived(HasTimestampMatcher(start_playing_from))); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(_)).Times(AnyNumber()); + + // Queue 3 frames, 20 msec apart. Stop 1 shy of min_buffered_frames_. + ASSERT_EQ(renderer_->min_buffered_frames_for_testing(), 4); + int frame_time = start_playing_from; + for (int i = 0; i < 3; i++) { + QueueFrames(base::NumberToString(frame_time)); + frame_time += 20; + } + + // Verify no transition to HAVE_ENOUGH since 3 < |min_buffered_frames_| + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) + .Times(0); + + StartPlayingFrom(start_playing_from); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(renderer_->effective_frames_queued_for_testing(), 3u); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Queuing one extra frame should trigger the transition. + QueueFrames(base::NumberToString(frame_time)); + SatisfyPendingDecode(); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&mock_cb_); + } +}; + +// Test default HaveEnough transition when no latency hint is set. +TEST_F(VideoRendererLatencyHintTest, HaveEnough_NoLatencyHint) { + Initialize(); + VerifyDefaultRebufferingBehavior(0); + Destroy(); +} + +// Test early HaveEnough transition when low latency hint is set. +TEST_F(VideoRendererLatencyHintTest, HaveEnough_LowLatencyHint) { + Initialize(); + + // Set latencyHint to bare minimum. + renderer_->SetLatencyHint(base::TimeDelta()); + + // Initial frames should trigger various callbacks. + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)); + + // Only 1 frame should be needed to trigger have enough. + ASSERT_EQ(renderer_->min_buffered_frames_for_testing(), 1); + QueueFrames("0"); + + StartPlayingFrom(0); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(renderer_->effective_frames_queued_for_testing(), 1u); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Verify latency hint doesn't reduce our ability to buffer beyond the + // 1-frame HAVE_ENOUGH (i.e. don't throttle decoding in the name of latency). + EXPECT_EQ(renderer_->max_buffered_frames_for_testing(), 4); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(3); + QueueFrames("10 20 30"); + WaitForPendingDecode(); + SatisfyPendingDecode(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(renderer_->frames_queued_for_testing(), 4u); + + // Unset latencyHint, to verify default behavior. + renderer_->SetLatencyHint(base::nullopt); + + // Flush to return to clean slate. + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, _)); + Flush(); + + VerifyDefaultRebufferingBehavior(1000); + + Destroy(); +} + +// Test late HaveEnough transition when high latency hint is set. +TEST_F(VideoRendererLatencyHintTest, HaveEnough_HighLatencyHint) { + Initialize(); + + // We must provide a |buffer_duration_| for the latencyHint to take effect + // immediately. The VideoRendererAlgorithm will eventually provide a PTS-delta + // duration, but not until after we've started rendering. + buffer_duration_ = base::TimeDelta::FromMilliseconds(30); + + // Set latencyHint to a large value. + renderer_->SetLatencyHint(base::TimeDelta::FromMilliseconds(400)); + + // Initial frames should trigger various callbacks. + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + + // Queue 12 frames, each 30 ms apart. At this framerate, 400ms rounds to 13 + // frames, so 12 frames should be 1 shy of the HaveEnough threshold. + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>(33))); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) + .Times(0); + QueueFrames("0 30 60 90 120 150 180 210 240 270 300 330"); + + StartPlayingFrom(0); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(renderer_->min_buffered_frames_for_testing(), 13); + EXPECT_EQ(renderer_->effective_frames_queued_for_testing(), 12u); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Queue 1 additional frame and verify HaveEnough threshold is reached. + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)); + QueueFrames("360"); + SatisfyPendingDecode(); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Unset latencyHint, to verify default behavior. + renderer_->SetLatencyHint(base::nullopt); + + // Flush to return to clean slate. + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, _)); + Flush(); + Mock::VerifyAndClearExpectations(&mock_cb_); + + VerifyDefaultRebufferingBehavior(1000); + + Destroy(); +} + +// Test updates to buffering limits upon underflow when latency hint set. +TEST_F(VideoRendererLatencyHintTest, + LatencyHintUnderflowUpdatesMaxBufferingLimit) { + // Enable low delay mode. Low delay mode is tested separately. + InitializeWithLowDelay(true); + EXPECT_EQ(renderer_->min_buffered_frames_for_testing(), 1); + + // We must provide a |buffer_duration_| for the latencyHint to take effect + // immediately. The VideoRendererAlgorithm will eventually provide a PTS-delta + // duration, but not until after we've started rendering. + buffer_duration_ = base::TimeDelta::FromMilliseconds(30); + + // Set latency hint to a medium value. + renderer_->SetLatencyHint(base::TimeDelta::FromMilliseconds(200)); + + // Queue up enough frames to trigger HAVE_ENOUGH. Each frame is 30 ms apart. + // At this spacing, 200ms rounds to 7 frames. + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>(33))); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)); + QueueFrames("0 30 60 90 120 150 180"); + StartPlayingFrom(0); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(renderer_->min_buffered_frames_for_testing(), 7); + EXPECT_EQ(renderer_->effective_frames_queued_for_testing(), 7u); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Advance time to trigger HAVE_NOTHING (underflow). + { + SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING"); + WaitableMessageLoopEvent event; + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(180))); + EXPECT_CALL(demuxer_stream_, IsReadPending()).WillOnce(Return(true)); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, + DEMUXER_UNDERFLOW)) + .WillOnce(RunOnceClosure(event.GetClosure())); + renderer_->OnTimeProgressing(); + time_source_.StartTicking(); + AdvanceTimeInMs(300); + event.RunAndWait(); + Mock::VerifyAndClearExpectations(&mock_cb_); + } + + // Simulate delayed buffering state callbacks. + time_source_.StopTicking(); + renderer_->OnTimeStopped(); + + // When latency hint set the max should increase while min remains steady + // (user controls the min via hint). + EXPECT_EQ(renderer_->min_buffered_frames_for_testing(), 7); + EXPECT_EQ(renderer_->max_buffered_frames_for_testing(), 7 + 1); + + Destroy(); +} + +// Test that latency hint overrides low delay mode. +TEST_F(VideoRendererLatencyHintTest, LatencyHintOverridesLowDelay) { + // Enable low delay mode. Low delay mode is tested separately. + InitializeWithLowDelay(true); + EXPECT_EQ(renderer_->min_buffered_frames_for_testing(), 1); + + // We must provide a |buffer_duration_| for the latencyHint to take effect + // immediately. The VideoRendererAlgorithm will eventually provide a PTS-delta + // duration, but not until after we've started rendering. + buffer_duration_ = base::TimeDelta::FromMilliseconds(30); + + // Set latency hint to a medium value. + renderer_->SetLatencyHint(base::TimeDelta::FromMilliseconds(200)); + + // Initial frames should trigger various callbacks. + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + + // Queue 6 frames, each 30 ms apart. At this spacing, 200ms rounds to + // 7 frames, so 6 frames should be 1 shy of the HaveEnough threshold. Verify + // that HAVE_ENOUGH is not triggered in spite of being initialized with low + // delay mode. + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(base::Optional<int>(33))); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)) + .Times(0); + QueueFrames("0 30 60 90 120 150"); + StartPlayingFrom(0); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(renderer_->min_buffered_frames_for_testing(), 7); + EXPECT_EQ(renderer_->effective_frames_queued_for_testing(), 6u); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Queue 1 additional frame and verify HaveEnough threshold is reached. + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)); + QueueFrames("180"); + SatisfyPendingDecode(); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Unset latencyHint, to verify default behavior. NOTE: low delay mode is not + // restored when latency hint unset. + renderer_->SetLatencyHint(base::nullopt); + + // Flush to return to clean slate. + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, _)); + Flush(); + Mock::VerifyAndClearExpectations(&mock_cb_); + + VerifyDefaultRebufferingBehavior(1000); + + Destroy(); +} + +// Test that !CanReadWithoutStalling() overrides latency hint. +TEST_F(VideoRendererLatencyHintTest, + CantReadWithoutStallingOverridesLatencyHint) { + Initialize(); + + // Let decoder indicate that it CANT read without stalling, meaning we should + // enter HAVE_ENOUGH with just one effective frame (waiting for more frames + // will stall the decoder). + ON_CALL(*decoder_, CanReadWithoutStalling()).WillByDefault(Return(false)); + + // We must provide a |buffer_duration_| for the latencyHint to take effect + // immediately. The VideoRendererAlgorithm will eventually provide a PTS-delta + // duration, but not until after we've started rendering. + buffer_duration_ = base::TimeDelta::FromMilliseconds(30); + + // Set latency hint to a medium value. At a spacing of 30ms this would set + // the HAVE_ENOUGH threshold to 4 frames. + renderer_->SetLatencyHint(base::TimeDelta::FromMilliseconds(200)); + + // Initial frames should trigger various callbacks. + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(0))); + EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + + // Queue 1 frame. This is well short of what the latency hint would require, + // but we CANT READ WITHOUT STALLING, so expect a transition to HAVE_ENOUGH + // after just 1 frame. + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)); + QueueFrames("0"); + StartPlayingFrom(0); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(renderer_->min_buffered_frames_for_testing(), 7); + EXPECT_EQ(renderer_->effective_frames_queued_for_testing(), 1u); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Queue some additional frames, verify buffering state holds at HAVE_ENOUGH. + QueueFrames("30 60 90 120"); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnBufferingStateChange(_, _)).Times(0); + // SatisfyPendingDecode(); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Unset latency hint to verify 1-frame HAVE_ENOUGH threshold is maintained. + renderer_->SetLatencyHint(base::nullopt); + + // Flush to return to clean slate. + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, _)); + Flush(); + Mock::VerifyAndClearExpectations(&mock_cb_); + + // Expect HAVE_ENOUGH (and various other callbacks) again. + EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _)); + EXPECT_CALL(mock_cb_, FrameReceived(HasTimestampMatcher(1000))); + EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber()); + EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(_)).Times(AnyNumber()); + + // Queue 1 frame. + QueueFrames("1000"); + StartPlayingFrom(1000); + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(renderer_->min_buffered_frames_for_testing(), 4); + EXPECT_EQ(renderer_->effective_frames_queued_for_testing(), 1u); + Mock::VerifyAndClearExpectations(&mock_cb_); + + Destroy(); +} + } // namespace media |