pipeline_integration_test_base.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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( 73 std::string(), std::string(), type, init_data.Pass(), init_data_size); 74} 75 76void PipelineIntegrationTestBase::OnEnded() { 77 DCHECK(!ended_); 78 ended_ = true; 79 pipeline_status_ = PIPELINE_OK; 80 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 81} 82 83bool PipelineIntegrationTestBase::WaitUntilOnEnded() { 84 if (ended_) 85 return (pipeline_status_ == PIPELINE_OK); 86 message_loop_.Run(); 87 EXPECT_TRUE(ended_); 88 return ended_ && (pipeline_status_ == PIPELINE_OK); 89} 90 91PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() { 92 if (ended_ || pipeline_status_ != PIPELINE_OK) 93 return pipeline_status_; 94 message_loop_.Run(); 95 return pipeline_status_; 96} 97 98void PipelineIntegrationTestBase::OnError(PipelineStatus status) { 99 DCHECK_NE(status, PIPELINE_OK); 100 pipeline_status_ = status; 101 message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 102} 103 104bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 105 PipelineStatus expected_status) { 106 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) 107 .Times(AtMost(1)); 108 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) 109 .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::OnBufferingState, 116 base::Unretained(this)), 117 base::Closure()); 118 message_loop_.Run(); 119 return (pipeline_status_ == PIPELINE_OK); 120} 121 122bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 123 PipelineStatus expected_status, 124 bool hashing_enabled) { 125 hashing_enabled_ = hashing_enabled; 126 return Start(file_path, expected_status); 127} 128 129bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) { 130 return Start(file_path, NULL); 131} 132 133bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, 134 Decryptor* decryptor) { 135 EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) 136 .Times(AtMost(1)); 137 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) 138 .Times(AtMost(1)); 139 pipeline_->Start( 140 CreateFilterCollection(file_path, decryptor), 141 base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), 142 base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), 143 base::Bind(&PipelineIntegrationTestBase::OnStatusCallback, 144 base::Unretained(this)), 145 base::Bind(&PipelineIntegrationTestBase::OnBufferingState, 146 base::Unretained(this)), 147 base::Closure()); 148 message_loop_.Run(); 149 return (pipeline_status_ == PIPELINE_OK); 150} 151 152void PipelineIntegrationTestBase::Play() { 153 pipeline_->SetPlaybackRate(1); 154} 155 156void PipelineIntegrationTestBase::Pause() { 157 pipeline_->SetPlaybackRate(0); 158} 159 160bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) { 161 ended_ = false; 162 163 EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)); 164 pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK)); 165 message_loop_.Run(); 166 return (pipeline_status_ == PIPELINE_OK); 167} 168 169void PipelineIntegrationTestBase::Stop() { 170 DCHECK(pipeline_->IsRunning()); 171 pipeline_->Stop(base::MessageLoop::QuitClosure()); 172 message_loop_.Run(); 173} 174 175void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask( 176 const base::TimeDelta& quit_time) { 177 if (pipeline_->GetMediaTime() >= quit_time || 178 pipeline_status_ != PIPELINE_OK) { 179 message_loop_.Quit(); 180 return; 181 } 182 183 message_loop_.PostDelayedTask( 184 FROM_HERE, 185 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, 186 base::Unretained(this), quit_time), 187 base::TimeDelta::FromMilliseconds(10)); 188} 189 190bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter( 191 const base::TimeDelta& wait_time) { 192 DCHECK(pipeline_->IsRunning()); 193 DCHECK_GT(pipeline_->GetPlaybackRate(), 0); 194 DCHECK(wait_time <= pipeline_->GetMediaDuration()); 195 196 message_loop_.PostDelayedTask( 197 FROM_HERE, 198 base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, 199 base::Unretained(this), 200 wait_time), 201 base::TimeDelta::FromMilliseconds(10)); 202 message_loop_.Run(); 203 return (pipeline_status_ == PIPELINE_OK); 204} 205 206scoped_ptr<FilterCollection> 207PipelineIntegrationTestBase::CreateFilterCollection( 208 const base::FilePath& file_path, 209 Decryptor* decryptor) { 210 FileDataSource* file_data_source = new FileDataSource(); 211 CHECK(file_data_source->Initialize(file_path)); 212 data_source_.reset(file_data_source); 213 214 media::FFmpegNeedKeyCB need_key_cb = 215 base::Bind(&PipelineIntegrationTestBase::DemuxerNeedKeyCB, 216 base::Unretained(this)); 217 scoped_ptr<Demuxer> demuxer(new FFmpegDemuxer( 218 message_loop_.message_loop_proxy(), data_source_.get(), need_key_cb)); 219 return CreateFilterCollection(demuxer.Pass(), decryptor); 220} 221 222scoped_ptr<FilterCollection> 223PipelineIntegrationTestBase::CreateFilterCollection( 224 scoped_ptr<Demuxer> demuxer, 225 Decryptor* decryptor) { 226 demuxer_ = demuxer.Pass(); 227 228 scoped_ptr<FilterCollection> collection(new FilterCollection()); 229 collection->SetDemuxer(demuxer_.get()); 230 231 ScopedVector<VideoDecoder> video_decoders; 232 video_decoders.push_back( 233 new VpxVideoDecoder(message_loop_.message_loop_proxy())); 234 video_decoders.push_back( 235 new FFmpegVideoDecoder(message_loop_.message_loop_proxy())); 236 237 // Disable frame dropping if hashing is enabled. 238 scoped_ptr<VideoRenderer> renderer(new VideoRendererBase( 239 message_loop_.message_loop_proxy(), 240 video_decoders.Pass(), 241 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 242 base::Unretained(this), decryptor), 243 base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, 244 base::Unretained(this)), 245 base::Bind(&PipelineIntegrationTestBase::OnSetOpaque, 246 base::Unretained(this)), 247 !hashing_enabled_)); 248 collection->SetVideoRenderer(renderer.Pass()); 249 250 audio_sink_ = new NullAudioSink(message_loop_.message_loop_proxy()); 251 252 ScopedVector<AudioDecoder> audio_decoders; 253 audio_decoders.push_back( 254 new FFmpegAudioDecoder(message_loop_.message_loop_proxy())); 255 audio_decoders.push_back( 256 new OpusAudioDecoder(message_loop_.message_loop_proxy())); 257 258 AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl( 259 message_loop_.message_loop_proxy(), 260 audio_sink_, 261 audio_decoders.Pass(), 262 base::Bind(&PipelineIntegrationTestBase::SetDecryptor, 263 base::Unretained(this), decryptor)); 264 // Disable underflow if hashing is enabled. 265 if (hashing_enabled_) { 266 audio_sink_->StartAudioHashForTesting(); 267 audio_renderer_impl->DisableUnderflowForTesting(); 268 } 269 scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_impl); 270 collection->SetAudioRenderer(audio_renderer.Pass()); 271 272 return collection.Pass(); 273} 274 275void PipelineIntegrationTestBase::SetDecryptor( 276 Decryptor* decryptor, 277 const DecryptorReadyCB& decryptor_ready_cb) { 278 decryptor_ready_cb.Run(decryptor); 279} 280 281void PipelineIntegrationTestBase::OnVideoRendererPaint( 282 const scoped_refptr<VideoFrame>& frame) { 283 last_video_frame_format_ = frame->format(); 284 if (!hashing_enabled_) 285 return; 286 frame->HashFrameForTesting(&md5_context_); 287} 288 289std::string PipelineIntegrationTestBase::GetVideoHash() { 290 DCHECK(hashing_enabled_); 291 base::MD5Digest digest; 292 base::MD5Final(&digest, &md5_context_); 293 return base::MD5DigestToBase16(digest); 294} 295 296std::string PipelineIntegrationTestBase::GetAudioHash() { 297 DCHECK(hashing_enabled_); 298 return audio_sink_->GetAudioHashForTesting(); 299} 300 301} // namespace media 302