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 3 // found in the LICENSE file. 4 5 #include "media/filters/pipeline_integration_test_base.h" 6 7 #include "base/bind.h" 8 #include "base/memory/scoped_vector.h" 9 #include "media/base/clock.h" 10 #include "media/base/media_log.h" 11 #include "media/filters/audio_renderer_impl.h" 12 #include "media/filters/chunk_demuxer.h" 13 #include "media/filters/ffmpeg_audio_decoder.h" 14 #include "media/filters/ffmpeg_demuxer.h" 15 #include "media/filters/ffmpeg_video_decoder.h" 16 #include "media/filters/file_data_source.h" 17 #include "media/filters/opus_audio_decoder.h" 18 #include "media/filters/vpx_video_decoder.h" 19 20 using ::testing::AnyNumber; 21 using ::testing::AtMost; 22 23 namespace media { 24 25 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e"; 26 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,"; 27 28 PipelineIntegrationTestBase::PipelineIntegrationTestBase() 29 : hashing_enabled_(false), 30 clockless_playback_(false), 31 pipeline_(new Pipeline(message_loop_.message_loop_proxy(), 32 new MediaLog())), 33 ended_(false), 34 pipeline_status_(PIPELINE_OK), 35 last_video_frame_format_(VideoFrame::UNKNOWN) { 36 base::MD5Init(&md5_context_); 37 EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber()); 38 } 39 40 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() { 41 if (!pipeline_->IsRunning()) 42 return; 43 44 Stop(); 45 } 46 47 void PipelineIntegrationTestBase::OnStatusCallback( 48 PipelineStatus status) { 49 pipeline_status_ = status; 50 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 51 } 52 53 void PipelineIntegrationTestBase::OnStatusCallbackChecked( 54 PipelineStatus expected_status, 55 PipelineStatus status) { 56 EXPECT_EQ(expected_status, status); 57 OnStatusCallback(status); 58 } 59 60 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB( 61 PipelineStatus expected_status) { 62 return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked, 63 base::Unretained(this), 64 expected_status); 65 } 66 67 void PipelineIntegrationTestBase::DemuxerNeedKeyCB( 68 const std::string& type, 69 const std::vector<uint8>& init_data) { 70 DCHECK(!init_data.empty()); 71 CHECK(!need_key_cb_.is_null()); 72 need_key_cb_.Run(type, init_data); 73 } 74 75 void PipelineIntegrationTestBase::OnEnded() { 76 DCHECK(!ended_); 77 ended_ = true; 78 pipeline_status_ = PIPELINE_OK; 79 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 80 } 81 82 bool PipelineIntegrationTestBase::WaitUntilOnEnded() { 83 if (ended_) 84 return (pipeline_status_ == PIPELINE_OK); 85 message_loop_.Run(); 86 EXPECT_TRUE(ended_); 87 return ended_ && (pipeline_status_ == PIPELINE_OK); 88 } 89 90 PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() { 91 if (ended_ || pipeline_status_ != PIPELINE_OK) 92 return pipeline_status_; 93 message_loop_.Run(); 94 return pipeline_status_; 95 } 96 97 void PipelineIntegrationTestBase::OnError(PipelineStatus status) { 98 DCHECK_NE(status, PIPELINE_OK); 99 pipeline_status_ = status; 100 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 101 } 102 103 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 104 PipelineStatus expected_status) { 105 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) 106 .Times(AtMost(1)); 107 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) 108 .Times(AtMost(1)); 109 pipeline_->Start( 110 CreateFilterCollection(file_path, NULL), 111 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), 112 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), 113 QuitOnStatusCB(expected_status), 114 base::Bind(&PipelineIntegrationTestBase::OnBufferingState, 115 base::Unretained(this)), 116 base::Closure()); 117 message_loop_.Run(); 118 return (pipeline_status_ == PIPELINE_OK); 119 } 120 121 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 122 PipelineStatus expected_status, 123 kTestType test_type) { 124 hashing_enabled_ = test_type == kHashed; 125 clockless_playback_ = test_type == kClockless; 126 if (clockless_playback_) { 127 pipeline_->SetClockForTesting(new Clock(&dummy_clock_)); 128 } 129 return Start(file_path, expected_status); 130 } 131 132 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) { 133 return Start(file_path, NULL); 134 } 135 136 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 137 Decryptor* decryptor) { 138 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) 139 .Times(AtMost(1)); 140 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) 141 .Times(AtMost(1)); 142 pipeline_->Start( 143 CreateFilterCollection(file_path, decryptor), 144 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), 145 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), 146 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback, 147 base::Unretained(this)), 148 base::Bind(&PipelineIntegrationTestBase::OnBufferingState, 149 base::Unretained(this)), 150 base::Closure()); 151 message_loop_.Run(); 152 return (pipeline_status_ == PIPELINE_OK); 153 } 154 155 void PipelineIntegrationTestBase::Play() { 156 pipeline_->SetPlaybackRate(1); 157 } 158 159 void PipelineIntegrationTestBase::Pause() { 160 pipeline_->SetPlaybackRate(0); 161 } 162 163 bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) { 164 ended_ = false; 165 166 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)); 167 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK)); 168 message_loop_.Run(); 169 return (pipeline_status_ == PIPELINE_OK); 170 } 171 172 void PipelineIntegrationTestBase::Stop() { 173 DCHECK(pipeline_->IsRunning()); 174 pipeline_->Stop(base::MessageLoop::QuitClosure()); 175 message_loop_.Run(); 176 } 177 178 void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask( 179 const base::TimeDelta& quit_time) { 180 if (pipeline_->GetMediaTime() >= quit_time || 181 pipeline_status_ != PIPELINE_OK) { 182 message_loop_.Quit(); 183 return; 184 } 185 186 message_loop_.PostDelayedTask( 187 FROM_HERE, 188 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, 189 base::Unretained(this), quit_time), 190 base::TimeDelta::FromMilliseconds(10)); 191 } 192 193 bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter( 194 const base::TimeDelta& wait_time) { 195 DCHECK(pipeline_->IsRunning()); 196 DCHECK_GT(pipeline_->GetPlaybackRate(), 0); 197 DCHECK(wait_time <= pipeline_->GetMediaDuration()); 198 199 message_loop_.PostDelayedTask( 200 FROM_HERE, 201 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, 202 base::Unretained(this), 203 wait_time), 204 base::TimeDelta::FromMilliseconds(10)); 205 message_loop_.Run(); 206 return (pipeline_status_ == PIPELINE_OK); 207 } 208 209 scoped_ptr<FilterCollection> 210 PipelineIntegrationTestBase::CreateFilterCollection( 211 const base::FilePath& file_path, 212 Decryptor* decryptor) { 213 FileDataSource* file_data_source = new FileDataSource(); 214 CHECK(file_data_source->Initialize(file_path)); 215 data_source_.reset(file_data_source); 216 217 Demuxer::NeedKeyCB need_key_cb = base::Bind( 218 &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this)); 219 scoped_ptr<Demuxer> demuxer( 220 new FFmpegDemuxer(message_loop_.message_loop_proxy(), 221 data_source_.get(), 222 need_key_cb, 223 new MediaLog())); 224 return CreateFilterCollection(demuxer.Pass(), decryptor); 225 } 226 227 scoped_ptr<FilterCollection> 228 PipelineIntegrationTestBase::CreateFilterCollection( 229 scoped_ptr<Demuxer> demuxer, 230 Decryptor* decryptor) { 231 demuxer_ = demuxer.Pass(); 232 233 scoped_ptr<FilterCollection> collection(new FilterCollection()); 234 collection->SetDemuxer(demuxer_.get()); 235 236 ScopedVector<VideoDecoder> video_decoders; 237 video_decoders.push_back( 238 new VpxVideoDecoder(message_loop_.message_loop_proxy())); 239 video_decoders.push_back( 240 new FFmpegVideoDecoder(message_loop_.message_loop_proxy())); 241 242 // Disable frame dropping if hashing is enabled. 243 scoped_ptr<VideoRenderer> renderer(new VideoRendererImpl( 244 message_loop_.message_loop_proxy(), 245 video_decoders.Pass(), 246 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 247 base::Unretained(this), 248 decryptor), 249 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, 250 base::Unretained(this)), 251 base::Bind(&PipelineIntegrationTestBase::OnSetOpaque, 252 base::Unretained(this)), 253 false)); 254 collection->SetVideoRenderer(renderer.Pass()); 255 256 if (!clockless_playback_) { 257 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy()); 258 } else { 259 clockless_audio_sink_ = new ClocklessAudioSink(); 260 } 261 262 ScopedVector<AudioDecoder> audio_decoders; 263 audio_decoders.push_back( 264 new FFmpegAudioDecoder(message_loop_.message_loop_proxy())); 265 audio_decoders.push_back( 266 new OpusAudioDecoder(message_loop_.message_loop_proxy())); 267 268 AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl( 269 message_loop_.message_loop_proxy(), 270 (clockless_playback_) 271 ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get()) 272 : audio_sink_.get(), 273 audio_decoders.Pass(), 274 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 275 base::Unretained(this), 276 decryptor)); 277 // Disable underflow if hashing is enabled. 278 if (hashing_enabled_) { 279 audio_sink_->StartAudioHashForTesting(); 280 audio_renderer_impl->DisableUnderflowForTesting(); 281 } 282 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl); 283 collection->SetAudioRenderer(audio_renderer.Pass()); 284 285 return collection.Pass(); 286 } 287 288 void PipelineIntegrationTestBase::SetDecryptor( 289 Decryptor* decryptor, 290 const DecryptorReadyCB& decryptor_ready_cb) { 291 decryptor_ready_cb.Run(decryptor); 292 } 293 294 void PipelineIntegrationTestBase::OnVideoRendererPaint( 295 const scoped_refptr<VideoFrame>& frame) { 296 last_video_frame_format_ = frame->format(); 297 if (!hashing_enabled_) 298 return; 299 frame->HashFrameForTesting(&md5_context_); 300 } 301 302 std::string PipelineIntegrationTestBase::GetVideoHash() { 303 DCHECK(hashing_enabled_); 304 base::MD5Digest digest; 305 base::MD5Final(&digest, &md5_context_); 306 return base::MD5DigestToBase16(digest); 307 } 308 309 std::string PipelineIntegrationTestBase::GetAudioHash() { 310 DCHECK(hashing_enabled_); 311 return audio_sink_->GetAudioHashForTesting(); 312 } 313 314 base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() { 315 DCHECK(clockless_playback_); 316 return clockless_audio_sink_->render_time(); 317 } 318 319 base::TimeTicks DummyTickClock::NowTicks() { 320 now_ += base::TimeDelta::FromSeconds(60); 321 return now_; 322 } 323 324 } // namespace media 325