// 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/base/test_helpers.h" #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/pickle.h" #include "base/run_loop.h" #include "base/test/test_timeouts.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "media/base/audio_buffer.h" #include "media/base/bind_to_current_loop.h" #include "media/base/decoder_buffer.h" #include "ui/gfx/geometry/rect.h" using ::testing::_; using ::testing::StrictMock; namespace media { // Utility mock for testing methods expecting Closures and PipelineStatusCBs. class MockCallback : public base::RefCountedThreadSafe { public: MockCallback(); MOCK_METHOD0(Run, void()); MOCK_METHOD1(RunWithBool, void(bool)); MOCK_METHOD1(RunWithStatus, void(PipelineStatus)); protected: friend class base::RefCountedThreadSafe; virtual ~MockCallback(); private: DISALLOW_COPY_AND_ASSIGN(MockCallback); }; MockCallback::MockCallback() {} MockCallback::~MockCallback() {} base::Closure NewExpectedClosure() { StrictMock* callback = new StrictMock(); EXPECT_CALL(*callback, Run()); return base::Bind(&MockCallback::Run, callback); } base::Callback NewExpectedBoolCB(bool success) { StrictMock* callback = new StrictMock(); EXPECT_CALL(*callback, RunWithBool(success)); return base::Bind(&MockCallback::RunWithBool, callback); } PipelineStatusCB NewExpectedStatusCB(PipelineStatus status) { StrictMock* callback = new StrictMock(); EXPECT_CALL(*callback, RunWithStatus(status)); return base::Bind(&MockCallback::RunWithStatus, callback); } WaitableMessageLoopEvent::WaitableMessageLoopEvent() : message_loop_(base::MessageLoop::current()), signaled_(false), status_(PIPELINE_OK) { DCHECK(message_loop_); } WaitableMessageLoopEvent::~WaitableMessageLoopEvent() {} base::Closure WaitableMessageLoopEvent::GetClosure() { DCHECK_EQ(message_loop_, base::MessageLoop::current()); return BindToCurrentLoop(base::Bind( &WaitableMessageLoopEvent::OnCallback, base::Unretained(this), PIPELINE_OK)); } PipelineStatusCB WaitableMessageLoopEvent::GetPipelineStatusCB() { DCHECK_EQ(message_loop_, base::MessageLoop::current()); return BindToCurrentLoop(base::Bind( &WaitableMessageLoopEvent::OnCallback, base::Unretained(this))); } void WaitableMessageLoopEvent::RunAndWait() { RunAndWaitForStatus(PIPELINE_OK); } void WaitableMessageLoopEvent::RunAndWaitForStatus(PipelineStatus expected) { DCHECK_EQ(message_loop_, base::MessageLoop::current()); if (signaled_) { EXPECT_EQ(expected, status_); return; } run_loop_.reset(new base::RunLoop()); base::Timer timer(false, false); timer.Start(FROM_HERE, TestTimeouts::action_timeout(), base::Bind( &WaitableMessageLoopEvent::OnTimeout, base::Unretained(this))); run_loop_->Run(); EXPECT_TRUE(signaled_); EXPECT_EQ(expected, status_); run_loop_.reset(); } void WaitableMessageLoopEvent::OnCallback(PipelineStatus status) { DCHECK_EQ(message_loop_, base::MessageLoop::current()); signaled_ = true; status_ = status; // |run_loop_| may be null if the callback fires before RunAndWaitForStatus(). if (run_loop_) run_loop_->Quit(); } void WaitableMessageLoopEvent::OnTimeout() { DCHECK_EQ(message_loop_, base::MessageLoop::current()); ADD_FAILURE() << "Timed out waiting for message loop to quit"; run_loop_->Quit(); } static VideoDecoderConfig GetTestConfig(VideoCodec codec, gfx::Size coded_size, bool is_encrypted) { gfx::Rect visible_rect(coded_size.width(), coded_size.height()); gfx::Size natural_size = coded_size; return VideoDecoderConfig(codec, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12, COLOR_SPACE_UNSPECIFIED, coded_size, visible_rect, natural_size, NULL, 0, is_encrypted); } static const gfx::Size kNormalSize(320, 240); static const gfx::Size kLargeSize(640, 480); VideoDecoderConfig TestVideoConfig::Invalid() { return GetTestConfig(kUnknownVideoCodec, kNormalSize, false); } VideoDecoderConfig TestVideoConfig::Normal() { return GetTestConfig(kCodecVP8, kNormalSize, false); } VideoDecoderConfig TestVideoConfig::NormalEncrypted() { return GetTestConfig(kCodecVP8, kNormalSize, true); } VideoDecoderConfig TestVideoConfig::Large() { return GetTestConfig(kCodecVP8, kLargeSize, false); } VideoDecoderConfig TestVideoConfig::LargeEncrypted() { return GetTestConfig(kCodecVP8, kLargeSize, true); } gfx::Size TestVideoConfig::NormalCodedSize() { return kNormalSize; } gfx::Size TestVideoConfig::LargeCodedSize() { return kLargeSize; } template scoped_refptr MakeAudioBuffer(SampleFormat format, ChannelLayout channel_layout, size_t channel_count, int sample_rate, T start, T increment, size_t frames, base::TimeDelta timestamp) { const size_t channels = ChannelLayoutToChannelCount(channel_layout); scoped_refptr output = AudioBuffer::CreateBuffer(format, channel_layout, static_cast(channel_count), sample_rate, static_cast(frames)); output->set_timestamp(timestamp); const bool is_planar = format == kSampleFormatPlanarS16 || format == kSampleFormatPlanarF32; // Values in channel 0 will be: // start // start + increment // start + 2 * increment, ... // While, values in channel 1 will be: // start + frames * increment // start + (frames + 1) * increment // start + (frames + 2) * increment, ... for (size_t ch = 0; ch < channels; ++ch) { T* buffer = reinterpret_cast(output->channel_data()[is_planar ? ch : 0]); const T v = static_cast(start + ch * frames * increment); for (size_t i = 0; i < frames; ++i) { buffer[is_planar ? i : ch + i * channels] = static_cast(v + i * increment); } } return output; } // Instantiate all the types of MakeAudioBuffer() and // MakeAudioBuffer() needed. #define DEFINE_MAKE_AUDIO_BUFFER_INSTANCE(type) \ template scoped_refptr MakeAudioBuffer( \ SampleFormat format, \ ChannelLayout channel_layout, \ size_t channel_count, \ int sample_rate, \ type start, \ type increment, \ size_t frames, \ base::TimeDelta start_time) DEFINE_MAKE_AUDIO_BUFFER_INSTANCE(uint8); DEFINE_MAKE_AUDIO_BUFFER_INSTANCE(int16); DEFINE_MAKE_AUDIO_BUFFER_INSTANCE(int32); DEFINE_MAKE_AUDIO_BUFFER_INSTANCE(float); static const char kFakeVideoBufferHeader[] = "FakeVideoBufferForTest"; scoped_refptr CreateFakeVideoBufferForTest( const VideoDecoderConfig& config, base::TimeDelta timestamp, base::TimeDelta duration) { base::Pickle pickle; pickle.WriteString(kFakeVideoBufferHeader); pickle.WriteInt(config.coded_size().width()); pickle.WriteInt(config.coded_size().height()); pickle.WriteInt64(timestamp.InMilliseconds()); scoped_refptr buffer = DecoderBuffer::CopyFrom( static_cast(pickle.data()), static_cast(pickle.size())); buffer->set_timestamp(timestamp); buffer->set_duration(duration); buffer->set_is_key_frame(true); return buffer; } bool VerifyFakeVideoBufferForTest( const scoped_refptr& buffer, const VideoDecoderConfig& config) { // Check if the input |buffer| matches the |config|. base::PickleIterator pickle(base::Pickle( reinterpret_cast(buffer->data()), buffer->data_size())); std::string header; int width = 0; int height = 0; bool success = pickle.ReadString(&header) && pickle.ReadInt(&width) && pickle.ReadInt(&height); return (success && header == kFakeVideoBufferHeader && width == config.coded_size().width() && height == config.coded_size().height()); } CallbackPairChecker::CallbackPairChecker() : expecting_b_(false) { } CallbackPairChecker::~CallbackPairChecker() { EXPECT_FALSE(expecting_b_); } void CallbackPairChecker::RecordACalled() { EXPECT_FALSE(expecting_b_); expecting_b_ = true; } void CallbackPairChecker::RecordBCalled() { EXPECT_TRUE(expecting_b_); expecting_b_ = false; } } // namespace media