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
19using ::testing::_;
20using ::testing::StrictMock;
21
22namespace media {
23
24// Utility mock for testing methods expecting Closures and PipelineStatusCBs.
25class 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
39MockCallback::MockCallback() {}
40MockCallback::~MockCallback() {}
41
42base::Closure NewExpectedClosure() {
43  StrictMock<MockCallback>* callback = new StrictMock<MockCallback>();
44  EXPECT_CALL(*callback, Run());
45  return base::Bind(&MockCallback::Run, callback);
46}
47
48PipelineStatusCB 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
54WaitableMessageLoopEvent::WaitableMessageLoopEvent()
55    : message_loop_(base::MessageLoop::current()),
56      signaled_(false),
57      status_(PIPELINE_OK) {
58  DCHECK(message_loop_);
59}
60
61WaitableMessageLoopEvent::~WaitableMessageLoopEvent() {}
62
63base::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
70PipelineStatusCB 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
76void WaitableMessageLoopEvent::RunAndWait() {
77  RunAndWaitForStatus(PIPELINE_OK);
78}
79
80void 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
96void WaitableMessageLoopEvent::OnCallback(PipelineStatus status) {
97  DCHECK_EQ(message_loop_, base::MessageLoop::current());
98  signaled_ = true;
99  status_ = status;
100  message_loop_->QuitWhenIdle();
101}
102
103void 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
109static 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
120static const gfx::Size kNormalSize(320, 240);
121static const gfx::Size kLargeSize(640, 480);
122
123VideoDecoderConfig TestVideoConfig::Invalid() {
124  return GetTestConfig(kUnknownVideoCodec, kNormalSize, false);
125}
126
127VideoDecoderConfig TestVideoConfig::Normal() {
128  return GetTestConfig(kCodecVP8, kNormalSize, false);
129}
130
131VideoDecoderConfig TestVideoConfig::NormalEncrypted() {
132  return GetTestConfig(kCodecVP8, kNormalSize, true);
133}
134
135VideoDecoderConfig TestVideoConfig::Large() {
136  return GetTestConfig(kCodecVP8, kLargeSize, false);
137}
138
139VideoDecoderConfig TestVideoConfig::LargeEncrypted() {
140  return GetTestConfig(kCodecVP8, kLargeSize, true);
141}
142
143gfx::Size TestVideoConfig::NormalCodedSize() {
144  return kNormalSize;
145}
146
147gfx::Size TestVideoConfig::LargeCodedSize() {
148  return kLargeSize;
149}
150
151template <class T>
152scoped_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
183template <class T>
184scoped_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)
230DEFINE_INTERLEAVED_INSTANCE(uint8);
231DEFINE_INTERLEAVED_INSTANCE(int16);
232DEFINE_INTERLEAVED_INSTANCE(int32);
233DEFINE_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);
244DEFINE_PLANAR_INSTANCE(int16);
245DEFINE_PLANAR_INSTANCE(float);
246
247static const char kFakeVideoBufferHeader[] = "FakeVideoBufferForTest";
248
249scoped_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
267bool 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