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/base/test_helpers.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/pickle.h"
11 #include "base/test/test_timeouts.h"
12 #include "base/time/time.h"
13 #include "base/timer/timer.h"
14 #include "media/base/audio_buffer.h"
15 #include "media/base/bind_to_loop.h"
16 #include "media/base/decoder_buffer.h"
17 #include "ui/gfx/rect.h"
18
19 using ::testing::_;
20 using ::testing::StrictMock;
21
22 namespace media {
23
24 // Utility mock for testing methods expecting Closures and PipelineStatusCBs.
25 class MockCallback : public base::RefCountedThreadSafe<MockCallback> {
26 public:
27 MockCallback();
28 MOCK_METHOD0(Run, void());
29 MOCK_METHOD1(RunWithStatus, void(PipelineStatus));
30
31 protected:
32 friend class base::RefCountedThreadSafe<MockCallback>;
33 virtual ~MockCallback();
34
35 private:
36 DISALLOW_COPY_AND_ASSIGN(MockCallback);
37 };
38
MockCallback()39 MockCallback::MockCallback() {}
~MockCallback()40 MockCallback::~MockCallback() {}
41
NewExpectedClosure()42 base::Closure NewExpectedClosure() {
43 StrictMock<MockCallback>* callback = new StrictMock<MockCallback>();
44 EXPECT_CALL(*callback, Run());
45 return base::Bind(&MockCallback::Run, callback);
46 }
47
NewExpectedStatusCB(PipelineStatus status)48 PipelineStatusCB NewExpectedStatusCB(PipelineStatus status) {
49 StrictMock<MockCallback>* callback = new StrictMock<MockCallback>();
50 EXPECT_CALL(*callback, RunWithStatus(status));
51 return base::Bind(&MockCallback::RunWithStatus, callback);
52 }
53
WaitableMessageLoopEvent()54 WaitableMessageLoopEvent::WaitableMessageLoopEvent()
55 : message_loop_(base::MessageLoop::current()),
56 signaled_(false),
57 status_(PIPELINE_OK) {
58 DCHECK(message_loop_);
59 }
60
~WaitableMessageLoopEvent()61 WaitableMessageLoopEvent::~WaitableMessageLoopEvent() {}
62
GetClosure()63 base::Closure WaitableMessageLoopEvent::GetClosure() {
64 DCHECK_EQ(message_loop_, base::MessageLoop::current());
65 return BindToLoop(message_loop_->message_loop_proxy(), base::Bind(
66 &WaitableMessageLoopEvent::OnCallback, base::Unretained(this),
67 PIPELINE_OK));
68 }
69
GetPipelineStatusCB()70 PipelineStatusCB WaitableMessageLoopEvent::GetPipelineStatusCB() {
71 DCHECK_EQ(message_loop_, base::MessageLoop::current());
72 return BindToLoop(message_loop_->message_loop_proxy(), base::Bind(
73 &WaitableMessageLoopEvent::OnCallback, base::Unretained(this)));
74 }
75
RunAndWait()76 void WaitableMessageLoopEvent::RunAndWait() {
77 RunAndWaitForStatus(PIPELINE_OK);
78 }
79
RunAndWaitForStatus(PipelineStatus expected)80 void WaitableMessageLoopEvent::RunAndWaitForStatus(PipelineStatus expected) {
81 DCHECK_EQ(message_loop_, base::MessageLoop::current());
82 if (signaled_) {
83 EXPECT_EQ(expected, status_);
84 return;
85 }
86
87 base::Timer timer(false, false);
88 timer.Start(FROM_HERE, TestTimeouts::action_timeout(), base::Bind(
89 &WaitableMessageLoopEvent::OnTimeout, base::Unretained(this)));
90
91 message_loop_->Run();
92 EXPECT_TRUE(signaled_);
93 EXPECT_EQ(expected, status_);
94 }
95
OnCallback(PipelineStatus status)96 void WaitableMessageLoopEvent::OnCallback(PipelineStatus status) {
97 DCHECK_EQ(message_loop_, base::MessageLoop::current());
98 signaled_ = true;
99 status_ = status;
100 message_loop_->QuitWhenIdle();
101 }
102
OnTimeout()103 void WaitableMessageLoopEvent::OnTimeout() {
104 DCHECK_EQ(message_loop_, base::MessageLoop::current());
105 ADD_FAILURE() << "Timed out waiting for message loop to quit";
106 message_loop_->QuitWhenIdle();
107 }
108
GetTestConfig(VideoCodec codec,gfx::Size coded_size,bool is_encrypted)109 static VideoDecoderConfig GetTestConfig(VideoCodec codec,
110 gfx::Size coded_size,
111 bool is_encrypted) {
112 gfx::Rect visible_rect(coded_size.width(), coded_size.height());
113 gfx::Size natural_size = coded_size;
114
115 return VideoDecoderConfig(codec, VIDEO_CODEC_PROFILE_UNKNOWN,
116 VideoFrame::YV12, coded_size, visible_rect, natural_size,
117 NULL, 0, is_encrypted);
118 }
119
120 static const gfx::Size kNormalSize(320, 240);
121 static const gfx::Size kLargeSize(640, 480);
122
Invalid()123 VideoDecoderConfig TestVideoConfig::Invalid() {
124 return GetTestConfig(kUnknownVideoCodec, kNormalSize, false);
125 }
126
Normal()127 VideoDecoderConfig TestVideoConfig::Normal() {
128 return GetTestConfig(kCodecVP8, kNormalSize, false);
129 }
130
NormalEncrypted()131 VideoDecoderConfig TestVideoConfig::NormalEncrypted() {
132 return GetTestConfig(kCodecVP8, kNormalSize, true);
133 }
134
Large()135 VideoDecoderConfig TestVideoConfig::Large() {
136 return GetTestConfig(kCodecVP8, kLargeSize, false);
137 }
138
LargeEncrypted()139 VideoDecoderConfig TestVideoConfig::LargeEncrypted() {
140 return GetTestConfig(kCodecVP8, kLargeSize, true);
141 }
142
NormalCodedSize()143 gfx::Size TestVideoConfig::NormalCodedSize() {
144 return kNormalSize;
145 }
146
LargeCodedSize()147 gfx::Size TestVideoConfig::LargeCodedSize() {
148 return kLargeSize;
149 }
150
151 template <class T>
MakeInterleavedAudioBuffer(SampleFormat format,int channels,T start,T increment,int frames,base::TimeDelta start_time,base::TimeDelta duration)152 scoped_refptr<AudioBuffer> MakeInterleavedAudioBuffer(
153 SampleFormat format,
154 int channels,
155 T start,
156 T increment,
157 int frames,
158 base::TimeDelta start_time,
159 base::TimeDelta duration) {
160 DCHECK(format == kSampleFormatU8 || format == kSampleFormatS16 ||
161 format == kSampleFormatS32 || format == kSampleFormatF32);
162
163 // Create a block of memory with values:
164 // start
165 // start + increment
166 // start + 2 * increment, ...
167 // Since this is interleaved data, channel 0 data will be:
168 // start
169 // start + channels * increment
170 // start + 2 * channels * increment, ...
171 int buffer_size = frames * channels * sizeof(T);
172 scoped_ptr<uint8[]> memory(new uint8[buffer_size]);
173 uint8* data[] = { memory.get() };
174 T* buffer = reinterpret_cast<T*>(memory.get());
175 for (int i = 0; i < frames * channels; ++i) {
176 buffer[i] = start;
177 start += increment;
178 }
179 return AudioBuffer::CopyFrom(
180 format, channels, frames, data, start_time, duration);
181 }
182
183 template <class T>
MakePlanarAudioBuffer(SampleFormat format,int channels,T start,T increment,int frames,base::TimeDelta start_time,base::TimeDelta duration)184 scoped_refptr<AudioBuffer> MakePlanarAudioBuffer(
185 SampleFormat format,
186 int channels,
187 T start,
188 T increment,
189 int frames,
190 base::TimeDelta start_time,
191 base::TimeDelta duration) {
192 DCHECK(format == kSampleFormatPlanarF32 || format == kSampleFormatPlanarS16);
193
194 // Create multiple blocks of data, one for each channel.
195 // Values in channel 0 will be:
196 // start
197 // start + increment
198 // start + 2 * increment, ...
199 // Values in channel 1 will be:
200 // start + frames * increment
201 // start + (frames + 1) * increment
202 // start + (frames + 2) * increment, ...
203 int buffer_size = frames * sizeof(T);
204 scoped_ptr<uint8*[]> data(new uint8*[channels]);
205 scoped_ptr<uint8[]> memory(new uint8[channels * buffer_size]);
206 for (int i = 0; i < channels; ++i) {
207 data.get()[i] = memory.get() + i * buffer_size;
208 T* buffer = reinterpret_cast<T*>(data.get()[i]);
209 for (int j = 0; j < frames; ++j) {
210 buffer[j] = start;
211 start += increment;
212 }
213 }
214 return AudioBuffer::CopyFrom(
215 format, channels, frames, data.get(), start_time, duration);
216 }
217
218 // Instantiate all the types of MakeInterleavedAudioBuffer() and
219 // MakePlanarAudioBuffer() needed.
220
221 #define DEFINE_INTERLEAVED_INSTANCE(type) \
222 template scoped_refptr<AudioBuffer> MakeInterleavedAudioBuffer<type>( \
223 SampleFormat format, \
224 int channels, \
225 type start, \
226 type increment, \
227 int frames, \
228 base::TimeDelta start_time, \
229 base::TimeDelta duration)
230 DEFINE_INTERLEAVED_INSTANCE(uint8);
231 DEFINE_INTERLEAVED_INSTANCE(int16);
232 DEFINE_INTERLEAVED_INSTANCE(int32);
233 DEFINE_INTERLEAVED_INSTANCE(float);
234
235 #define DEFINE_PLANAR_INSTANCE(type) \
236 template scoped_refptr<AudioBuffer> MakePlanarAudioBuffer<type>( \
237 SampleFormat format, \
238 int channels, \
239 type start, \
240 type increment, \
241 int frames, \
242 base::TimeDelta start_time, \
243 base::TimeDelta duration);
244 DEFINE_PLANAR_INSTANCE(int16);
245 DEFINE_PLANAR_INSTANCE(float);
246
247 static const char kFakeVideoBufferHeader[] = "FakeVideoBufferForTest";
248
CreateFakeVideoBufferForTest(const VideoDecoderConfig & config,base::TimeDelta timestamp,base::TimeDelta duration)249 scoped_refptr<DecoderBuffer> CreateFakeVideoBufferForTest(
250 const VideoDecoderConfig& config,
251 base::TimeDelta timestamp, base::TimeDelta duration) {
252 Pickle pickle;
253 pickle.WriteString(kFakeVideoBufferHeader);
254 pickle.WriteInt(config.coded_size().width());
255 pickle.WriteInt(config.coded_size().height());
256 pickle.WriteInt64(timestamp.InMilliseconds());
257
258 scoped_refptr<DecoderBuffer> buffer = DecoderBuffer::CopyFrom(
259 static_cast<const uint8*>(pickle.data()),
260 static_cast<int>(pickle.size()));
261 buffer->set_timestamp(timestamp);
262 buffer->set_duration(duration);
263
264 return buffer;
265 }
266
VerifyFakeVideoBufferForTest(const scoped_refptr<DecoderBuffer> & buffer,const VideoDecoderConfig & config)267 bool VerifyFakeVideoBufferForTest(
268 const scoped_refptr<DecoderBuffer>& buffer,
269 const VideoDecoderConfig& config) {
270 // Check if the input |buffer| matches the |config|.
271 PickleIterator pickle(Pickle(reinterpret_cast<const char*>(buffer->data()),
272 buffer->data_size()));
273 std::string header;
274 int width = 0;
275 int height = 0;
276 bool success = pickle.ReadString(&header) && pickle.ReadInt(&width) &&
277 pickle.ReadInt(&height);
278 return (success && header == kFakeVideoBufferHeader &&
279 width == config.coded_size().width() &&
280 height == config.coded_size().height());
281 }
282
283 } // namespace media
284