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 "base/memory/scoped_ptr.h"
6#include "media/base/video_frame.h"
7#include "media/base/video_util.h"
8#include "testing/gtest/include/gtest/gtest.h"
9
10namespace media {
11
12class VideoUtilTest : public testing::Test {
13 public:
14  VideoUtilTest()
15      : height_(0),
16        y_stride_(0),
17        u_stride_(0),
18        v_stride_(0) {
19  }
20
21  virtual ~VideoUtilTest() {}
22
23  void CreateSourceFrame(int width, int height,
24                         int y_stride, int u_stride, int v_stride) {
25    EXPECT_GE(y_stride, width);
26    EXPECT_GE(u_stride, width / 2);
27    EXPECT_GE(v_stride, width / 2);
28
29    height_ = height;
30    y_stride_ = y_stride;
31    u_stride_ = u_stride;
32    v_stride_ = v_stride;
33
34    y_plane_.reset(new uint8[y_stride * height]);
35    u_plane_.reset(new uint8[u_stride * height / 2]);
36    v_plane_.reset(new uint8[v_stride * height / 2]);
37  }
38
39  void CreateDestinationFrame(int width, int height) {
40    gfx::Size size(width, height);
41    destination_frame_ =
42        VideoFrame::CreateFrame(VideoFrame::YV12, size, gfx::Rect(size), size,
43                                base::TimeDelta());
44  }
45
46  void CopyPlanes() {
47    CopyYPlane(y_plane_.get(), y_stride_, height_, destination_frame_.get());
48    CopyUPlane(
49        u_plane_.get(), u_stride_, height_ / 2, destination_frame_.get());
50    CopyVPlane(
51        v_plane_.get(), v_stride_, height_ / 2, destination_frame_.get());
52  }
53
54 private:
55  scoped_ptr<uint8[]> y_plane_;
56  scoped_ptr<uint8[]> u_plane_;
57  scoped_ptr<uint8[]> v_plane_;
58
59  int height_;
60  int y_stride_;
61  int u_stride_;
62  int v_stride_;
63
64  scoped_refptr<VideoFrame> destination_frame_;
65
66  DISALLOW_COPY_AND_ASSIGN(VideoUtilTest);
67};
68
69TEST_F(VideoUtilTest, CopyPlane_Exact) {
70  CreateSourceFrame(16, 16, 16, 8, 8);
71  CreateDestinationFrame(16, 16);
72  CopyPlanes();
73}
74
75TEST_F(VideoUtilTest, CopyPlane_SmallerSource) {
76  CreateSourceFrame(8, 8, 8, 4, 4);
77  CreateDestinationFrame(16, 16);
78  CopyPlanes();
79}
80
81TEST_F(VideoUtilTest, CopyPlane_SmallerDestination) {
82  CreateSourceFrame(16, 16, 16, 8, 8);
83  CreateDestinationFrame(8, 8);
84  CopyPlanes();
85}
86
87namespace {
88
89uint8 src6x4[] = {
90  0,  1,  2,  3,  4,  5,
91  6,  7,  8,  9, 10, 11,
92 12, 13, 14, 15, 16, 17,
93 18, 19, 20, 21, 22, 23
94};
95
96// Target images, name pattern target_rotation_flipV_flipH.
97uint8* target6x4_0_n_n = src6x4;
98
99uint8 target6x4_0_n_y[] = {
100  5,  4,  3,  2,  1,  0,
101 11, 10,  9,  8,  7,  6,
102 17, 16, 15, 14, 13, 12,
103 23, 22, 21, 20, 19, 18
104};
105
106uint8 target6x4_0_y_n[] = {
107 18, 19, 20, 21, 22, 23,
108 12, 13, 14, 15, 16, 17,
109  6,  7,  8,  9, 10, 11,
110  0,  1,  2,  3,  4,  5
111};
112
113uint8 target6x4_0_y_y[] = {
114 23, 22, 21, 20, 19, 18,
115 17, 16, 15, 14, 13, 12,
116 11, 10,  9,  8,  7,  6,
117  5,  4,  3,  2,  1,  0
118};
119
120uint8 target6x4_90_n_n[] = {
121 255, 19, 13,  7,  1, 255,
122 255, 20, 14,  8,  2, 255,
123 255, 21, 15,  9,  3, 255,
124 255, 22, 16, 10,  4, 255
125};
126
127uint8 target6x4_90_n_y[] = {
128 255,  1,  7, 13, 19, 255,
129 255,  2,  8, 14, 20, 255,
130 255,  3,  9, 15, 21, 255,
131 255,  4, 10, 16, 22, 255
132};
133
134uint8 target6x4_90_y_n[] = {
135 255, 22, 16, 10,  4, 255,
136 255, 21, 15,  9,  3, 255,
137 255, 20, 14,  8,  2, 255,
138 255, 19, 13,  7,  1, 255
139};
140
141uint8 target6x4_90_y_y[] = {
142 255,  4, 10, 16, 22, 255,
143 255,  3,  9, 15, 21, 255,
144 255,  2,  8, 14, 20, 255,
145 255,  1,  7, 13, 19, 255
146};
147
148uint8* target6x4_180_n_n = target6x4_0_y_y;
149uint8* target6x4_180_n_y = target6x4_0_y_n;
150uint8* target6x4_180_y_n = target6x4_0_n_y;
151uint8* target6x4_180_y_y = target6x4_0_n_n;
152
153uint8* target6x4_270_n_n = target6x4_90_y_y;
154uint8* target6x4_270_n_y = target6x4_90_y_n;
155uint8* target6x4_270_y_n = target6x4_90_n_y;
156uint8* target6x4_270_y_y = target6x4_90_n_n;
157
158uint8 src4x6[] = {
159  0,  1,  2,  3,
160  4,  5,  6,  7,
161  8,  9, 10, 11,
162 12, 13, 14, 15,
163 16, 17, 18, 19,
164 20, 21, 22, 23
165};
166
167uint8* target4x6_0_n_n = src4x6;
168
169uint8 target4x6_0_n_y[] = {
170  3,  2,  1,  0,
171  7,  6,  5,  4,
172 11, 10,  9,  8,
173 15, 14, 13, 12,
174 19, 18, 17, 16,
175 23, 22, 21, 20
176};
177
178uint8 target4x6_0_y_n[] = {
179 20, 21, 22, 23,
180 16, 17, 18, 19,
181 12, 13, 14, 15,
182  8,  9, 10, 11,
183  4,  5,  6,  7,
184  0,  1,  2,  3
185};
186
187uint8 target4x6_0_y_y[] = {
188 23, 22, 21, 20,
189 19, 18, 17, 16,
190 15, 14, 13, 12,
191 11, 10,  9,  8,
192  7,  6,  5,  4,
193  3,  2,  1,  0
194};
195
196uint8 target4x6_90_n_n[] = {
197 255, 255, 255, 255,
198  16,  12,   8,   4,
199  17,  13,   9,   5,
200  18,  14,  10,   6,
201  19,  15,  11,   7,
202 255, 255, 255, 255
203};
204
205uint8 target4x6_90_n_y[] = {
206 255, 255, 255, 255,
207   4,   8,  12,  16,
208   5,   9,  13,  17,
209   6,  10,  14,  18,
210   7,  11,  15,  19,
211 255, 255, 255, 255
212};
213
214uint8 target4x6_90_y_n[] = {
215 255, 255, 255, 255,
216  19,  15,  11,   7,
217  18,  14,  10,   6,
218  17,  13,   9,   5,
219  16,  12,   8,   4,
220 255, 255, 255, 255
221};
222
223uint8 target4x6_90_y_y[] = {
224 255, 255, 255, 255,
225   7,  11,  15,  19,
226   6,  10,  14,  18,
227   5,   9,  13,  17,
228   4,   8,  12,  16,
229 255, 255, 255, 255
230};
231
232uint8* target4x6_180_n_n = target4x6_0_y_y;
233uint8* target4x6_180_n_y = target4x6_0_y_n;
234uint8* target4x6_180_y_n = target4x6_0_n_y;
235uint8* target4x6_180_y_y = target4x6_0_n_n;
236
237uint8* target4x6_270_n_n = target4x6_90_y_y;
238uint8* target4x6_270_n_y = target4x6_90_y_n;
239uint8* target4x6_270_y_n = target4x6_90_n_y;
240uint8* target4x6_270_y_y = target4x6_90_n_n;
241
242struct VideoRotationTestData {
243  uint8* src;
244  uint8* target;
245  int width;
246  int height;
247  int rotation;
248  bool flip_vert;
249  bool flip_horiz;
250};
251
252const VideoRotationTestData kVideoRotationTestData[] = {
253  { src6x4, target6x4_0_n_n, 6, 4, 0, false, false },
254  { src6x4, target6x4_0_n_y, 6, 4, 0, false, true },
255  { src6x4, target6x4_0_y_n, 6, 4, 0, true, false },
256  { src6x4, target6x4_0_y_y, 6, 4, 0, true, true },
257
258  { src6x4, target6x4_90_n_n, 6, 4, 90, false, false },
259  { src6x4, target6x4_90_n_y, 6, 4, 90, false, true },
260  { src6x4, target6x4_90_y_n, 6, 4, 90, true, false },
261  { src6x4, target6x4_90_y_y, 6, 4, 90, true, true },
262
263  { src6x4, target6x4_180_n_n, 6, 4, 180, false, false },
264  { src6x4, target6x4_180_n_y, 6, 4, 180, false, true },
265  { src6x4, target6x4_180_y_n, 6, 4, 180, true, false },
266  { src6x4, target6x4_180_y_y, 6, 4, 180, true, true },
267
268  { src6x4, target6x4_270_n_n, 6, 4, 270, false, false },
269  { src6x4, target6x4_270_n_y, 6, 4, 270, false, true },
270  { src6x4, target6x4_270_y_n, 6, 4, 270, true, false },
271  { src6x4, target6x4_270_y_y, 6, 4, 270, true, true },
272
273  { src4x6, target4x6_0_n_n, 4, 6, 0, false, false },
274  { src4x6, target4x6_0_n_y, 4, 6, 0, false, true },
275  { src4x6, target4x6_0_y_n, 4, 6, 0, true, false },
276  { src4x6, target4x6_0_y_y, 4, 6, 0, true, true },
277
278  { src4x6, target4x6_90_n_n, 4, 6, 90, false, false },
279  { src4x6, target4x6_90_n_y, 4, 6, 90, false, true },
280  { src4x6, target4x6_90_y_n, 4, 6, 90, true, false },
281  { src4x6, target4x6_90_y_y, 4, 6, 90, true, true },
282
283  { src4x6, target4x6_180_n_n, 4, 6, 180, false, false },
284  { src4x6, target4x6_180_n_y, 4, 6, 180, false, true },
285  { src4x6, target4x6_180_y_n, 4, 6, 180, true, false },
286  { src4x6, target4x6_180_y_y, 4, 6, 180, true, true },
287
288  { src4x6, target4x6_270_n_n, 4, 6, 270, false, false },
289  { src4x6, target4x6_270_n_y, 4, 6, 270, false, true },
290  { src4x6, target4x6_270_y_n, 4, 6, 270, true, false },
291  { src4x6, target4x6_270_y_y, 4, 6, 270, true, true }
292};
293
294}  // namespace
295
296class VideoUtilRotationTest
297    : public testing::TestWithParam<VideoRotationTestData> {
298 public:
299  VideoUtilRotationTest() {
300    dest_.reset(new uint8[GetParam().width * GetParam().height]);
301  }
302
303  virtual ~VideoUtilRotationTest() {}
304
305  uint8* dest_plane() { return dest_.get(); }
306
307 private:
308  scoped_ptr<uint8[]> dest_;
309
310  DISALLOW_COPY_AND_ASSIGN(VideoUtilRotationTest);
311};
312
313TEST_P(VideoUtilRotationTest, Rotate) {
314  int rotation = GetParam().rotation;
315  EXPECT_TRUE((rotation >= 0) && (rotation < 360) && (rotation % 90 == 0));
316
317  int size = GetParam().width * GetParam().height;
318  uint8* dest = dest_plane();
319  memset(dest, 255, size);
320
321  RotatePlaneByPixels(GetParam().src, dest, GetParam().width,
322                      GetParam().height, rotation,
323                      GetParam().flip_vert, GetParam().flip_horiz);
324
325  EXPECT_EQ(memcmp(dest, GetParam().target, size), 0);
326}
327
328INSTANTIATE_TEST_CASE_P(, VideoUtilRotationTest,
329                        testing::ValuesIn(kVideoRotationTestData));
330
331TEST_F(VideoUtilTest, ComputeLetterboxRegion) {
332  EXPECT_EQ(gfx::Rect(167, 0, 666, 500),
333            ComputeLetterboxRegion(gfx::Rect(0, 0, 1000, 500),
334                                   gfx::Size(640, 480)));
335  EXPECT_EQ(gfx::Rect(0, 312, 500, 375),
336            ComputeLetterboxRegion(gfx::Rect(0, 0, 500, 1000),
337                                   gfx::Size(640, 480)));
338  EXPECT_EQ(gfx::Rect(56, 0, 888, 500),
339            ComputeLetterboxRegion(gfx::Rect(0, 0, 1000, 500),
340                                   gfx::Size(1920, 1080)));
341  EXPECT_EQ(gfx::Rect(0, 12, 100, 75),
342            ComputeLetterboxRegion(gfx::Rect(0, 0, 100, 100),
343                                   gfx::Size(400, 300)));
344  EXPECT_EQ(gfx::Rect(0, 250000000, 2000000000, 1500000000),
345            ComputeLetterboxRegion(gfx::Rect(0, 0, 2000000000, 2000000000),
346                                   gfx::Size(40000, 30000)));
347  EXPECT_TRUE(ComputeLetterboxRegion(gfx::Rect(0, 0, 2000000000, 2000000000),
348                                     gfx::Size(0, 0)).IsEmpty());
349}
350
351TEST_F(VideoUtilTest, LetterboxYUV) {
352  int width = 40;
353  int height = 30;
354  gfx::Size size(width, height);
355  scoped_refptr<VideoFrame> frame(
356      VideoFrame::CreateFrame(VideoFrame::YV12, size, gfx::Rect(size), size,
357                              base::TimeDelta()));
358
359  for (int left_margin = 0; left_margin <= 10; left_margin += 10) {
360    for (int right_margin = 0; right_margin <= 10; right_margin += 10) {
361      for (int top_margin = 0; top_margin <= 10; top_margin += 10) {
362        for (int bottom_margin = 0; bottom_margin <= 10; bottom_margin += 10) {
363          gfx::Rect view_area(left_margin, top_margin,
364                              width - left_margin - right_margin,
365                              height - top_margin - bottom_margin);
366          FillYUV(frame.get(), 0x1, 0x2, 0x3);
367          LetterboxYUV(frame.get(), view_area);
368          for (int x = 0; x < width; x++) {
369            for (int y = 0; y < height; y++) {
370              bool inside = x >= view_area.x() &&
371                  x < view_area.x() + view_area.width() &&
372                  y >= view_area.y() &&
373                  y < view_area.y() + view_area.height();
374              EXPECT_EQ(frame->data(VideoFrame::kYPlane)[
375                  y * frame->stride(VideoFrame::kYPlane) + x],
376                        inside ? 0x01 : 0x00);
377              EXPECT_EQ(frame->data(VideoFrame::kUPlane)[
378                  (y / 2) * frame->stride(VideoFrame::kUPlane) + (x / 2)],
379                        inside ? 0x02 : 0x80);
380              EXPECT_EQ(frame->data(VideoFrame::kVPlane)[
381                  (y / 2) * frame->stride(VideoFrame::kVPlane) + (x / 2)],
382                        inside ? 0x03 : 0x80);
383            }
384          }
385        }
386      }
387    }
388  }
389}
390
391}  // namespace media
392