// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/filters/pipeline_integration_test_base.h" #include "base/bind.h" #include "media/base/media_log.h" #include "media/filters/audio_renderer_impl.h" #include "media/filters/chunk_demuxer.h" #include "media/filters/ffmpeg_audio_decoder.h" #include "media/filters/ffmpeg_demuxer.h" #include "media/filters/ffmpeg_video_decoder.h" #include "media/filters/file_data_source.h" #include "media/filters/opus_audio_decoder.h" #include "media/filters/vpx_video_decoder.h" using ::testing::AnyNumber; using ::testing::AtMost; namespace media { const char kNullHash[] = "d41d8cd98f00b204e9800998ecf8427e"; PipelineIntegrationTestBase::PipelineIntegrationTestBase() : hashing_enabled_(false), pipeline_(new Pipeline(message_loop_.message_loop_proxy(), new MediaLog())), ended_(false), pipeline_status_(PIPELINE_OK) { base::MD5Init(&md5_context_); EXPECT_CALL(*this, OnSetOpaque(true)).Times(AnyNumber()); } PipelineIntegrationTestBase::~PipelineIntegrationTestBase() { if (!pipeline_->IsRunning()) return; Stop(); } void PipelineIntegrationTestBase::OnStatusCallback( PipelineStatus status) { pipeline_status_ = status; message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); } void PipelineIntegrationTestBase::OnStatusCallbackChecked( PipelineStatus expected_status, PipelineStatus status) { EXPECT_EQ(expected_status, status); OnStatusCallback(status); } PipelineStatusCB PipelineIntegrationTestBase::QuitOnStatusCB( PipelineStatus expected_status) { return base::Bind(&PipelineIntegrationTestBase::OnStatusCallbackChecked, base::Unretained(this), expected_status); } void PipelineIntegrationTestBase::OnEnded() { DCHECK(!ended_); ended_ = true; pipeline_status_ = PIPELINE_OK; message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); } bool PipelineIntegrationTestBase::WaitUntilOnEnded() { if (ended_) return (pipeline_status_ == PIPELINE_OK); message_loop_.Run(); EXPECT_TRUE(ended_); return ended_ && (pipeline_status_ == PIPELINE_OK); } PipelineStatus PipelineIntegrationTestBase::WaitUntilEndedOrError() { if (ended_ || pipeline_status_ != PIPELINE_OK) return pipeline_status_; message_loop_.Run(); return pipeline_status_; } void PipelineIntegrationTestBase::OnError(PipelineStatus status) { DCHECK_NE(status, PIPELINE_OK); pipeline_status_ = status; message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure()); } bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, PipelineStatus expected_status) { EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) .Times(AtMost(1)); EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) .Times(AtMost(1)); pipeline_->Start( CreateFilterCollection(file_path), base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), QuitOnStatusCB(expected_status), base::Bind(&PipelineIntegrationTestBase::OnBufferingState, base::Unretained(this)), base::Closure()); message_loop_.Run(); return (pipeline_status_ == PIPELINE_OK); } bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path, PipelineStatus expected_status, bool hashing_enabled) { hashing_enabled_ = hashing_enabled; return Start(file_path, expected_status); } bool PipelineIntegrationTestBase::Start(const base::FilePath& file_path) { EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata)) .Times(AtMost(1)); EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)) .Times(AtMost(1)); pipeline_->Start( CreateFilterCollection(file_path), base::Bind(&PipelineIntegrationTestBase::OnEnded, base::Unretained(this)), base::Bind(&PipelineIntegrationTestBase::OnError, base::Unretained(this)), base::Bind(&PipelineIntegrationTestBase::OnStatusCallback, base::Unretained(this)), base::Bind(&PipelineIntegrationTestBase::OnBufferingState, base::Unretained(this)), base::Closure()); message_loop_.Run(); return (pipeline_status_ == PIPELINE_OK); } void PipelineIntegrationTestBase::Play() { pipeline_->SetPlaybackRate(1); } void PipelineIntegrationTestBase::Pause() { pipeline_->SetPlaybackRate(0); } bool PipelineIntegrationTestBase::Seek(base::TimeDelta seek_time) { ended_ = false; EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted)); pipeline_->Seek(seek_time, QuitOnStatusCB(PIPELINE_OK)); message_loop_.Run(); return (pipeline_status_ == PIPELINE_OK); } void PipelineIntegrationTestBase::Stop() { DCHECK(pipeline_->IsRunning()); pipeline_->Stop(MessageLoop::QuitClosure()); message_loop_.Run(); } void PipelineIntegrationTestBase::QuitAfterCurrentTimeTask( const base::TimeDelta& quit_time) { if (pipeline_->GetMediaTime() >= quit_time || pipeline_status_ != PIPELINE_OK) { message_loop_.Quit(); return; } message_loop_.PostDelayedTask( FROM_HERE, base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, base::Unretained(this), quit_time), base::TimeDelta::FromMilliseconds(10)); } bool PipelineIntegrationTestBase::WaitUntilCurrentTimeIsAfter( const base::TimeDelta& wait_time) { DCHECK(pipeline_->IsRunning()); DCHECK_GT(pipeline_->GetPlaybackRate(), 0); DCHECK(wait_time <= pipeline_->GetMediaDuration()); message_loop_.PostDelayedTask( FROM_HERE, base::Bind(&PipelineIntegrationTestBase::QuitAfterCurrentTimeTask, base::Unretained(this), wait_time), base::TimeDelta::FromMilliseconds(10)); message_loop_.Run(); return (pipeline_status_ == PIPELINE_OK); } scoped_ptr PipelineIntegrationTestBase::CreateFilterCollection( const base::FilePath& file_path) { scoped_refptr data_source = new FileDataSource(); CHECK(data_source->Initialize(file_path)); return CreateFilterCollection( new FFmpegDemuxer(message_loop_.message_loop_proxy(), data_source), NULL); } scoped_ptr PipelineIntegrationTestBase::CreateFilterCollection( const scoped_refptr& demuxer, Decryptor* decryptor) { scoped_ptr collection(new FilterCollection()); collection->SetDemuxer(demuxer); scoped_refptr audio_decoder = new FFmpegAudioDecoder( message_loop_.message_loop_proxy()); scoped_refptr opus_decoder = new OpusAudioDecoder( message_loop_.message_loop_proxy()); scoped_refptr video_decoder = new FFmpegVideoDecoder( message_loop_.message_loop_proxy()); scoped_refptr vpx_decoder = new VpxVideoDecoder( message_loop_.message_loop_proxy()); collection->GetAudioDecoders()->push_back(audio_decoder); collection->GetAudioDecoders()->push_back(opus_decoder); collection->GetVideoDecoders()->push_back(video_decoder); collection->GetVideoDecoders()->push_back(vpx_decoder); // Disable frame dropping if hashing is enabled. scoped_ptr renderer(new VideoRendererBase( message_loop_.message_loop_proxy(), base::Bind(&PipelineIntegrationTestBase::SetDecryptor, base::Unretained(this), decryptor), base::Bind(&PipelineIntegrationTestBase::OnVideoRendererPaint, base::Unretained(this)), base::Bind(&PipelineIntegrationTestBase::OnSetOpaque, base::Unretained(this)), !hashing_enabled_)); collection->SetVideoRenderer(renderer.Pass()); audio_sink_ = new NullAudioSink(); AudioRendererImpl* audio_renderer_impl = new AudioRendererImpl( message_loop_.message_loop_proxy(), audio_sink_, base::Bind(&PipelineIntegrationTestBase::SetDecryptor, base::Unretained(this), decryptor)); // Disable underflow if hashing is enabled. if (hashing_enabled_) { audio_sink_->StartAudioHashForTesting(); audio_renderer_impl->DisableUnderflowForTesting(); } scoped_ptr audio_renderer(audio_renderer_impl); collection->SetAudioRenderer(audio_renderer.Pass()); return collection.Pass(); } void PipelineIntegrationTestBase::SetDecryptor( Decryptor* decryptor, const DecryptorReadyCB& decryptor_ready_cb) { decryptor_ready_cb.Run(decryptor); } void PipelineIntegrationTestBase::OnVideoRendererPaint( const scoped_refptr& frame) { if (!hashing_enabled_) return; frame->HashFrameForTesting(&md5_context_); } std::string PipelineIntegrationTestBase::GetVideoHash() { DCHECK(hashing_enabled_); base::MD5Digest digest; base::MD5Final(&digest, &md5_context_); return base::MD5DigestToBase16(digest); } std::string PipelineIntegrationTestBase::GetAudioHash() { DCHECK(hashing_enabled_); return audio_sink_->GetAudioHashForTesting(); } } // namespace media