Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/bind.h" | 5 #include "base/bind.h" |
| 6 #include "base/message_loop.h" | 6 #include "base/message_loop.h" |
| 7 #include "media/base/filter_collection.h" | 7 #include "media/base/filter_collection.h" |
| 8 #include "media/base/media_log.h" | 8 #include "media/base/media_log.h" |
| 9 #include "media/base/message_loop_factory_impl.h" | 9 #include "media/base/message_loop_factory_impl.h" |
| 10 #include "media/base/pipeline.h" | 10 #include "media/base/pipeline.h" |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 // in the real AudioRendererImpl & SkCanvasVideoRenderer implementations used in | 97 // in the real AudioRendererImpl & SkCanvasVideoRenderer implementations used in |
| 98 // the browser. The renderers in this test don't actually write data to a | 98 // the browser. The renderers in this test don't actually write data to a |
| 99 // display or audio device. Both of these devices are simulated since they have | 99 // display or audio device. Both of these devices are simulated since they have |
| 100 // little effect on verifying pipeline behavior and allow tests to run faster | 100 // little effect on verifying pipeline behavior and allow tests to run faster |
| 101 // than real-time. | 101 // than real-time. |
| 102 class PipelineIntegrationTest : public testing::Test { | 102 class PipelineIntegrationTest : public testing::Test { |
| 103 public: | 103 public: |
| 104 PipelineIntegrationTest() | 104 PipelineIntegrationTest() |
| 105 : message_loop_factory_(new MessageLoopFactoryImpl()), | 105 : message_loop_factory_(new MessageLoopFactoryImpl()), |
| 106 pipeline_(new Pipeline(&message_loop_, new MediaLog())), | 106 pipeline_(new Pipeline(&message_loop_, new MediaLog())), |
| 107 ended_(false) { | 107 ended_(false), |
| 108 error_status_(PIPELINE_OK) { | |
|
Ami GONE FROM CHROMIUM
2012/01/31 19:06:54
since it can hold OK, s/error_/pipeline_/
acolwell GONE FROM CHROMIUM
2012/01/31 19:16:00
Done.
| |
| 108 EXPECT_CALL(*this, OnVideoRendererPaint()).Times(AnyNumber()); | 109 EXPECT_CALL(*this, OnVideoRendererPaint()).Times(AnyNumber()); |
| 109 EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber()); | 110 EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber()); |
| 110 } | 111 } |
| 111 | 112 |
| 112 virtual ~PipelineIntegrationTest() { | 113 virtual ~PipelineIntegrationTest() { |
| 113 if (!pipeline_->IsRunning()) | 114 if (!pipeline_->IsRunning()) |
| 114 return; | 115 return; |
| 115 | 116 |
| 116 Stop(); | 117 Stop(); |
| 117 } | 118 } |
| 118 | 119 |
| 119 void OnStatusCallback(PipelineStatus expected_status, | 120 void OnStatusCallback(PipelineStatus expected_status, |
| 120 PipelineStatus status) { | 121 PipelineStatus status) { |
| 121 EXPECT_EQ(status, expected_status); | 122 EXPECT_EQ(status, expected_status); |
| 123 error_status_ = status; | |
| 122 message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 124 message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| 123 } | 125 } |
| 124 | 126 |
| 125 PipelineStatusCB QuitOnStatusCB(PipelineStatus expected_status) { | 127 PipelineStatusCB QuitOnStatusCB(PipelineStatus expected_status) { |
| 126 return base::Bind(&PipelineIntegrationTest::OnStatusCallback, | 128 return base::Bind(&PipelineIntegrationTest::OnStatusCallback, |
| 127 base::Unretained(this), | 129 base::Unretained(this), |
| 128 expected_status); | 130 expected_status); |
| 129 } | 131 } |
| 130 | 132 |
| 131 void OnEnded(PipelineStatus status) { | 133 void OnEnded(PipelineStatus status) { |
| 132 DCHECK_EQ(status, PIPELINE_OK); | 134 DCHECK_EQ(status, PIPELINE_OK); |
| 133 DCHECK(!ended_); | 135 DCHECK(!ended_); |
| 134 ended_ = true; | 136 ended_ = true; |
| 135 message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 137 message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| 136 } | 138 } |
| 137 | 139 |
| 138 void WaitUntilOnEnded() { | 140 bool WaitUntilOnEnded() { |
| 139 if (!ended_) { | 141 if (ended_) |
| 140 message_loop_.Run(); | 142 return (error_status_ == PIPELINE_OK); |
| 141 DCHECK(ended_); | 143 message_loop_.Run(); |
| 142 } | 144 EXPECT_TRUE(ended_); |
| 145 return ended_ && (error_status_ == PIPELINE_OK); | |
| 143 } | 146 } |
| 144 | 147 |
| 145 MOCK_METHOD1(OnError, void(PipelineStatus)); | 148 void OnError(PipelineStatus status) { |
| 149 DCHECK_NE(status, PIPELINE_OK); | |
| 150 error_status_ = status; | |
| 151 message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); | |
| 152 } | |
| 146 | 153 |
| 147 void Start(const std::string& url, PipelineStatus expected_status) { | 154 bool Start(const std::string& url, PipelineStatus expected_status) { |
| 148 pipeline_->Start( | 155 pipeline_->Start( |
| 149 CreateFilterCollection(url), | 156 CreateFilterCollection(url), |
| 150 url, | 157 url, |
| 151 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), | 158 base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)), |
| 152 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), | 159 base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)), |
| 153 NetworkEventCB(), | 160 NetworkEventCB(), |
| 154 QuitOnStatusCB(expected_status)); | 161 QuitOnStatusCB(expected_status)); |
| 155 message_loop_.Run(); | 162 message_loop_.Run(); |
| 163 return (error_status_ == PIPELINE_OK); | |
| 156 } | 164 } |
| 157 | 165 |
| 158 void Play() { | 166 void Play() { |
| 159 pipeline_->SetPlaybackRate(1); | 167 pipeline_->SetPlaybackRate(1); |
| 160 } | 168 } |
| 161 | 169 |
| 162 void Pause() { | 170 void Pause() { |
| 163 pipeline_->SetPlaybackRate(0); | 171 pipeline_->SetPlaybackRate(0); |
| 164 } | 172 } |
| 165 | 173 |
| 166 void Seek(base::TimeDelta seek_time) { | 174 bool Seek(base::TimeDelta seek_time) { |
| 167 ended_ = false; | 175 ended_ = false; |
| 168 | 176 |
| 169 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK)); | 177 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK)); |
| 170 message_loop_.Run(); | 178 message_loop_.Run(); |
| 179 return (error_status_ == PIPELINE_OK); | |
| 171 } | 180 } |
| 172 | 181 |
| 173 void Stop() { | 182 void Stop() { |
| 174 DCHECK(pipeline_->IsRunning()); | 183 DCHECK(pipeline_->IsRunning()); |
| 175 pipeline_->Stop(QuitOnStatusCB(PIPELINE_OK)); | 184 pipeline_->Stop(QuitOnStatusCB(PIPELINE_OK)); |
| 176 message_loop_.Run(); | 185 message_loop_.Run(); |
| 177 } | 186 } |
| 178 | 187 |
| 179 void QuitAfterCurrentTimeTask(const base::TimeDelta& quit_time) { | 188 void QuitAfterCurrentTimeTask(const base::TimeDelta& quit_time) { |
| 180 if (pipeline_->GetCurrentTime() >= quit_time) { | 189 if (pipeline_->GetCurrentTime() >= quit_time || |
| 190 error_status_ != PIPELINE_OK) { | |
| 181 message_loop_.Quit(); | 191 message_loop_.Quit(); |
| 182 return; | 192 return; |
| 183 } | 193 } |
| 184 | 194 |
| 185 message_loop_.PostDelayedTask( | 195 message_loop_.PostDelayedTask( |
| 186 FROM_HERE, | 196 FROM_HERE, |
| 187 base::Bind(&PipelineIntegrationTest::QuitAfterCurrentTimeTask, | 197 base::Bind(&PipelineIntegrationTest::QuitAfterCurrentTimeTask, |
| 188 base::Unretained(this), quit_time), | 198 base::Unretained(this), quit_time), |
| 189 10); | 199 10); |
| 190 } | 200 } |
| 191 | 201 |
| 192 void WaitUntilCurrentTimeIsAfter(const base::TimeDelta& wait_time) { | 202 bool WaitUntilCurrentTimeIsAfter(const base::TimeDelta& wait_time) { |
| 193 DCHECK(pipeline_->IsRunning()); | 203 DCHECK(pipeline_->IsRunning()); |
| 194 DCHECK_GT(pipeline_->GetPlaybackRate(), 0); | 204 DCHECK_GT(pipeline_->GetPlaybackRate(), 0); |
| 195 DCHECK(wait_time <= pipeline_->GetMediaDuration()); | 205 DCHECK(wait_time <= pipeline_->GetMediaDuration()); |
| 196 | 206 |
| 197 message_loop_.PostDelayedTask( | 207 message_loop_.PostDelayedTask( |
| 198 FROM_HERE, | 208 FROM_HERE, |
| 199 base::Bind(&PipelineIntegrationTest::QuitAfterCurrentTimeTask, | 209 base::Bind(&PipelineIntegrationTest::QuitAfterCurrentTimeTask, |
| 200 base::Unretained(this), | 210 base::Unretained(this), |
| 201 wait_time), | 211 wait_time), |
| 202 10); | 212 10); |
| 203 message_loop_.Run(); | 213 message_loop_.Run(); |
| 214 return (error_status_ == PIPELINE_OK); | |
| 204 } | 215 } |
| 205 | 216 |
| 206 scoped_ptr<FilterCollection> CreateFilterCollection(const std::string& url) { | 217 scoped_ptr<FilterCollection> CreateFilterCollection(const std::string& url) { |
| 207 scoped_refptr<FileDataSource> data_source = new FileDataSource(); | 218 scoped_refptr<FileDataSource> data_source = new FileDataSource(); |
| 208 CHECK_EQ(PIPELINE_OK, data_source->Initialize(url)); | 219 CHECK_EQ(PIPELINE_OK, data_source->Initialize(url)); |
| 209 return CreateFilterCollection(scoped_ptr<DemuxerFactory>( | 220 return CreateFilterCollection(scoped_ptr<DemuxerFactory>( |
| 210 new FFmpegDemuxerFactory(data_source, &message_loop_))); | 221 new FFmpegDemuxerFactory(data_source, &message_loop_))); |
| 211 } | 222 } |
| 212 | 223 |
| 213 scoped_ptr<FilterCollection> CreateFilterCollection( | 224 scoped_ptr<FilterCollection> CreateFilterCollection( |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 229 base::Unretained(this)), | 240 base::Unretained(this)), |
| 230 base::Bind(&PipelineIntegrationTest::OnSetOpaque, | 241 base::Bind(&PipelineIntegrationTest::OnSetOpaque, |
| 231 base::Unretained(this)))); | 242 base::Unretained(this)))); |
| 232 collection->AddAudioRenderer(new NullAudioRenderer()); | 243 collection->AddAudioRenderer(new NullAudioRenderer()); |
| 233 return collection.Pass(); | 244 return collection.Pass(); |
| 234 } | 245 } |
| 235 | 246 |
| 236 // Verifies that seeking works properly for ChunkDemuxer when the | 247 // Verifies that seeking works properly for ChunkDemuxer when the |
| 237 // seek happens while there is a pending read on the ChunkDemuxer | 248 // seek happens while there is a pending read on the ChunkDemuxer |
| 238 // and no data is available. | 249 // and no data is available. |
| 239 void TestSeekDuringRead(const std::string& filename, | 250 bool TestSeekDuringRead(const std::string& filename, |
| 240 int initial_append_size, | 251 int initial_append_size, |
| 241 base::TimeDelta start_seek_time, | 252 base::TimeDelta start_seek_time, |
| 242 base::TimeDelta seek_time, | 253 base::TimeDelta seek_time, |
| 243 int seek_file_position, | 254 int seek_file_position, |
| 244 int seek_append_size) { | 255 int seek_append_size) { |
| 245 MockMediaSource source(filename, initial_append_size); | 256 MockMediaSource source(filename, initial_append_size); |
| 246 | 257 |
| 247 pipeline_->Start(CreateFilterCollection(&source), source.url(), | 258 pipeline_->Start(CreateFilterCollection(&source), source.url(), |
| 248 base::Bind(&PipelineIntegrationTest::OnEnded, | 259 base::Bind(&PipelineIntegrationTest::OnEnded, |
| 249 base::Unretained(this)), | 260 base::Unretained(this)), |
| 250 base::Bind(&PipelineIntegrationTest::OnError, | 261 base::Bind(&PipelineIntegrationTest::OnError, |
| 251 base::Unretained(this)), | 262 base::Unretained(this)), |
| 252 NetworkEventCB(), | 263 NetworkEventCB(), |
| 253 QuitOnStatusCB(PIPELINE_OK)); | 264 QuitOnStatusCB(PIPELINE_OK)); |
| 254 message_loop_.Run(); | 265 message_loop_.Run(); |
| 255 | 266 |
| 267 if (error_status_ != PIPELINE_OK) | |
| 268 return false; | |
| 269 | |
| 256 Play(); | 270 Play(); |
| 257 WaitUntilCurrentTimeIsAfter(start_seek_time); | 271 if (!WaitUntilCurrentTimeIsAfter(start_seek_time)) |
| 272 return false; | |
| 258 | 273 |
| 259 source.Seek(seek_file_position, seek_append_size); | 274 source.Seek(seek_file_position, seek_append_size); |
| 260 Seek(seek_time); | 275 if (!Seek(seek_time)) |
| 276 return false; | |
| 261 | 277 |
| 262 source.EndOfStream(); | 278 source.EndOfStream(); |
| 263 | 279 |
| 264 source.Abort(); | 280 source.Abort(); |
| 265 Stop(); | 281 Stop(); |
| 282 return true; | |
| 266 } | 283 } |
| 267 | 284 |
| 268 protected: | 285 protected: |
| 269 MessageLoop message_loop_; | 286 MessageLoop message_loop_; |
| 270 scoped_ptr<MessageLoopFactory> message_loop_factory_; | 287 scoped_ptr<MessageLoopFactory> message_loop_factory_; |
| 271 scoped_refptr<Pipeline> pipeline_; | 288 scoped_refptr<Pipeline> pipeline_; |
| 272 bool ended_; | 289 bool ended_; |
| 290 PipelineStatus error_status_; | |
| 273 | 291 |
| 274 private: | 292 private: |
| 275 MOCK_METHOD0(OnVideoRendererPaint, void()); | 293 MOCK_METHOD0(OnVideoRendererPaint, void()); |
| 276 MOCK_METHOD1(OnSetOpaque, void(bool)); | 294 MOCK_METHOD1(OnSetOpaque, void(bool)); |
| 277 }; | 295 }; |
| 278 | 296 |
| 279 | 297 |
| 280 TEST_F(PipelineIntegrationTest, BasicPlayback) { | 298 TEST_F(PipelineIntegrationTest, BasicPlayback) { |
| 281 Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK); | 299 ASSERT_TRUE(Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK)); |
| 282 | 300 |
| 283 Play(); | 301 Play(); |
| 284 | 302 |
| 285 WaitUntilOnEnded(); | 303 ASSERT_TRUE(WaitUntilOnEnded()); |
| 286 } | 304 } |
| 287 | 305 |
| 288 TEST_F(PipelineIntegrationTest, SeekWhilePaused) { | 306 TEST_F(PipelineIntegrationTest, SeekWhilePaused) { |
| 289 Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK); | 307 ASSERT_TRUE(Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK)); |
| 290 | 308 |
| 291 base::TimeDelta duration(pipeline_->GetMediaDuration()); | 309 base::TimeDelta duration(pipeline_->GetMediaDuration()); |
| 292 base::TimeDelta start_seek_time(duration / 4); | 310 base::TimeDelta start_seek_time(duration / 4); |
| 293 base::TimeDelta seek_time(duration * 3 / 4); | 311 base::TimeDelta seek_time(duration * 3 / 4); |
| 294 | 312 |
| 295 Play(); | 313 Play(); |
| 296 WaitUntilCurrentTimeIsAfter(start_seek_time); | 314 ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time)); |
| 297 Pause(); | 315 Pause(); |
| 298 Seek(seek_time); | 316 ASSERT_TRUE(Seek(seek_time)); |
| 299 EXPECT_EQ(pipeline_->GetCurrentTime(), seek_time); | 317 EXPECT_EQ(pipeline_->GetCurrentTime(), seek_time); |
| 300 Play(); | 318 Play(); |
| 301 WaitUntilOnEnded(); | 319 ASSERT_TRUE(WaitUntilOnEnded()); |
| 302 | 320 |
| 303 // Make sure seeking after reaching the end works as expected. | 321 // Make sure seeking after reaching the end works as expected. |
| 304 Pause(); | 322 Pause(); |
| 305 Seek(seek_time); | 323 ASSERT_TRUE(Seek(seek_time)); |
| 306 EXPECT_EQ(pipeline_->GetCurrentTime(), seek_time); | 324 EXPECT_EQ(pipeline_->GetCurrentTime(), seek_time); |
| 307 Play(); | 325 Play(); |
| 308 WaitUntilOnEnded(); | 326 ASSERT_TRUE(WaitUntilOnEnded()); |
| 309 } | 327 } |
| 310 | 328 |
| 311 // TODO(acolwell): Fix flakiness http://crbug.com/109875 | 329 // TODO(acolwell): Fix flakiness http://crbug.com/109875 |
| 312 TEST_F(PipelineIntegrationTest, DISABLED_SeekWhilePlaying) { | 330 TEST_F(PipelineIntegrationTest, FLAKY_SeekWhilePlaying) { |
| 313 Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK); | 331 ASSERT_TRUE(Start(GetTestDataURL("bear-320x240.webm"), PIPELINE_OK)); |
| 314 | 332 |
| 315 base::TimeDelta duration(pipeline_->GetMediaDuration()); | 333 base::TimeDelta duration(pipeline_->GetMediaDuration()); |
| 316 base::TimeDelta start_seek_time(duration / 4); | 334 base::TimeDelta start_seek_time(duration / 4); |
| 317 base::TimeDelta seek_time(duration * 3 / 4); | 335 base::TimeDelta seek_time(duration * 3 / 4); |
| 318 | 336 |
| 319 Play(); | 337 Play(); |
| 320 WaitUntilCurrentTimeIsAfter(start_seek_time); | 338 ASSERT_TRUE(WaitUntilCurrentTimeIsAfter(start_seek_time)); |
| 321 Seek(seek_time); | 339 ASSERT_TRUE(Seek(seek_time)); |
| 322 EXPECT_GE(pipeline_->GetCurrentTime(), seek_time); | 340 EXPECT_GE(pipeline_->GetCurrentTime(), seek_time); |
| 323 WaitUntilOnEnded(); | 341 ASSERT_TRUE(WaitUntilOnEnded()); |
| 324 | 342 |
| 325 // Make sure seeking after reaching the end works as expected. | 343 // Make sure seeking after reaching the end works as expected. |
| 326 Seek(seek_time); | 344 ASSERT_TRUE(Seek(seek_time)); |
| 327 EXPECT_GE(pipeline_->GetCurrentTime(), seek_time); | 345 EXPECT_GE(pipeline_->GetCurrentTime(), seek_time); |
| 328 WaitUntilOnEnded(); | 346 ASSERT_TRUE(WaitUntilOnEnded()); |
| 329 } | 347 } |
| 330 | 348 |
| 331 // Verify audio decoder & renderer can handle aborted demuxer reads. | 349 // Verify audio decoder & renderer can handle aborted demuxer reads. |
| 332 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) { | 350 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) { |
| 333 TestSeekDuringRead("bear-320x240-audio-only.webm", 8192, | 351 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", 8192, |
| 334 base::TimeDelta::FromMilliseconds(477), | 352 base::TimeDelta::FromMilliseconds(477), |
| 335 base::TimeDelta::FromMilliseconds(617), | 353 base::TimeDelta::FromMilliseconds(617), |
| 336 0x10CA, 19730); | 354 0x10CA, 19730)); |
| 337 } | 355 } |
| 338 | 356 |
| 339 // Verify video decoder & renderer can handle aborted demuxer reads. | 357 // Verify video decoder & renderer can handle aborted demuxer reads. |
| 340 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { | 358 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) { |
| 341 TestSeekDuringRead("bear-320x240-video-only.webm", 32768, | 359 ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", 32768, |
| 342 base::TimeDelta::FromMilliseconds(200), | 360 base::TimeDelta::FromMilliseconds(200), |
| 343 base::TimeDelta::FromMilliseconds(1668), | 361 base::TimeDelta::FromMilliseconds(1668), |
| 344 0x1C896, 65536); | 362 0x1C896, 65536)); |
| 345 } | 363 } |
| 346 | 364 |
| 347 } // namespace media | 365 } // namespace media |
| OLD | NEW |