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 |