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