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/media_log.h" 10#include "media/filters/audio_renderer_impl.h" 11#include "media/filters/chunk_demuxer.h" 12#include "media/filters/ffmpeg_audio_decoder.h" 13#include "media/filters/ffmpeg_demuxer.h" 14#include "media/filters/ffmpeg_video_decoder.h" 15#include "media/filters/file_data_source.h" 16#include "media/filters/opus_audio_decoder.h" 17#include "media/filters/renderer_impl.h" 18#include "media/filters/vpx_video_decoder.h" 19 20using ::testing::_; 21using ::testing::AnyNumber; 22using ::testing::AtMost; 23using ::testing::SaveArg; 24 25namespace media { 26 27const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e"; 28const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,"; 29 30PipelineIntegrationTestBase::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 42PipelineIntegrationTestBase::~PipelineIntegrationTestBase() { 43 if (!pipeline_->IsRunning()) 44 return; 45 46 Stop(); 47} 48 49void PipelineIntegrationTestBase::OnStatusCallback( 50 PipelineStatus status) { 51 pipeline_status_ = status; 52 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 53} 54 55void PipelineIntegrationTestBase::OnStatusCallbackChecked( 56 PipelineStatus expected_status, 57 PipelineStatus status) { 58 EXPECT_EQ(expected_status, status); 59 OnStatusCallback(status); 60} 61 62PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB( 63 PipelineStatus expected_status) { 64 return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked, 65 base::Unretained(this), 66 expected_status); 67} 68 69void 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 77void PipelineIntegrationTestBase::OnEnded() { 78 DCHECK(!ended_); 79 ended_ = true; 80 pipeline_status_ = PIPELINE_OK; 81 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 82} 83 84bool 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 92PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() { 93 if (ended_ || pipeline_status_ != PIPELINE_OK) 94 return pipeline_status_; 95 message_loop_.Run(); 96 return pipeline_status_; 97} 98 99void 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 105bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 106 PipelineStatus expected_status) { 107 EXPECT_CALL(*this, OnMetadata(_)) 108 .Times(AtMost(1)) 109 .WillRepeatedly(SaveArg<0>(&metadata_)); 110 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH)) 111 .Times(AtMost(1)); 112 CreateDemuxer(file_path); 113 pipeline_->Start( 114 demuxer_.get(), 115 CreateRenderer(NULL), 116 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), 117 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), 118 QuitOnStatusCB(expected_status), 119 base::Bind(&PipelineIntegrationTestBase::OnMetadata, 120 base::Unretained(this)), 121 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged, 122 base::Unretained(this)), 123 base::Closure(), 124 base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack, 125 base::Unretained(this))); 126 message_loop_.Run(); 127 return (pipeline_status_ == PIPELINE_OK); 128} 129 130bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 131 PipelineStatus expected_status, 132 kTestType test_type) { 133 hashing_enabled_ = test_type == kHashed; 134 clockless_playback_ = test_type == kClockless; 135 return Start(file_path, expected_status); 136} 137 138bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) { 139 return Start(file_path, NULL); 140} 141 142bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 143 Decryptor* decryptor) { 144 EXPECT_CALL(*this, OnMetadata(_)) 145 .Times(AtMost(1)) 146 .WillRepeatedly(SaveArg<0>(&metadata_)); 147 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH)) 148 .Times(AtMost(1)); 149 150 CreateDemuxer(file_path); 151 pipeline_->Start( 152 demuxer_.get(), 153 CreateRenderer(decryptor), 154 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), 155 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), 156 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback, 157 base::Unretained(this)), 158 base::Bind(&PipelineIntegrationTestBase::OnMetadata, 159 base::Unretained(this)), 160 base::Bind(&PipelineIntegrationTestBase::OnBufferingStateChanged, 161 base::Unretained(this)), 162 base::Closure(), 163 base::Bind(&PipelineIntegrationTestBase::OnAddTextTrack, 164 base::Unretained(this))); 165 message_loop_.Run(); 166 return (pipeline_status_ == PIPELINE_OK); 167} 168 169void PipelineIntegrationTestBase::Play() { 170 pipeline_->SetPlaybackRate(1); 171} 172 173void PipelineIntegrationTestBase::Pause() { 174 pipeline_->SetPlaybackRate(0); 175} 176 177bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) { 178 ended_ = false; 179 180 EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH)); 181 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK)); 182 message_loop_.Run(); 183 return (pipeline_status_ == PIPELINE_OK); 184} 185 186void PipelineIntegrationTestBase::Stop() { 187 DCHECK(pipeline_->IsRunning()); 188 pipeline_->Stop(base::MessageLoop::QuitClosure()); 189 message_loop_.Run(); 190} 191 192void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask( 193 const base::TimeDelta& quit_time) { 194 if (pipeline_->GetMediaTime() >= quit_time || 195 pipeline_status_ != PIPELINE_OK) { 196 message_loop_.Quit(); 197 return; 198 } 199 200 message_loop_.PostDelayedTask( 201 FROM_HERE, 202 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, 203 base::Unretained(this), quit_time), 204 base::TimeDelta::FromMilliseconds(10)); 205} 206 207bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter( 208 const base::TimeDelta& wait_time) { 209 DCHECK(pipeline_->IsRunning()); 210 DCHECK_GT(pipeline_->GetPlaybackRate(), 0); 211 DCHECK(wait_time <= pipeline_->GetMediaDuration()); 212 213 message_loop_.PostDelayedTask( 214 FROM_HERE, 215 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, 216 base::Unretained(this), 217 wait_time), 218 base::TimeDelta::FromMilliseconds(10)); 219 message_loop_.Run(); 220 return (pipeline_status_ == PIPELINE_OK); 221} 222 223void PipelineIntegrationTestBase::CreateDemuxer( 224 const base::FilePath& file_path) { 225 FileDataSource* file_data_source = new FileDataSource(); 226 CHECK(file_data_source->Initialize(file_path)) << "Is " << file_path.value() 227 << " missing?"; 228 data_source_.reset(file_data_source); 229 230 Demuxer::NeedKeyCB need_key_cb = base::Bind( 231 &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this)); 232 demuxer_ = 233 scoped_ptr<Demuxer>(new FFmpegDemuxer(message_loop_.message_loop_proxy(), 234 data_source_.get(), 235 need_key_cb, 236 new MediaLog())); 237} 238 239scoped_ptr<Renderer> PipelineIntegrationTestBase::CreateRenderer( 240 Decryptor* decryptor) { 241 ScopedVector<VideoDecoder> video_decoders; 242#if !defined(MEDIA_DISABLE_LIBVPX) 243 video_decoders.push_back( 244 new VpxVideoDecoder(message_loop_.message_loop_proxy())); 245#endif // !defined(MEDIA_DISABLE_LIBVPX) 246 video_decoders.push_back( 247 new FFmpegVideoDecoder(message_loop_.message_loop_proxy())); 248 249 // Disable frame dropping if hashing is enabled. 250 scoped_ptr<VideoRenderer> video_renderer(new VideoRendererImpl( 251 message_loop_.message_loop_proxy(), 252 video_decoders.Pass(), 253 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 254 base::Unretained(this), 255 decryptor), 256 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, 257 base::Unretained(this)), 258 false, 259 new MediaLog())); 260 261 if (!clockless_playback_) { 262 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy()); 263 } else { 264 clockless_audio_sink_ = new ClocklessAudioSink(); 265 } 266 267 ScopedVector<AudioDecoder> audio_decoders; 268 audio_decoders.push_back( 269 new FFmpegAudioDecoder(message_loop_.message_loop_proxy(), LogCB())); 270 audio_decoders.push_back( 271 new OpusAudioDecoder(message_loop_.message_loop_proxy())); 272 273 AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY, 274 CHANNEL_LAYOUT_STEREO, 275 44100, 276 16, 277 512); 278 hardware_config_.UpdateOutputConfig(out_params); 279 280 scoped_ptr<AudioRenderer> audio_renderer(new AudioRendererImpl( 281 message_loop_.message_loop_proxy(), 282 (clockless_playback_) 283 ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get()) 284 : audio_sink_.get(), 285 audio_decoders.Pass(), 286 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 287 base::Unretained(this), 288 decryptor), 289 hardware_config_, 290 new MediaLog())); 291 if (hashing_enabled_) 292 audio_sink_->StartAudioHashForTesting(); 293 294 scoped_ptr<RendererImpl> renderer_impl( 295 new RendererImpl(message_loop_.message_loop_proxy(), 296 demuxer_.get(), 297 audio_renderer.Pass(), 298 video_renderer.Pass())); 299 300 // Prevent non-deterministic buffering state callbacks from firing (e.g., slow 301 // machine, valgrind). 302 renderer_impl->DisableUnderflowForTesting(); 303 304 if (clockless_playback_) 305 renderer_impl->EnableClocklessVideoPlaybackForTesting(); 306 307 return renderer_impl.PassAs<Renderer>(); 308} 309 310void PipelineIntegrationTestBase::SetDecryptor( 311 Decryptor* decryptor, 312 const DecryptorReadyCB& decryptor_ready_cb) { 313 decryptor_ready_cb.Run( 314 decryptor, 315 base::Bind(&PipelineIntegrationTestBase::DecryptorAttached, 316 base::Unretained(this))); 317 EXPECT_CALL(*this, DecryptorAttached(true)); 318} 319 320void PipelineIntegrationTestBase::OnVideoRendererPaint( 321 const scoped_refptr<VideoFrame>& frame) { 322 last_video_frame_format_ = frame->format(); 323 if (!hashing_enabled_) 324 return; 325 frame->HashFrameForTesting(&md5_context_); 326} 327 328std::string PipelineIntegrationTestBase::GetVideoHash() { 329 DCHECK(hashing_enabled_); 330 base::MD5Digest digest; 331 base::MD5Final(&digest, &md5_context_); 332 return base::MD5DigestToBase16(digest); 333} 334 335std::string PipelineIntegrationTestBase::GetAudioHash() { 336 DCHECK(hashing_enabled_); 337 return audio_sink_->GetAudioHashForTesting(); 338} 339 340base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() { 341 DCHECK(clockless_playback_); 342 return clockless_audio_sink_->render_time(); 343} 344 345base::TimeTicks DummyTickClock::NowTicks() { 346 now_ += base::TimeDelta::FromSeconds(60); 347 return now_; 348} 349 350} // namespace media 351