pipeline_integration_test_base.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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 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 38PipelineIntegrationTestBase::~PipelineIntegrationTestBase() { 39 if (!pipeline_->IsRunning()) 40 return; 41 42 Stop(); 43} 44 45void PipelineIntegrationTestBase::OnStatusCallback( 46 PipelineStatus status) { 47 pipeline_status_ = status; 48 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 49} 50 51void PipelineIntegrationTestBase::OnStatusCallbackChecked( 52 PipelineStatus expected_status, 53 PipelineStatus status) { 54 EXPECT_EQ(expected_status, status); 55 OnStatusCallback(status); 56} 57 58PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB( 59 PipelineStatus expected_status) { 60 return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked, 61 base::Unretained(this), 62 expected_status); 63} 64 65void 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 75void PipelineIntegrationTestBase::OnEnded() { 76 DCHECK(!ended_); 77 ended_ = true; 78 pipeline_status_ = PIPELINE_OK; 79 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 80} 81 82bool 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 90PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() { 91 if (ended_ || pipeline_status_ != PIPELINE_OK) 92 return pipeline_status_; 93 message_loop_.Run(); 94 return pipeline_status_; 95} 96 97void 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 103bool 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 121bool 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 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 media::FFmpegNeedKeyCB need_key_cb = 214 base::Bind(&PipelineIntegrationTestBase::DemuxerNeedKeyCB, 215 base::Unretained(this)); 216 scoped_ptr<Demuxer> demuxer(new FFmpegDemuxer( 217 message_loop_.message_loop_proxy(), data_source_.get(), need_key_cb)); 218 return CreateFilterCollection(demuxer.Pass(), decryptor); 219} 220 221scoped_ptr<FilterCollection> 222PipelineIntegrationTestBase::CreateFilterCollection( 223 scoped_ptr<Demuxer> demuxer, 224 Decryptor* decryptor) { 225 demuxer_ = demuxer.Pass(); 226 227 scoped_ptr<FilterCollection> collection(new FilterCollection()); 228 collection->SetDemuxer(demuxer_.get()); 229 230 ScopedVector<VideoDecoder> video_decoders; 231 video_decoders.push_back( 232 new VpxVideoDecoder(message_loop_.message_loop_proxy())); 233 video_decoders.push_back( 234 new FFmpegVideoDecoder(message_loop_.message_loop_proxy())); 235 236 // Disable frame dropping if hashing is enabled. 237 scoped_ptr<VideoRenderer> renderer(new VideoRendererBase( 238 message_loop_.message_loop_proxy(), 239 video_decoders.Pass(), 240 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 241 base::Unretained(this), decryptor), 242 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, 243 base::Unretained(this)), 244 base::Bind(&PipelineIntegrationTestBase::OnSetOpaque, 245 base::Unretained(this)), 246 !hashing_enabled_)); 247 collection->SetVideoRenderer(renderer.Pass()); 248 249 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy()); 250 251 ScopedVector<AudioDecoder> audio_decoders; 252 audio_decoders.push_back( 253 new FFmpegAudioDecoder(message_loop_.message_loop_proxy())); 254 audio_decoders.push_back( 255 new OpusAudioDecoder(message_loop_.message_loop_proxy())); 256 257 AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl( 258 message_loop_.message_loop_proxy(), 259 audio_sink_.get(), 260 audio_decoders.Pass(), 261 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 262 base::Unretained(this), 263 decryptor), 264 true); 265 // Disable underflow if hashing is enabled. 266 if (hashing_enabled_) { 267 audio_sink_->StartAudioHashForTesting(); 268 audio_renderer_impl->DisableUnderflowForTesting(); 269 } 270 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl); 271 collection->SetAudioRenderer(audio_renderer.Pass()); 272 273 return collection.Pass(); 274} 275 276void PipelineIntegrationTestBase::SetDecryptor( 277 Decryptor* decryptor, 278 const DecryptorReadyCB& decryptor_ready_cb) { 279 decryptor_ready_cb.Run(decryptor); 280} 281 282void PipelineIntegrationTestBase::OnVideoRendererPaint( 283 const scoped_refptr<VideoFrame>& frame) { 284 last_video_frame_format_ = frame->format(); 285 if (!hashing_enabled_) 286 return; 287 frame->HashFrameForTesting(&md5_context_); 288} 289 290std::string PipelineIntegrationTestBase::GetVideoHash() { 291 DCHECK(hashing_enabled_); 292 base::MD5Digest digest; 293 base::MD5Final(&digest, &md5_context_); 294 return base::MD5DigestToBase16(digest); 295} 296 297std::string PipelineIntegrationTestBase::GetAudioHash() { 298 DCHECK(hashing_enabled_); 299 return audio_sink_->GetAudioHashForTesting(); 300} 301 302} // namespace media 303