| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 <vector> | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/message_loop/message_loop.h" | |
| 9 #include "base/run_loop.h" | |
| 10 #include "base/test/simple_test_tick_clock.h" | |
| 11 #include "media/base/gmock_callback_support.h" | |
| 12 #include "media/base/mock_filters.h" | |
| 13 #include "media/base/test_helpers.h" | |
| 14 #include "media/filters/renderer_impl.h" | |
| 15 #include "testing/gtest/include/gtest/gtest.h" | |
| 16 | |
| 17 using ::testing::_; | |
| 18 using ::testing::DoAll; | |
| 19 using ::testing::InSequence; | |
| 20 using ::testing::Mock; | |
| 21 using ::testing::Return; | |
| 22 using ::testing::SaveArg; | |
| 23 using ::testing::StrictMock; | |
| 24 | |
| 25 namespace media { | |
| 26 | |
| 27 const int64 kStartPlayingTimeInMs = 100; | |
| 28 | |
| 29 ACTION_P2(SetBufferingState, cb, buffering_state) { | |
| 30 cb->Run(buffering_state); | |
| 31 } | |
| 32 | |
| 33 ACTION_P2(AudioError, cb, error) { | |
| 34 cb->Run(error); | |
| 35 } | |
| 36 | |
| 37 class RendererImplTest : public ::testing::Test { | |
| 38 public: | |
| 39 // Used for setting expectations on pipeline callbacks. Using a StrictMock | |
| 40 // also lets us test for missing callbacks. | |
| 41 class CallbackHelper { | |
| 42 public: | |
| 43 CallbackHelper() {} | |
| 44 virtual ~CallbackHelper() {} | |
| 45 | |
| 46 MOCK_METHOD1(OnInitialize, void(PipelineStatus)); | |
| 47 MOCK_METHOD0(OnFlushed, void()); | |
| 48 MOCK_METHOD0(OnEnded, void()); | |
| 49 MOCK_METHOD1(OnError, void(PipelineStatus)); | |
| 50 MOCK_METHOD1(OnUpdateStatistics, void(const PipelineStatistics&)); | |
| 51 MOCK_METHOD1(OnBufferingStateChange, void(BufferingState)); | |
| 52 MOCK_METHOD1(OnVideoFramePaint, void(const scoped_refptr<VideoFrame>&)); | |
| 53 | |
| 54 private: | |
| 55 DISALLOW_COPY_AND_ASSIGN(CallbackHelper); | |
| 56 }; | |
| 57 | |
| 58 RendererImplTest() | |
| 59 : demuxer_(new StrictMock<MockDemuxer>()), | |
| 60 video_renderer_(new StrictMock<MockVideoRenderer>()), | |
| 61 audio_renderer_(new StrictMock<MockAudioRenderer>()), | |
| 62 renderer_impl_( | |
| 63 new RendererImpl(message_loop_.message_loop_proxy(), | |
| 64 scoped_ptr<AudioRenderer>(audio_renderer_), | |
| 65 scoped_ptr<VideoRenderer>(video_renderer_))) { | |
| 66 // SetDemuxerExpectations() adds overriding expectations for expected | |
| 67 // non-NULL streams. | |
| 68 DemuxerStream* null_pointer = NULL; | |
| 69 EXPECT_CALL(*demuxer_, GetStream(_)) | |
| 70 .WillRepeatedly(Return(null_pointer)); | |
| 71 } | |
| 72 | |
| 73 virtual ~RendererImplTest() { | |
| 74 renderer_impl_.reset(); | |
| 75 base::RunLoop().RunUntilIdle(); | |
| 76 } | |
| 77 | |
| 78 protected: | |
| 79 typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector; | |
| 80 | |
| 81 scoped_ptr<StrictMock<MockDemuxerStream> > CreateStream( | |
| 82 DemuxerStream::Type type) { | |
| 83 scoped_ptr<StrictMock<MockDemuxerStream> > stream( | |
| 84 new StrictMock<MockDemuxerStream>(type)); | |
| 85 return stream.Pass(); | |
| 86 } | |
| 87 | |
| 88 // Sets up expectations to allow the audio renderer to initialize. | |
| 89 void SetAudioRendererInitializeExpectations(PipelineStatus status) { | |
| 90 EXPECT_CALL(*audio_renderer_, | |
| 91 Initialize(audio_stream_.get(), _, _, _, _, _, _)) | |
| 92 .WillOnce(DoAll(SaveArg<4>(&audio_buffering_state_cb_), | |
| 93 SaveArg<5>(&audio_ended_cb_), | |
| 94 SaveArg<6>(&audio_error_cb_), RunCallback<1>(status))); | |
| 95 } | |
| 96 | |
| 97 // Sets up expectations to allow the video renderer to initialize. | |
| 98 void SetVideoRendererInitializeExpectations(PipelineStatus status) { | |
| 99 EXPECT_CALL(*video_renderer_, | |
| 100 Initialize(video_stream_.get(), _, _, _, _, _, _, _, _)) | |
| 101 .WillOnce(DoAll(SaveArg<4>(&video_buffering_state_cb_), | |
| 102 SaveArg<6>(&video_ended_cb_), RunCallback<1>(status))); | |
| 103 } | |
| 104 | |
| 105 void InitializeAndExpect(PipelineStatus start_status) { | |
| 106 EXPECT_CALL(callbacks_, OnInitialize(start_status)); | |
| 107 | |
| 108 if (start_status == PIPELINE_OK && audio_stream_) { | |
| 109 EXPECT_CALL(*audio_renderer_, GetTimeSource()) | |
| 110 .WillOnce(Return(&time_source_)); | |
| 111 } | |
| 112 | |
| 113 renderer_impl_->Initialize( | |
| 114 demuxer_.get(), | |
| 115 base::Bind(&CallbackHelper::OnInitialize, | |
| 116 base::Unretained(&callbacks_)), | |
| 117 base::Bind(&CallbackHelper::OnUpdateStatistics, | |
| 118 base::Unretained(&callbacks_)), | |
| 119 base::Bind(&CallbackHelper::OnBufferingStateChange, | |
| 120 base::Unretained(&callbacks_)), | |
| 121 base::Bind(&CallbackHelper::OnVideoFramePaint, | |
| 122 base::Unretained(&callbacks_)), | |
| 123 base::Bind(&CallbackHelper::OnEnded, base::Unretained(&callbacks_)), | |
| 124 base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_))); | |
| 125 base::RunLoop().RunUntilIdle(); | |
| 126 } | |
| 127 | |
| 128 void CreateAudioStream() { | |
| 129 audio_stream_ = CreateStream(DemuxerStream::AUDIO); | |
| 130 streams_.push_back(audio_stream_.get()); | |
| 131 EXPECT_CALL(*demuxer_, GetStream(DemuxerStream::AUDIO)) | |
| 132 .WillRepeatedly(Return(audio_stream_.get())); | |
| 133 } | |
| 134 | |
| 135 void CreateVideoStream() { | |
| 136 video_stream_ = CreateStream(DemuxerStream::VIDEO); | |
| 137 video_stream_->set_video_decoder_config(video_decoder_config_); | |
| 138 streams_.push_back(video_stream_.get()); | |
| 139 EXPECT_CALL(*demuxer_, GetStream(DemuxerStream::VIDEO)) | |
| 140 .WillRepeatedly(Return(video_stream_.get())); | |
| 141 } | |
| 142 | |
| 143 void CreateAudioAndVideoStream() { | |
| 144 CreateAudioStream(); | |
| 145 CreateVideoStream(); | |
| 146 } | |
| 147 | |
| 148 void InitializeWithAudio() { | |
| 149 CreateAudioStream(); | |
| 150 SetAudioRendererInitializeExpectations(PIPELINE_OK); | |
| 151 InitializeAndExpect(PIPELINE_OK); | |
| 152 } | |
| 153 | |
| 154 void InitializeWithVideo() { | |
| 155 CreateVideoStream(); | |
| 156 SetVideoRendererInitializeExpectations(PIPELINE_OK); | |
| 157 InitializeAndExpect(PIPELINE_OK); | |
| 158 } | |
| 159 | |
| 160 void InitializeWithAudioAndVideo() { | |
| 161 CreateAudioAndVideoStream(); | |
| 162 SetAudioRendererInitializeExpectations(PIPELINE_OK); | |
| 163 SetVideoRendererInitializeExpectations(PIPELINE_OK); | |
| 164 InitializeAndExpect(PIPELINE_OK); | |
| 165 } | |
| 166 | |
| 167 void Play() { | |
| 168 DCHECK(audio_stream_ || video_stream_); | |
| 169 EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); | |
| 170 | |
| 171 base::TimeDelta start_time( | |
| 172 base::TimeDelta::FromMilliseconds(kStartPlayingTimeInMs)); | |
| 173 | |
| 174 if (audio_stream_) { | |
| 175 EXPECT_CALL(time_source_, SetMediaTime(start_time)); | |
| 176 EXPECT_CALL(time_source_, StartTicking()); | |
| 177 EXPECT_CALL(*audio_renderer_, StartPlaying()) | |
| 178 .WillOnce(SetBufferingState(&audio_buffering_state_cb_, | |
| 179 BUFFERING_HAVE_ENOUGH)); | |
| 180 } | |
| 181 | |
| 182 if (video_stream_) { | |
| 183 EXPECT_CALL(*video_renderer_, StartPlayingFrom(start_time)) | |
| 184 .WillOnce(SetBufferingState(&video_buffering_state_cb_, | |
| 185 BUFFERING_HAVE_ENOUGH)); | |
| 186 } | |
| 187 | |
| 188 renderer_impl_->StartPlayingFrom(start_time); | |
| 189 base::RunLoop().RunUntilIdle(); | |
| 190 } | |
| 191 | |
| 192 void Flush(bool underflowed) { | |
| 193 if (audio_stream_) { | |
| 194 if (!underflowed) | |
| 195 EXPECT_CALL(time_source_, StopTicking()); | |
| 196 EXPECT_CALL(*audio_renderer_, Flush(_)) | |
| 197 .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_, | |
| 198 BUFFERING_HAVE_NOTHING), | |
| 199 RunClosure<0>())); | |
| 200 } | |
| 201 | |
| 202 if (video_stream_) { | |
| 203 EXPECT_CALL(*video_renderer_, Flush(_)) | |
| 204 .WillOnce(DoAll(SetBufferingState(&video_buffering_state_cb_, | |
| 205 BUFFERING_HAVE_NOTHING), | |
| 206 RunClosure<0>())); | |
| 207 } | |
| 208 | |
| 209 EXPECT_CALL(callbacks_, OnFlushed()); | |
| 210 | |
| 211 renderer_impl_->Flush( | |
| 212 base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_))); | |
| 213 base::RunLoop().RunUntilIdle(); | |
| 214 } | |
| 215 | |
| 216 void SetPlaybackRate(float playback_rate) { | |
| 217 EXPECT_CALL(time_source_, SetPlaybackRate(playback_rate)); | |
| 218 renderer_impl_->SetPlaybackRate(playback_rate); | |
| 219 base::RunLoop().RunUntilIdle(); | |
| 220 } | |
| 221 | |
| 222 int64 GetMediaTimeMs() { | |
| 223 return renderer_impl_->GetMediaTime().InMilliseconds(); | |
| 224 } | |
| 225 | |
| 226 bool IsMediaTimeAdvancing(float playback_rate) { | |
| 227 int64 start_time_ms = GetMediaTimeMs(); | |
| 228 const int64 time_to_advance_ms = 100; | |
| 229 | |
| 230 test_tick_clock_.Advance( | |
| 231 base::TimeDelta::FromMilliseconds(time_to_advance_ms)); | |
| 232 | |
| 233 if (GetMediaTimeMs() == start_time_ms + time_to_advance_ms * playback_rate) | |
| 234 return true; | |
| 235 | |
| 236 DCHECK_EQ(start_time_ms, GetMediaTimeMs()); | |
| 237 return false; | |
| 238 } | |
| 239 | |
| 240 bool IsMediaTimeAdvancing() { | |
| 241 return IsMediaTimeAdvancing(1.0f); | |
| 242 } | |
| 243 | |
| 244 // Fixture members. | |
| 245 base::MessageLoop message_loop_; | |
| 246 StrictMock<CallbackHelper> callbacks_; | |
| 247 base::SimpleTestTickClock test_tick_clock_; | |
| 248 | |
| 249 scoped_ptr<StrictMock<MockDemuxer> > demuxer_; | |
| 250 StrictMock<MockVideoRenderer>* video_renderer_; | |
| 251 StrictMock<MockAudioRenderer>* audio_renderer_; | |
| 252 scoped_ptr<RendererImpl> renderer_impl_; | |
| 253 | |
| 254 StrictMock<MockTimeSource> time_source_; | |
| 255 scoped_ptr<StrictMock<MockDemuxerStream> > audio_stream_; | |
| 256 scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_; | |
| 257 MockDemuxerStreamVector streams_; | |
| 258 BufferingStateCB audio_buffering_state_cb_; | |
| 259 BufferingStateCB video_buffering_state_cb_; | |
| 260 base::Closure audio_ended_cb_; | |
| 261 base::Closure video_ended_cb_; | |
| 262 PipelineStatusCB audio_error_cb_; | |
| 263 VideoDecoderConfig video_decoder_config_; | |
| 264 | |
| 265 private: | |
| 266 DISALLOW_COPY_AND_ASSIGN(RendererImplTest); | |
| 267 }; | |
| 268 | |
| 269 TEST_F(RendererImplTest, DestroyBeforeInitialize) { | |
| 270 // |renderer_impl_| will be destroyed in the dtor. | |
| 271 } | |
| 272 | |
| 273 TEST_F(RendererImplTest, InitializeWithAudio) { | |
| 274 InitializeWithAudio(); | |
| 275 } | |
| 276 | |
| 277 TEST_F(RendererImplTest, InitializeWithVideo) { | |
| 278 InitializeWithVideo(); | |
| 279 } | |
| 280 | |
| 281 TEST_F(RendererImplTest, InitializeWithAudioVideo) { | |
| 282 InitializeWithAudioAndVideo(); | |
| 283 } | |
| 284 | |
| 285 TEST_F(RendererImplTest, InitializeWithAudio_Failed) { | |
| 286 CreateAudioStream(); | |
| 287 SetAudioRendererInitializeExpectations(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 288 InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 289 } | |
| 290 | |
| 291 TEST_F(RendererImplTest, InitializeWithVideo_Failed) { | |
| 292 CreateVideoStream(); | |
| 293 SetVideoRendererInitializeExpectations(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 294 InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 295 } | |
| 296 | |
| 297 TEST_F(RendererImplTest, InitializeWithAudioVideo_AudioRendererFailed) { | |
| 298 CreateAudioAndVideoStream(); | |
| 299 SetAudioRendererInitializeExpectations(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 300 // VideoRenderer::Initialize() should not be called. | |
| 301 InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 302 } | |
| 303 | |
| 304 TEST_F(RendererImplTest, InitializeWithAudioVideo_VideoRendererFailed) { | |
| 305 CreateAudioAndVideoStream(); | |
| 306 SetAudioRendererInitializeExpectations(PIPELINE_OK); | |
| 307 SetVideoRendererInitializeExpectations(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 308 InitializeAndExpect(PIPELINE_ERROR_INITIALIZATION_FAILED); | |
| 309 } | |
| 310 | |
| 311 TEST_F(RendererImplTest, StartPlayingFrom) { | |
| 312 InitializeWithAudioAndVideo(); | |
| 313 Play(); | |
| 314 } | |
| 315 | |
| 316 TEST_F(RendererImplTest, FlushAfterInitialization) { | |
| 317 InitializeWithAudioAndVideo(); | |
| 318 Flush(true); | |
| 319 } | |
| 320 | |
| 321 TEST_F(RendererImplTest, FlushAfterPlay) { | |
| 322 InitializeWithAudioAndVideo(); | |
| 323 Play(); | |
| 324 Flush(false); | |
| 325 } | |
| 326 | |
| 327 TEST_F(RendererImplTest, FlushAfterUnderflow) { | |
| 328 InitializeWithAudioAndVideo(); | |
| 329 Play(); | |
| 330 | |
| 331 // Simulate underflow. | |
| 332 EXPECT_CALL(time_source_, StopTicking()); | |
| 333 audio_buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING); | |
| 334 | |
| 335 // Flush while underflowed. We shouldn't call StopTicking() again. | |
| 336 Flush(true); | |
| 337 } | |
| 338 | |
| 339 TEST_F(RendererImplTest, SetPlaybackRate) { | |
| 340 InitializeWithAudioAndVideo(); | |
| 341 SetPlaybackRate(1.0f); | |
| 342 SetPlaybackRate(2.0f); | |
| 343 } | |
| 344 | |
| 345 TEST_F(RendererImplTest, SetVolume) { | |
| 346 InitializeWithAudioAndVideo(); | |
| 347 EXPECT_CALL(*audio_renderer_, SetVolume(2.0f)); | |
| 348 renderer_impl_->SetVolume(2.0f); | |
| 349 } | |
| 350 | |
| 351 TEST_F(RendererImplTest, AudioStreamEnded) { | |
| 352 InitializeWithAudio(); | |
| 353 Play(); | |
| 354 | |
| 355 EXPECT_CALL(time_source_, StopTicking()); | |
| 356 EXPECT_CALL(callbacks_, OnEnded()); | |
| 357 | |
| 358 audio_ended_cb_.Run(); | |
| 359 base::RunLoop().RunUntilIdle(); | |
| 360 } | |
| 361 | |
| 362 TEST_F(RendererImplTest, VideoStreamEnded) { | |
| 363 InitializeWithVideo(); | |
| 364 Play(); | |
| 365 | |
| 366 // Video ended won't affect |time_source_|. | |
| 367 EXPECT_CALL(callbacks_, OnEnded()); | |
| 368 | |
| 369 video_ended_cb_.Run(); | |
| 370 base::RunLoop().RunUntilIdle(); | |
| 371 } | |
| 372 | |
| 373 TEST_F(RendererImplTest, AudioVideoStreamsEnded) { | |
| 374 InitializeWithAudioAndVideo(); | |
| 375 Play(); | |
| 376 | |
| 377 // OnEnded() is called only when all streams have finished. | |
| 378 audio_ended_cb_.Run(); | |
| 379 base::RunLoop().RunUntilIdle(); | |
| 380 | |
| 381 EXPECT_CALL(time_source_, StopTicking()); | |
| 382 EXPECT_CALL(callbacks_, OnEnded()); | |
| 383 | |
| 384 video_ended_cb_.Run(); | |
| 385 base::RunLoop().RunUntilIdle(); | |
| 386 } | |
| 387 | |
| 388 TEST_F(RendererImplTest, ErrorAfterInitialize) { | |
| 389 InitializeWithAudio(); | |
| 390 EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_DECODE)); | |
| 391 audio_error_cb_.Run(PIPELINE_ERROR_DECODE); | |
| 392 base::RunLoop().RunUntilIdle(); | |
| 393 } | |
| 394 | |
| 395 TEST_F(RendererImplTest, ErrorDuringPlaying) { | |
| 396 InitializeWithAudio(); | |
| 397 Play(); | |
| 398 | |
| 399 EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_DECODE)); | |
| 400 audio_error_cb_.Run(PIPELINE_ERROR_DECODE); | |
| 401 base::RunLoop().RunUntilIdle(); | |
| 402 } | |
| 403 | |
| 404 TEST_F(RendererImplTest, ErrorDuringFlush) { | |
| 405 InitializeWithAudio(); | |
| 406 Play(); | |
| 407 | |
| 408 InSequence s; | |
| 409 EXPECT_CALL(time_source_, StopTicking()); | |
| 410 EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce(DoAll( | |
| 411 AudioError(&audio_error_cb_, PIPELINE_ERROR_DECODE), | |
| 412 RunClosure<0>())); | |
| 413 EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_DECODE)); | |
| 414 EXPECT_CALL(callbacks_, OnFlushed()); | |
| 415 renderer_impl_->Flush( | |
| 416 base::Bind(&CallbackHelper::OnFlushed, base::Unretained(&callbacks_))); | |
| 417 base::RunLoop().RunUntilIdle(); | |
| 418 } | |
| 419 | |
| 420 TEST_F(RendererImplTest, ErrorAfterFlush) { | |
| 421 InitializeWithAudio(); | |
| 422 Play(); | |
| 423 Flush(false); | |
| 424 | |
| 425 EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_DECODE)); | |
| 426 audio_error_cb_.Run(PIPELINE_ERROR_DECODE); | |
| 427 base::RunLoop().RunUntilIdle(); | |
| 428 } | |
| 429 | |
| 430 TEST_F(RendererImplTest, ErrorDuringInitialize) { | |
| 431 CreateAudioAndVideoStream(); | |
| 432 SetAudioRendererInitializeExpectations(PIPELINE_OK); | |
| 433 | |
| 434 // Force an audio error to occur during video renderer initialization. | |
| 435 EXPECT_CALL(*video_renderer_, | |
| 436 Initialize(video_stream_.get(), _, _, _, _, _, _, _, _)) | |
| 437 .WillOnce(DoAll(AudioError(&audio_error_cb_, PIPELINE_ERROR_DECODE), | |
| 438 SaveArg<4>(&video_buffering_state_cb_), | |
| 439 SaveArg<6>(&video_ended_cb_), | |
| 440 RunCallback<1>(PIPELINE_OK))); | |
| 441 | |
| 442 InitializeAndExpect(PIPELINE_ERROR_DECODE); | |
| 443 } | |
| 444 | |
| 445 } // namespace media | |
| OLD | NEW |