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