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