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. 4 5 #include <utility> 6 7 #include "base/bind.h" 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/timer/timer.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/filters/video_renderer_impl.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 using ::testing::_; 28 using ::testing::AnyNumber; 29 using ::testing::AtLeast; 30 using ::testing::InSequence; 31 using ::testing::Invoke; 32 using ::testing::NiceMock; 33 using ::testing::NotNull; 34 using ::testing::Return; 35 using ::testing::SaveArg; 36 using ::testing::StrictMock; 37 38 namespace media { 39 40 ACTION_P(RunClosure, closure) { 41 closure.Run(); 42 } 43 44 MATCHER_P(HasTimestamp, ms, "") { 45 *result_listener << "has timestamp " << arg->timestamp().InMilliseconds(); 46 return arg->timestamp().InMilliseconds() == ms; 47 } 48 49 // Arbitrary value. Has to be larger to cover any timestamp value used in tests. 50 static const int kVideoDurationInMs = 1000; 51 52 class VideoRendererImplTest : public ::testing::Test { 53 public: 54 VideoRendererImplTest() 55 : decoder_(new MockVideoDecoder()), 56 demuxer_stream_(DemuxerStream::VIDEO) { 57 ScopedVector<VideoDecoder> decoders; 58 decoders.push_back(decoder_); 59 60 renderer_.reset( 61 new VideoRendererImpl(message_loop_.message_loop_proxy(), 62 decoders.Pass(), 63 media::SetDecryptorReadyCB(), 64 base::Bind(&StrictMock<MockDisplayCB>::Display, 65 base::Unretained(&mock_display_cb_)), 66 true)); 67 68 demuxer_stream_.set_video_decoder_config(TestVideoConfig::Normal()); 69 70 // We expect these to be called but we don't care how/when. 71 EXPECT_CALL(demuxer_stream_, Read(_)).WillRepeatedly( 72 RunCallback<0>(DemuxerStream::kOk, 73 scoped_refptr<DecoderBuffer>(new DecoderBuffer(0)))); 74 EXPECT_CALL(*decoder_, Stop()) 75 .WillRepeatedly(Invoke(this, &VideoRendererImplTest::StopRequested)); 76 EXPECT_CALL(statistics_cb_object_, OnStatistics(_)) 77 .Times(AnyNumber()); 78 EXPECT_CALL(*this, OnTimeUpdate(_)) 79 .Times(AnyNumber()); 80 } 81 82 virtual ~VideoRendererImplTest() {} 83 84 // Callbacks passed into Initialize(). 85 MOCK_METHOD1(OnTimeUpdate, void(base::TimeDelta)); 86 87 void Initialize() { 88 InitializeWithLowDelay(false); 89 } 90 91 void InitializeWithLowDelay(bool low_delay) { 92 // Monitor decodes from the decoder. 93 EXPECT_CALL(*decoder_, Decode(_, _)) 94 .WillRepeatedly(Invoke(this, &VideoRendererImplTest::DecodeRequested)); 95 96 EXPECT_CALL(*decoder_, Reset(_)) 97 .WillRepeatedly(Invoke(this, &VideoRendererImplTest::FlushRequested)); 98 99 InSequence s; 100 101 // Set playback rate before anything else happens. 102 renderer_->SetPlaybackRate(1.0f); 103 104 // Initialize, we shouldn't have any reads. 105 InitializeRenderer(PIPELINE_OK, low_delay); 106 } 107 108 void InitializeRenderer(PipelineStatus expected, bool low_delay) { 109 SCOPED_TRACE(base::StringPrintf("InitializeRenderer(%d)", expected)); 110 WaitableMessageLoopEvent event; 111 CallInitialize(event.GetPipelineStatusCB(), low_delay, expected); 112 event.RunAndWaitForStatus(expected); 113 } 114 115 void CallInitialize(const PipelineStatusCB& status_cb, 116 bool low_delay, 117 PipelineStatus decoder_status) { 118 EXPECT_CALL(*decoder_, Initialize(_, _, _, _)).WillOnce( 119 DoAll(SaveArg<3>(&output_cb_), RunCallback<2>(decoder_status))); 120 renderer_->Initialize( 121 &demuxer_stream_, 122 low_delay, 123 status_cb, 124 base::Bind(&MockStatisticsCB::OnStatistics, 125 base::Unretained(&statistics_cb_object_)), 126 base::Bind(&VideoRendererImplTest::OnTimeUpdate, 127 base::Unretained(this)), 128 ended_event_.GetClosure(), 129 error_event_.GetPipelineStatusCB(), 130 base::Bind(&VideoRendererImplTest::GetTime, base::Unretained(this)), 131 base::Bind(&VideoRendererImplTest::GetDuration, 132 base::Unretained(this))); 133 } 134 135 void Play() { 136 SCOPED_TRACE("Play()"); 137 WaitableMessageLoopEvent event; 138 renderer_->Play(event.GetClosure()); 139 event.RunAndWait(); 140 } 141 142 void Preroll(int timestamp_ms, PipelineStatus expected) { 143 SCOPED_TRACE(base::StringPrintf("Preroll(%d, %d)", timestamp_ms, expected)); 144 WaitableMessageLoopEvent event; 145 renderer_->Preroll( 146 base::TimeDelta::FromMilliseconds(timestamp_ms), 147 event.GetPipelineStatusCB()); 148 event.RunAndWaitForStatus(expected); 149 } 150 151 void Flush() { 152 SCOPED_TRACE("Flush()"); 153 WaitableMessageLoopEvent event; 154 renderer_->Flush(event.GetClosure()); 155 event.RunAndWait(); 156 } 157 158 void Stop() { 159 SCOPED_TRACE("Stop()"); 160 WaitableMessageLoopEvent event; 161 renderer_->Stop(event.GetClosure()); 162 event.RunAndWait(); 163 } 164 165 void Shutdown() { 166 Flush(); 167 Stop(); 168 } 169 170 // Parses a string representation of video frames and generates corresponding 171 // VideoFrame objects in |decode_results_|. 172 // 173 // Syntax: 174 // nn - Queue a decoder buffer with timestamp nn * 1000us 175 // abort - Queue an aborted read 176 // error - Queue a decoder error 177 // 178 // Examples: 179 // A clip that is four frames long: "0 10 20 30" 180 // A clip that has a decode error: "60 70 error" 181 void QueueFrames(const std::string& str) { 182 std::vector<std::string> tokens; 183 base::SplitString(str, ' ', &tokens); 184 for (size_t i = 0; i < tokens.size(); ++i) { 185 if (tokens[i] == "abort") { 186 scoped_refptr<VideoFrame> null_frame; 187 decode_results_.push_back( 188 std::make_pair(VideoDecoder::kAborted, null_frame)); 189 continue; 190 } 191 192 if (tokens[i] == "error") { 193 scoped_refptr<VideoFrame> null_frame; 194 decode_results_.push_back( 195 std::make_pair(VideoDecoder::kDecodeError, null_frame)); 196 continue; 197 } 198 199 int timestamp_in_ms = 0; 200 if (base::StringToInt(tokens[i], ×tamp_in_ms)) { 201 gfx::Size natural_size = TestVideoConfig::NormalCodedSize(); 202 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame( 203 VideoFrame::YV12, 204 natural_size, 205 gfx::Rect(natural_size), 206 natural_size, 207 base::TimeDelta::FromMilliseconds(timestamp_in_ms)); 208 decode_results_.push_back(std::make_pair(VideoDecoder::kOk, frame)); 209 continue; 210 } 211 212 CHECK(false) << "Unrecognized decoder buffer token: " << tokens[i]; 213 } 214 } 215 216 bool IsReadPending() { 217 return !decode_cb_.is_null(); 218 } 219 220 void WaitForError(PipelineStatus expected) { 221 SCOPED_TRACE(base::StringPrintf("WaitForError(%d)", expected)); 222 error_event_.RunAndWaitForStatus(expected); 223 } 224 225 void WaitForEnded() { 226 SCOPED_TRACE("WaitForEnded()"); 227 ended_event_.RunAndWait(); 228 } 229 230 void WaitForPendingRead() { 231 SCOPED_TRACE("WaitForPendingRead()"); 232 if (!decode_cb_.is_null()) 233 return; 234 235 DCHECK(wait_for_pending_decode_cb_.is_null()); 236 237 WaitableMessageLoopEvent event; 238 wait_for_pending_decode_cb_ = event.GetClosure(); 239 event.RunAndWait(); 240 241 DCHECK(!decode_cb_.is_null()); 242 DCHECK(wait_for_pending_decode_cb_.is_null()); 243 } 244 245 void SatisfyPendingRead() { 246 CHECK(!decode_cb_.is_null()); 247 CHECK(!decode_results_.empty()); 248 249 // Post tasks for OutputCB and DecodeCB. 250 scoped_refptr<VideoFrame> frame = decode_results_.front().second; 251 if (frame) 252 message_loop_.PostTask(FROM_HERE, base::Bind(output_cb_, frame)); 253 message_loop_.PostTask( 254 FROM_HERE, base::Bind(base::ResetAndReturn(&decode_cb_), 255 decode_results_.front().first)); 256 decode_results_.pop_front(); 257 } 258 259 void SatisfyPendingReadWithEndOfStream() { 260 DCHECK(!decode_cb_.is_null()); 261 262 // Return EOS buffer to trigger EOS frame. 263 EXPECT_CALL(demuxer_stream_, Read(_)) 264 .WillOnce(RunCallback<0>(DemuxerStream::kOk, 265 DecoderBuffer::CreateEOSBuffer())); 266 267 // Satify pending |decode_cb_| to trigger a new DemuxerStream::Read(). 268 message_loop_.PostTask( 269 FROM_HERE, 270 base::Bind(base::ResetAndReturn(&decode_cb_), VideoDecoder::kOk)); 271 272 WaitForPendingRead(); 273 274 message_loop_.PostTask( 275 FROM_HERE, 276 base::Bind(base::ResetAndReturn(&decode_cb_), VideoDecoder::kOk)); 277 } 278 279 void AdvanceTimeInMs(int time_ms) { 280 DCHECK_EQ(&message_loop_, base::MessageLoop::current()); 281 base::AutoLock l(lock_); 282 time_ += base::TimeDelta::FromMilliseconds(time_ms); 283 DCHECK_LE(time_.InMicroseconds(), GetDuration().InMicroseconds()); 284 } 285 286 protected: 287 // Fixture members. 288 scoped_ptr<VideoRendererImpl> renderer_; 289 MockVideoDecoder* decoder_; // Owned by |renderer_|. 290 NiceMock<MockDemuxerStream> demuxer_stream_; 291 MockStatisticsCB statistics_cb_object_; 292 293 // Use StrictMock<T> to catch missing/extra display callbacks. 294 class MockDisplayCB { 295 public: 296 MOCK_METHOD1(Display, void(const scoped_refptr<VideoFrame>&)); 297 }; 298 StrictMock<MockDisplayCB> mock_display_cb_; 299 300 private: 301 base::TimeDelta GetTime() { 302 base::AutoLock l(lock_); 303 return time_; 304 } 305 306 base::TimeDelta GetDuration() { 307 return base::TimeDelta::FromMilliseconds(kVideoDurationInMs); 308 } 309 310 void DecodeRequested(const scoped_refptr<DecoderBuffer>& buffer, 311 const VideoDecoder::DecodeCB& decode_cb) { 312 DCHECK_EQ(&message_loop_, base::MessageLoop::current()); 313 CHECK(decode_cb_.is_null()); 314 decode_cb_ = decode_cb; 315 316 // Wake up WaitForPendingRead() if needed. 317 if (!wait_for_pending_decode_cb_.is_null()) 318 base::ResetAndReturn(&wait_for_pending_decode_cb_).Run(); 319 320 if (decode_results_.empty()) 321 return; 322 323 SatisfyPendingRead(); 324 } 325 326 void FlushRequested(const base::Closure& callback) { 327 DCHECK_EQ(&message_loop_, base::MessageLoop::current()); 328 decode_results_.clear(); 329 if (!decode_cb_.is_null()) { 330 QueueFrames("abort"); 331 SatisfyPendingRead(); 332 } 333 334 message_loop_.PostTask(FROM_HERE, callback); 335 } 336 337 void StopRequested() { 338 DCHECK_EQ(&message_loop_, base::MessageLoop::current()); 339 decode_results_.clear(); 340 if (!decode_cb_.is_null()) { 341 QueueFrames("abort"); 342 SatisfyPendingRead(); 343 } 344 } 345 346 base::MessageLoop message_loop_; 347 348 // Used to protect |time_|. 349 base::Lock lock_; 350 base::TimeDelta time_; 351 352 // Used for satisfying reads. 353 VideoDecoder::OutputCB output_cb_; 354 VideoDecoder::DecodeCB decode_cb_; 355 base::TimeDelta next_frame_timestamp_; 356 357 WaitableMessageLoopEvent error_event_; 358 WaitableMessageLoopEvent ended_event_; 359 360 // Run during DecodeRequested() to unblock WaitForPendingRead(). 361 base::Closure wait_for_pending_decode_cb_; 362 363 std::deque<std::pair< 364 VideoDecoder::Status, scoped_refptr<VideoFrame> > > decode_results_; 365 366 DISALLOW_COPY_AND_ASSIGN(VideoRendererImplTest); 367 }; 368 369 TEST_F(VideoRendererImplTest, DoNothing) { 370 // Test that creation and deletion doesn't depend on calls to Initialize() 371 // and/or Stop(). 372 } 373 374 TEST_F(VideoRendererImplTest, StopWithoutInitialize) { 375 Stop(); 376 } 377 378 TEST_F(VideoRendererImplTest, Initialize) { 379 Initialize(); 380 Shutdown(); 381 } 382 383 TEST_F(VideoRendererImplTest, InitializeAndPreroll) { 384 Initialize(); 385 QueueFrames("0 10 20 30"); 386 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 387 Preroll(0, PIPELINE_OK); 388 Shutdown(); 389 } 390 391 static void ExpectNotCalled(PipelineStatus) { 392 base::debug::StackTrace stack; 393 ADD_FAILURE() << "Expected callback not to be called\n" << stack.ToString(); 394 } 395 396 TEST_F(VideoRendererImplTest, StopWhileInitializing) { 397 CallInitialize(base::Bind(&ExpectNotCalled), false, PIPELINE_OK); 398 Stop(); 399 400 // ~VideoRendererImpl() will CHECK() if we left anything initialized. 401 } 402 403 TEST_F(VideoRendererImplTest, StopWhileFlushing) { 404 Initialize(); 405 renderer_->Flush(base::Bind(&ExpectNotCalled, PIPELINE_OK)); 406 Stop(); 407 408 // ~VideoRendererImpl() will CHECK() if we left anything initialized. 409 } 410 411 TEST_F(VideoRendererImplTest, Play) { 412 Initialize(); 413 QueueFrames("0 10 20 30"); 414 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 415 Preroll(0, PIPELINE_OK); 416 Play(); 417 Shutdown(); 418 } 419 420 TEST_F(VideoRendererImplTest, EndOfStream_ClipDuration) { 421 Initialize(); 422 QueueFrames("0 10 20 30"); 423 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 424 Preroll(0, PIPELINE_OK); 425 Play(); 426 427 // Next frame has timestamp way past duration. Its timestamp will be adjusted 428 // to match the duration of the video. 429 QueueFrames(base::IntToString(kVideoDurationInMs + 1000)); 430 SatisfyPendingRead(); 431 WaitForPendingRead(); 432 433 // Queue the end of stream frame and wait for the last frame to be rendered. 434 SatisfyPendingReadWithEndOfStream(); 435 436 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(kVideoDurationInMs))); 437 AdvanceTimeInMs(kVideoDurationInMs); 438 WaitForEnded(); 439 440 Shutdown(); 441 } 442 443 TEST_F(VideoRendererImplTest, DecodeError_Playing) { 444 Initialize(); 445 QueueFrames("0 10 20 30"); 446 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 447 Preroll(0, PIPELINE_OK); 448 Play(); 449 450 QueueFrames("error"); 451 SatisfyPendingRead(); 452 WaitForError(PIPELINE_ERROR_DECODE); 453 Shutdown(); 454 } 455 456 TEST_F(VideoRendererImplTest, DecodeError_DuringPreroll) { 457 Initialize(); 458 QueueFrames("error"); 459 Preroll(0, PIPELINE_ERROR_DECODE); 460 Shutdown(); 461 } 462 463 TEST_F(VideoRendererImplTest, Preroll_Exact) { 464 Initialize(); 465 QueueFrames("50 60 70 80 90"); 466 467 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(60))); 468 Preroll(60, PIPELINE_OK); 469 Shutdown(); 470 } 471 472 TEST_F(VideoRendererImplTest, Preroll_RightBefore) { 473 Initialize(); 474 QueueFrames("50 60 70 80 90"); 475 476 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(50))); 477 Preroll(59, PIPELINE_OK); 478 Shutdown(); 479 } 480 481 TEST_F(VideoRendererImplTest, Preroll_RightAfter) { 482 Initialize(); 483 QueueFrames("50 60 70 80 90"); 484 485 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(60))); 486 Preroll(61, PIPELINE_OK); 487 Shutdown(); 488 } 489 490 TEST_F(VideoRendererImplTest, Preroll_LowDelay) { 491 // In low-delay mode only one frame is required to finish preroll. 492 InitializeWithLowDelay(true); 493 QueueFrames("0"); 494 495 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 496 Preroll(0, PIPELINE_OK); 497 Play(); 498 499 QueueFrames("10"); 500 SatisfyPendingRead(); 501 502 WaitableMessageLoopEvent event; 503 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(10))) 504 .WillOnce(RunClosure(event.GetClosure())); 505 AdvanceTimeInMs(10); 506 event.RunAndWait(); 507 508 Shutdown(); 509 } 510 511 TEST_F(VideoRendererImplTest, PlayAfterPreroll) { 512 Initialize(); 513 QueueFrames("0 10 20 30"); 514 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 515 Preroll(0, PIPELINE_OK); 516 Play(); 517 518 // Check that there is an outstanding Read() request. 519 EXPECT_TRUE(IsReadPending()); 520 521 Shutdown(); 522 } 523 524 TEST_F(VideoRendererImplTest, Rebuffer) { 525 Initialize(); 526 QueueFrames("0 10 20 30"); 527 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 528 Preroll(0, PIPELINE_OK); 529 Play(); 530 531 // Advance time past prerolled time drain the ready frame queue. 532 AdvanceTimeInMs(50); 533 WaitForPendingRead(); 534 535 // Simulate a Preroll/Play rebuffer sequence. 536 WaitableMessageLoopEvent event; 537 renderer_->Preroll(kNoTimestamp(), 538 event.GetPipelineStatusCB()); 539 540 // Queue enough frames to satisfy preroll. 541 QueueFrames("40 50 60 70"); 542 SatisfyPendingRead(); 543 544 // TODO(scherkus): We shouldn't display the next ready frame in a rebuffer 545 // situation, see http://crbug.com/365516 546 EXPECT_CALL(mock_display_cb_, Display(_)).Times(AtLeast(1)); 547 548 event.RunAndWaitForStatus(PIPELINE_OK); 549 550 Play(); 551 552 Shutdown(); 553 } 554 555 TEST_F(VideoRendererImplTest, Rebuffer_AlreadyHaveEnoughFrames) { 556 Initialize(); 557 QueueFrames("0 10 20 30"); 558 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 559 Preroll(0, PIPELINE_OK); 560 561 // Queue an extra frame so that we'll have enough frames to satisfy 562 // preroll even after the first frame is painted. 563 QueueFrames("40"); 564 SatisfyPendingRead(); 565 Play(); 566 567 // Simulate a Preroll/Play rebuffer sequence. 568 // 569 // TODO(scherkus): We shouldn't display the next ready frame in a rebuffer 570 // situation, see http://crbug.com/365516 571 EXPECT_CALL(mock_display_cb_, Display(_)).Times(AtLeast(1)); 572 573 WaitableMessageLoopEvent event; 574 renderer_->Preroll(kNoTimestamp(), 575 event.GetPipelineStatusCB()); 576 577 event.RunAndWaitForStatus(PIPELINE_OK); 578 579 Play(); 580 581 Shutdown(); 582 } 583 584 // Verify that a late decoder response doesn't break invariants in the renderer. 585 TEST_F(VideoRendererImplTest, StopDuringOutstandingRead) { 586 Initialize(); 587 QueueFrames("0 10 20 30"); 588 EXPECT_CALL(mock_display_cb_, Display(HasTimestamp(0))); 589 Preroll(0, PIPELINE_OK); 590 Play(); 591 592 // Check that there is an outstanding Read() request. 593 EXPECT_TRUE(IsReadPending()); 594 595 WaitableMessageLoopEvent event; 596 renderer_->Stop(event.GetClosure()); 597 event.RunAndWait(); 598 } 599 600 TEST_F(VideoRendererImplTest, VideoDecoder_InitFailure) { 601 InSequence s; 602 InitializeRenderer(DECODER_ERROR_NOT_SUPPORTED, false); 603 Stop(); 604 } 605 606 } // namespace media 607