summaryrefslogtreecommitdiff
path: root/chromium/media/renderers/video_renderer_impl_unittest.cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/media/renderers/video_renderer_impl_unittest.cc
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-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.cc697
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