pipeline_integration_test_base.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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 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(_)).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 124bool 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 135bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) { 136 return Start(file_path, NULL); 137} 138 139bool 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 159void PipelineIntegrationTestBase::Play() { 160 pipeline_->SetPlaybackRate(1); 161} 162 163void PipelineIntegrationTestBase::Pause() { 164 pipeline_->SetPlaybackRate(0); 165} 166 167bool 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 176void PipelineIntegrationTestBase::Stop() { 177 DCHECK(pipeline_->IsRunning()); 178 pipeline_->Stop(base::MessageLoop::QuitClosure()); 179 message_loop_.Run(); 180} 181 182void 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 197bool 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 213scoped_ptr<FilterCollection> 214PipelineIntegrationTestBase::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)); 219 data_source_.reset(file_data_source); 220 221 Demuxer::NeedKeyCB need_key_cb = base::Bind( 222 &PipelineIntegrationTestBase::DemuxerNeedKeyCB, base::Unretained(this)); 223 scoped_ptr<Demuxer> demuxer( 224 new FFmpegDemuxer(message_loop_.message_loop_proxy(), 225 data_source_.get(), 226 need_key_cb, 227 new MediaLog())); 228 return CreateFilterCollection(demuxer.Pass(), decryptor); 229} 230 231scoped_ptr<FilterCollection> 232PipelineIntegrationTestBase::CreateFilterCollection( 233 scoped_ptr<Demuxer> demuxer, 234 Decryptor* decryptor) { 235 demuxer_ = demuxer.Pass(); 236 237 scoped_ptr<FilterCollection> collection(new FilterCollection()); 238 collection->SetDemuxer(demuxer_.get()); 239 240 ScopedVector<VideoDecoder> video_decoders; 241 video_decoders.push_back( 242 new VpxVideoDecoder(message_loop_.message_loop_proxy())); 243 video_decoders.push_back( 244 new FFmpegVideoDecoder(message_loop_.message_loop_proxy())); 245 246 // Disable frame dropping if hashing is enabled. 247 scoped_ptr<VideoRenderer> renderer(new VideoRendererImpl( 248 message_loop_.message_loop_proxy(), 249 video_decoders.Pass(), 250 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 251 base::Unretained(this), 252 decryptor), 253 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, 254 base::Unretained(this)), 255 false)); 256 collection->SetVideoRenderer(renderer.Pass()); 257 258 if (!clockless_playback_) { 259 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy()); 260 } else { 261 clockless_audio_sink_ = new ClocklessAudioSink(); 262 } 263 264 ScopedVector<AudioDecoder> audio_decoders; 265 audio_decoders.push_back( 266 new FFmpegAudioDecoder(message_loop_.message_loop_proxy())); 267 audio_decoders.push_back( 268 new OpusAudioDecoder(message_loop_.message_loop_proxy())); 269 270 AudioParameters out_params(AudioParameters::AUDIO_PCM_LOW_LATENCY, 271 CHANNEL_LAYOUT_STEREO, 272 44100, 273 16, 274 512); 275 hardware_config_.UpdateOutputConfig(out_params); 276 277 AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl( 278 message_loop_.message_loop_proxy(), 279 (clockless_playback_) 280 ? static_cast<AudioRendererSink*>(clockless_audio_sink_.get()) 281 : audio_sink_.get(), 282 audio_decoders.Pass(), 283 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 284 base::Unretained(this), 285 decryptor), 286 &hardware_config_); 287 // Disable underflow if hashing is enabled. 288 if (hashing_enabled_) { 289 audio_sink_->StartAudioHashForTesting(); 290 audio_renderer_impl->DisableUnderflowForTesting(); 291 } 292 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl); 293 collection->SetAudioRenderer(audio_renderer.Pass()); 294 295 return collection.Pass(); 296} 297 298void PipelineIntegrationTestBase::SetDecryptor( 299 Decryptor* decryptor, 300 const DecryptorReadyCB& decryptor_ready_cb) { 301 decryptor_ready_cb.Run(decryptor); 302} 303 304void PipelineIntegrationTestBase::OnVideoRendererPaint( 305 const scoped_refptr<VideoFrame>& frame) { 306 last_video_frame_format_ = frame->format(); 307 if (!hashing_enabled_) 308 return; 309 frame->HashFrameForTesting(&md5_context_); 310} 311 312std::string PipelineIntegrationTestBase::GetVideoHash() { 313 DCHECK(hashing_enabled_); 314 base::MD5Digest digest; 315 base::MD5Final(&digest, &md5_context_); 316 return base::MD5DigestToBase16(digest); 317} 318 319std::string PipelineIntegrationTestBase::GetAudioHash() { 320 DCHECK(hashing_enabled_); 321 return audio_sink_->GetAudioHashForTesting(); 322} 323 324base::TimeDelta PipelineIntegrationTestBase::GetAudioTime() { 325 DCHECK(clockless_playback_); 326 return clockless_audio_sink_->render_time(); 327} 328 329base::TimeTicks DummyTickClock::NowTicks() { 330 now_ += base::TimeDelta::FromSeconds(60); 331 return now_; 332} 333 334} // namespace media 335