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
3 // found in the LICENSE file.
8 #include "base/callback.h"
9 #include "base/callback_helpers.h"
10 #include "base/debug/stack_trace.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/synchronization/lock.h"
17 #include "base/test/simple_test_tick_clock.h"
18 #include "media/base/data_buffer.h"
19 #include "media/base/gmock_callback_support.h"
20 #include "media/base/limits.h"
21 #include "media/base/mock_filters.h"
22 #include "media/base/test_helpers.h"
23 #include "media/base/video_frame.h"
24 #include "media/renderers/video_renderer_impl.h"
25 #include "testing/gtest/include/gtest/gtest.h"
28 using ::testing::AnyNumber
;
29 using ::testing::Invoke
;
30 using ::testing::Mock
;
31 using ::testing::NiceMock
;
32 using ::testing::Return
;
33 using ::testing::SaveArg
;
34 using ::testing::StrictMock
;
38 ACTION_P(RunClosure
, closure
) {
42 MATCHER_P(HasTimestamp
, ms
, "") {
43 *result_listener
<< "has timestamp " << arg
->timestamp().InMilliseconds();
44 return arg
->timestamp().InMilliseconds() == ms
;
47 class VideoRendererImplTest
: public ::testing::Test
{
49 VideoRendererImplTest()
50 : tick_clock_(new base::SimpleTestTickClock()),
51 decoder_(new MockVideoDecoder()),
52 demuxer_stream_(DemuxerStream::VIDEO
) {
53 ScopedVector
<VideoDecoder
> decoders
;
54 decoders
.push_back(decoder_
);
56 renderer_
.reset(new VideoRendererImpl(message_loop_
.message_loop_proxy(),
58 decoders
.Pass(), true,
60 renderer_
->SetTickClockForTesting(scoped_ptr
<base::TickClock
>(tick_clock_
));
62 // Start wallclock time at a non-zero value.
63 AdvanceWallclockTimeInMs(12345);
65 demuxer_stream_
.set_video_decoder_config(TestVideoConfig::Normal());
67 // We expect these to be called but we don't care how/when.
68 EXPECT_CALL(demuxer_stream_
, Read(_
)).WillRepeatedly(
69 RunCallback
<0>(DemuxerStream::kOk
,
70 scoped_refptr
<DecoderBuffer
>(new DecoderBuffer(0))));
73 virtual ~VideoRendererImplTest() {}
76 InitializeWithLowDelay(false);
79 void InitializeWithLowDelay(bool low_delay
) {
80 // Monitor decodes from the decoder.
81 EXPECT_CALL(*decoder_
, Decode(_
, _
))
82 .WillRepeatedly(Invoke(this, &VideoRendererImplTest::DecodeRequested
));
84 EXPECT_CALL(*decoder_
, Reset(_
))
85 .WillRepeatedly(Invoke(this, &VideoRendererImplTest::FlushRequested
));
87 // Initialize, we shouldn't have any reads.
88 InitializeRenderer(PIPELINE_OK
, low_delay
);
91 void InitializeRenderer(PipelineStatus expected
, bool low_delay
) {
92 SCOPED_TRACE(base::StringPrintf("InitializeRenderer(%d)", expected
));
93 WaitableMessageLoopEvent event
;
94 CallInitialize(event
.GetPipelineStatusCB(), low_delay
, expected
);
95 event
.RunAndWaitForStatus(expected
);
98 void CallInitialize(const PipelineStatusCB
& status_cb
,
100 PipelineStatus decoder_status
) {
102 demuxer_stream_
.set_liveness(DemuxerStream::LIVENESS_LIVE
);
103 EXPECT_CALL(*decoder_
, Initialize(_
, _
, _
, _
)).WillOnce(
104 DoAll(SaveArg
<3>(&output_cb_
), RunCallback
<2>(decoder_status
)));
105 EXPECT_CALL(*this, OnWaitingForDecryptionKey()).Times(0);
106 renderer_
->Initialize(
107 &demuxer_stream_
, status_cb
, media::SetDecryptorReadyCB(),
108 base::Bind(&VideoRendererImplTest::OnStatisticsUpdate
,
109 base::Unretained(this)),
110 base::Bind(&StrictMock
<MockCB
>::BufferingStateChange
,
111 base::Unretained(&mock_cb_
)),
112 ended_event_
.GetClosure(), error_event_
.GetPipelineStatusCB(),
113 base::Bind(&VideoRendererImplTest::GetWallClockTime
,
114 base::Unretained(this)),
115 base::Bind(&VideoRendererImplTest::OnWaitingForDecryptionKey
,
116 base::Unretained(this)));
119 void StartPlayingFrom(int milliseconds
) {
120 SCOPED_TRACE(base::StringPrintf("StartPlayingFrom(%d)", milliseconds
));
121 renderer_
->StartPlayingFrom(
122 base::TimeDelta::FromMilliseconds(milliseconds
));
123 message_loop_
.RunUntilIdle();
127 SCOPED_TRACE("Flush()");
128 WaitableMessageLoopEvent event
;
129 renderer_
->Flush(event
.GetClosure());
134 SCOPED_TRACE("Destroy()");
136 message_loop_
.RunUntilIdle();
139 // Parses a string representation of video frames and generates corresponding
140 // VideoFrame objects in |decode_results_|.
143 // nn - Queue a decoder buffer with timestamp nn * 1000us
144 // abort - Queue an aborted read
145 // error - Queue a decoder error
148 // A clip that is four frames long: "0 10 20 30"
149 // A clip that has a decode error: "60 70 error"
150 void QueueFrames(const std::string
& str
) {
151 std::vector
<std::string
> tokens
;
152 base::SplitString(str
, ' ', &tokens
);
153 for (size_t i
= 0; i
< tokens
.size(); ++i
) {
154 if (tokens
[i
] == "abort") {
155 scoped_refptr
<VideoFrame
> null_frame
;
156 decode_results_
.push_back(
157 std::make_pair(VideoDecoder::kAborted
, null_frame
));
161 if (tokens
[i
] == "error") {
162 scoped_refptr
<VideoFrame
> null_frame
;
163 decode_results_
.push_back(
164 std::make_pair(VideoDecoder::kDecodeError
, null_frame
));
168 int timestamp_in_ms
= 0;
169 if (base::StringToInt(tokens
[i
], ×tamp_in_ms
)) {
170 gfx::Size natural_size
= TestVideoConfig::NormalCodedSize();
171 scoped_refptr
<VideoFrame
> frame
= VideoFrame::CreateFrame(
174 gfx::Rect(natural_size
),
176 base::TimeDelta::FromMilliseconds(timestamp_in_ms
));
177 decode_results_
.push_back(std::make_pair(VideoDecoder::kOk
, frame
));
181 CHECK(false) << "Unrecognized decoder buffer token: " << tokens
[i
];
185 bool IsReadPending() {
186 return !decode_cb_
.is_null();
189 void WaitForError(PipelineStatus expected
) {
190 SCOPED_TRACE(base::StringPrintf("WaitForError(%d)", expected
));
191 error_event_
.RunAndWaitForStatus(expected
);
194 void WaitForEnded() {
195 SCOPED_TRACE("WaitForEnded()");
196 ended_event_
.RunAndWait();
199 void WaitForPendingRead() {
200 SCOPED_TRACE("WaitForPendingRead()");
201 if (!decode_cb_
.is_null())
204 DCHECK(wait_for_pending_decode_cb_
.is_null());
206 WaitableMessageLoopEvent event
;
207 wait_for_pending_decode_cb_
= event
.GetClosure();
210 DCHECK(!decode_cb_
.is_null());
211 DCHECK(wait_for_pending_decode_cb_
.is_null());
214 void SatisfyPendingRead() {
215 CHECK(!decode_cb_
.is_null());
216 CHECK(!decode_results_
.empty());
218 // Post tasks for OutputCB and DecodeCB.
219 scoped_refptr
<VideoFrame
> frame
= decode_results_
.front().second
;
221 message_loop_
.PostTask(FROM_HERE
, base::Bind(output_cb_
, frame
));
222 message_loop_
.PostTask(
223 FROM_HERE
, base::Bind(base::ResetAndReturn(&decode_cb_
),
224 decode_results_
.front().first
));
225 decode_results_
.pop_front();
228 void SatisfyPendingReadWithEndOfStream() {
229 DCHECK(!decode_cb_
.is_null());
231 // Return EOS buffer to trigger EOS frame.
232 EXPECT_CALL(demuxer_stream_
, Read(_
))
233 .WillOnce(RunCallback
<0>(DemuxerStream::kOk
,
234 DecoderBuffer::CreateEOSBuffer()));
236 // Satify pending |decode_cb_| to trigger a new DemuxerStream::Read().
237 message_loop_
.PostTask(
239 base::Bind(base::ResetAndReturn(&decode_cb_
), VideoDecoder::kOk
));
241 WaitForPendingRead();
243 message_loop_
.PostTask(
245 base::Bind(base::ResetAndReturn(&decode_cb_
), VideoDecoder::kOk
));
248 void AdvanceWallclockTimeInMs(int time_ms
) {
249 DCHECK_EQ(&message_loop_
, base::MessageLoop::current());
250 base::AutoLock
l(lock_
);
251 tick_clock_
->Advance(base::TimeDelta::FromMilliseconds(time_ms
));
254 void AdvanceTimeInMs(int time_ms
) {
255 DCHECK_EQ(&message_loop_
, base::MessageLoop::current());
256 base::AutoLock
l(lock_
);
257 time_
+= base::TimeDelta::FromMilliseconds(time_ms
);
262 scoped_ptr
<VideoRendererImpl
> renderer_
;
263 base::SimpleTestTickClock
* tick_clock_
; // Owned by |renderer_|.
264 MockVideoDecoder
* decoder_
; // Owned by |renderer_|.
265 NiceMock
<MockDemuxerStream
> demuxer_stream_
;
267 // Use StrictMock<T> to catch missing/extra callbacks.
268 // TODO(dalecurtis): Mocks won't be useful for the new rendering path, we'll
269 // need fake callback generators like we have for the audio path.
270 // http://crbug.com/473424
271 class MockCB
: public VideoRendererSink
{
273 MOCK_METHOD1(Start
, void(VideoRendererSink::RenderCallback
*));
274 MOCK_METHOD0(Stop
, void());
275 MOCK_METHOD1(PaintFrameUsingOldRenderingPath
,
276 void(const scoped_refptr
<VideoFrame
>&));
277 MOCK_METHOD1(BufferingStateChange
, void(BufferingState
));
279 StrictMock
<MockCB
> mock_cb_
;
282 base::TimeTicks
GetWallClockTime(base::TimeDelta time
) {
283 base::AutoLock
l(lock_
);
284 return tick_clock_
->NowTicks() + (time
- time_
);
287 void DecodeRequested(const scoped_refptr
<DecoderBuffer
>& buffer
,
288 const VideoDecoder::DecodeCB
& decode_cb
) {
289 DCHECK_EQ(&message_loop_
, base::MessageLoop::current());
290 CHECK(decode_cb_
.is_null());
291 decode_cb_
= decode_cb
;
293 // Wake up WaitForPendingRead() if needed.
294 if (!wait_for_pending_decode_cb_
.is_null())
295 base::ResetAndReturn(&wait_for_pending_decode_cb_
).Run();
297 if (decode_results_
.empty())
300 SatisfyPendingRead();
303 void FlushRequested(const base::Closure
& callback
) {
304 DCHECK_EQ(&message_loop_
, base::MessageLoop::current());
305 decode_results_
.clear();
306 if (!decode_cb_
.is_null()) {
307 QueueFrames("abort");
308 SatisfyPendingRead();
311 message_loop_
.PostTask(FROM_HERE
, callback
);
314 void OnStatisticsUpdate(const PipelineStatistics
& stats
) {}
316 MOCK_METHOD0(OnWaitingForDecryptionKey
, void(void));
318 base::MessageLoop message_loop_
;
320 // Used to protect |time_|.
322 base::TimeDelta time_
;
324 // Used for satisfying reads.
325 VideoDecoder::OutputCB output_cb_
;
326 VideoDecoder::DecodeCB decode_cb_
;
327 base::TimeDelta next_frame_timestamp_
;
329 WaitableMessageLoopEvent error_event_
;
330 WaitableMessageLoopEvent ended_event_
;
332 // Run during DecodeRequested() to unblock WaitForPendingRead().
333 base::Closure wait_for_pending_decode_cb_
;
335 std::deque
<std::pair
<
336 VideoDecoder::Status
, scoped_refptr
<VideoFrame
> > > decode_results_
;
338 DISALLOW_COPY_AND_ASSIGN(VideoRendererImplTest
);
341 TEST_F(VideoRendererImplTest
, DoNothing
) {
342 // Test that creation and deletion doesn't depend on calls to Initialize()
346 TEST_F(VideoRendererImplTest
, DestroyWithoutInitialize
) {
350 TEST_F(VideoRendererImplTest
, Initialize
) {
355 TEST_F(VideoRendererImplTest
, InitializeAndStartPlayingFrom
) {
357 QueueFrames("0 10 20 30");
358 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(0)));
359 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
364 TEST_F(VideoRendererImplTest
, DestroyWhileInitializing
) {
365 CallInitialize(NewExpectedStatusCB(PIPELINE_ERROR_ABORT
), false, PIPELINE_OK
);
369 TEST_F(VideoRendererImplTest
, DestroyWhileFlushing
) {
371 QueueFrames("0 10 20 30");
372 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(0)));
373 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
375 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_NOTHING
));
376 renderer_
->Flush(NewExpectedClosure());
380 TEST_F(VideoRendererImplTest
, Play
) {
382 QueueFrames("0 10 20 30");
383 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(0)));
384 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
389 TEST_F(VideoRendererImplTest
, FlushWithNothingBuffered
) {
393 // We shouldn't expect a buffering state change since we never reached
394 // BUFFERING_HAVE_ENOUGH.
399 TEST_F(VideoRendererImplTest
, DecodeError_Playing
) {
401 QueueFrames("0 10 20 30");
402 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(0)));
403 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
406 QueueFrames("error");
407 SatisfyPendingRead();
408 WaitForError(PIPELINE_ERROR_DECODE
);
412 TEST_F(VideoRendererImplTest
, DecodeError_DuringStartPlayingFrom
) {
414 QueueFrames("error");
419 TEST_F(VideoRendererImplTest
, StartPlayingFrom_Exact
) {
421 QueueFrames("50 60 70 80 90");
423 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(60)));
424 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
425 StartPlayingFrom(60);
429 TEST_F(VideoRendererImplTest
, StartPlayingFrom_RightBefore
) {
431 QueueFrames("50 60 70 80 90");
433 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(50)));
434 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
435 StartPlayingFrom(59);
439 TEST_F(VideoRendererImplTest
, StartPlayingFrom_RightAfter
) {
441 QueueFrames("50 60 70 80 90");
443 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(60)));
444 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
445 StartPlayingFrom(61);
449 TEST_F(VideoRendererImplTest
, StartPlayingFrom_LowDelay
) {
450 // In low-delay mode only one frame is required to finish preroll.
451 InitializeWithLowDelay(true);
454 // Expect some amount of have enough/nothing due to only requiring one frame.
455 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(0)));
456 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
))
458 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_NOTHING
))
463 SatisfyPendingRead();
465 WaitableMessageLoopEvent event
;
466 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(10)))
467 .WillOnce(RunClosure(event
.GetClosure()));
474 // Verify that a late decoder response doesn't break invariants in the renderer.
475 TEST_F(VideoRendererImplTest
, DestroyDuringOutstandingRead
) {
477 QueueFrames("0 10 20 30");
478 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(0)));
479 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
));
482 // Check that there is an outstanding Read() request.
483 EXPECT_TRUE(IsReadPending());
488 TEST_F(VideoRendererImplTest
, VideoDecoder_InitFailure
) {
489 InitializeRenderer(DECODER_ERROR_NOT_SUPPORTED
, false);
493 TEST_F(VideoRendererImplTest
, Underflow
) {
495 QueueFrames("0 10 20 30");
498 WaitableMessageLoopEvent event
;
499 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(0)));
500 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
))
501 .WillOnce(RunClosure(event
.GetClosure()));
504 Mock::VerifyAndClearExpectations(&mock_cb_
);
507 // Advance time slightly, but enough to exceed the duration of the last frame.
508 // Frames should be dropped and we should NOT signal having nothing.
510 SCOPED_TRACE("Waiting for frame drops");
511 WaitableMessageLoopEvent event
;
512 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(10)))
514 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(20)))
516 EXPECT_CALL(mock_cb_
, PaintFrameUsingOldRenderingPath(HasTimestamp(30)))
517 .WillOnce(RunClosure(event
.GetClosure()));
520 Mock::VerifyAndClearExpectations(&mock_cb_
);
523 // Advance time more. Now we should signal having nothing. And put
524 // the last frame up for display.
526 SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING");
527 WaitableMessageLoopEvent event
;
528 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_NOTHING
))
529 .WillOnce(RunClosure(event
.GetClosure()));
530 AdvanceWallclockTimeInMs(9);
532 Mock::VerifyAndClearExpectations(&mock_cb_
);
535 // Receiving end of stream should signal having enough.
537 SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH");
538 WaitableMessageLoopEvent event
;
539 EXPECT_CALL(mock_cb_
, BufferingStateChange(BUFFERING_HAVE_ENOUGH
))
540 .WillOnce(RunClosure(event
.GetClosure()));
541 SatisfyPendingReadWithEndOfStream();