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 19 using ::testing::AnyNumber; 20 using ::testing::AtMost; 21 22 namespace media { 23 24 const char kNullVideoHash[] = "d41d8cd98f00b204e9800998ecf8427e"; 25 const char kNullAudioHash[] = "0.00,0.00,0.00,0.00,0.00,0.00,"; 26 27 PipelineIntegrationTestBase::PipelineIntegrationTestBase() 28 : hashing_enabled_(false), 29 pipeline_(new Pipeline(message_loop_.message_loop_proxy(), 30 new MediaLog())), 31 ended_(false), 32 pipeline_status_(PIPELINE_OK), 33 last_video_frame_format_(VideoFrame::INVALID) { 34 base::MD5Init(&md5_context_); 35 EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber()); 36 } 37 38 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() { 39 if (!pipeline_->IsRunning()) 40 return; 41 42 Stop(); 43 } 44 45 void PipelineIntegrationTestBase::OnStatusCallback( 46 PipelineStatus status) { 47 pipeline_status_ = status; 48 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 49 } 50 51 void PipelineIntegrationTestBase::OnStatusCallbackChecked( 52 PipelineStatus expected_status, 53 PipelineStatus status) { 54 EXPECT_EQ(expected_status, status); 55 OnStatusCallback(status); 56 } 57 58 PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB( 59 PipelineStatus expected_status) { 60 return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked, 61 base::Unretained(this), 62 expected_status); 63 } 64 65 void PipelineIntegrationTestBase::DemuxerNeedKeyCB( 66 const std::string& type, 67 scoped_ptr<uint8[]> init_data, 68 int init_data_size) { 69 DCHECK(init_data.get()); 70 DCHECK_GT(init_data_size, 0); 71 CHECK(!need_key_cb_.is_null()); 72 need_key_cb_.Run(std::string(), type, init_data.Pass(), init_data_size); 73 } 74 75 void PipelineIntegrationTestBase::OnEnded() { 76 DCHECK(!ended_); 77 ended_ = true; 78 pipeline_status_ = PIPELINE_OK; 79 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 80 } 81 82 bool PipelineIntegrationTestBase::WaitUntilOnEnded() { 83 if (ended_) 84 return (pipeline_status_ == PIPELINE_OK); 85 message_loop_.Run(); 86 EXPECT_TRUE(ended_); 87 return ended_ && (pipeline_status_ == PIPELINE_OK); 88 } 89 90 PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() { 91 if (ended_ || pipeline_status_ != PIPELINE_OK) 92 return pipeline_status_; 93 message_loop_.Run(); 94 return pipeline_status_; 95 } 96 97 void PipelineIntegrationTestBase::OnError(PipelineStatus status) { 98 DCHECK_NE(status, PIPELINE_OK); 99 pipeline_status_ = status; 100 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 101 } 102 103 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 104 PipelineStatus expected_status) { 105 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) 106 .Times(AtMost(1)); 107 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) 108 .Times(AtMost(1)); 109 pipeline_->Start( 110 CreateFilterCollection(file_path, NULL), 111 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), 112 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), 113 QuitOnStatusCB(expected_status), 114 base::Bind(&PipelineIntegrationTestBase::OnBufferingState, 115 base::Unretained(this)), 116 base::Closure()); 117 message_loop_.Run(); 118 return (pipeline_status_ == PIPELINE_OK); 119 } 120 121 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 122 PipelineStatus expected_status, 123 bool hashing_enabled) { 124 hashing_enabled_ = hashing_enabled; 125 return Start(file_path, expected_status); 126 } 127 128 bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) { 129 return Start(file_path, NULL); 130 } 131 132 bool 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 151 void PipelineIntegrationTestBase::Play() { 152 pipeline_->SetPlaybackRate(1); 153 } 154 155 void PipelineIntegrationTestBase::Pause() { 156 pipeline_->SetPlaybackRate(0); 157 } 158 159 bool 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 168 void PipelineIntegrationTestBase::Stop() { 169 DCHECK(pipeline_->IsRunning()); 170 pipeline_->Stop(base::MessageLoop::QuitClosure()); 171 message_loop_.Run(); 172 } 173 174 void 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 189 bool 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 205 scoped_ptr<FilterCollection> 206 PipelineIntegrationTestBase::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 media::FFmpegNeedKeyCB 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 223 scoped_ptr<FilterCollection> 224 PipelineIntegrationTestBase::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 ScopedVector<VideoDecoder> video_decoders; 233 video_decoders.push_back( 234 new VpxVideoDecoder(message_loop_.message_loop_proxy())); 235 video_decoders.push_back( 236 new FFmpegVideoDecoder(message_loop_.message_loop_proxy())); 237 238 // Disable frame dropping if hashing is enabled. 239 scoped_ptr<VideoRenderer> renderer(new VideoRendererBase( 240 message_loop_.message_loop_proxy(), 241 video_decoders.Pass(), 242 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 243 base::Unretained(this), decryptor), 244 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, 245 base::Unretained(this)), 246 base::Bind(&PipelineIntegrationTestBase::OnSetOpaque, 247 base::Unretained(this)), 248 !hashing_enabled_)); 249 collection->SetVideoRenderer(renderer.Pass()); 250 251 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy()); 252 253 ScopedVector<AudioDecoder> audio_decoders; 254 audio_decoders.push_back( 255 new FFmpegAudioDecoder(message_loop_.message_loop_proxy())); 256 audio_decoders.push_back( 257 new OpusAudioDecoder(message_loop_.message_loop_proxy())); 258 259 AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl( 260 message_loop_.message_loop_proxy(), 261 audio_sink_.get(), 262 audio_decoders.Pass(), 263 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 264 base::Unretained(this), 265 decryptor), 266 true); 267 // Disable underflow if hashing is enabled. 268 if (hashing_enabled_) { 269 audio_sink_->StartAudioHashForTesting(); 270 audio_renderer_impl->DisableUnderflowForTesting(); 271 } 272 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl); 273 collection->SetAudioRenderer(audio_renderer.Pass()); 274 275 return collection.Pass(); 276 } 277 278 void PipelineIntegrationTestBase::SetDecryptor( 279 Decryptor* decryptor, 280 const DecryptorReadyCB& decryptor_ready_cb) { 281 decryptor_ready_cb.Run(decryptor); 282 } 283 284 void PipelineIntegrationTestBase::OnVideoRendererPaint( 285 const scoped_refptr<VideoFrame>& frame) { 286 last_video_frame_format_ = frame->format(); 287 if (!hashing_enabled_) 288 return; 289 frame->HashFrameForTesting(&md5_context_); 290 } 291 292 std::string PipelineIntegrationTestBase::GetVideoHash() { 293 DCHECK(hashing_enabled_); 294 base::MD5Digest digest; 295 base::MD5Final(&digest, &md5_context_); 296 return base::MD5DigestToBase16(digest); 297 } 298 299 std::string PipelineIntegrationTestBase::GetAudioHash() { 300 DCHECK(hashing_enabled_); 301 return audio_sink_->GetAudioHashForTesting(); 302 } 303 304 } // namespace media 305