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