1// Copyright 2020 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/capture/video/mac/pixel_buffer_transferer_mac.h"
6
7#include <cmath>
8#include <vector>
9
10#include "base/logging.h"
11#include "media/capture/video/mac/pixel_buffer_pool_mac.h"
12#include "media/capture/video/mac/test/pixel_buffer_test_utils_mac.h"
13#include "media/capture/video/mac/video_capture_device_avfoundation_utils_mac.h"
14#include "testing/gmock/include/gmock/gmock.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace media {
18
19namespace {
20
21constexpr uint8_t kColorR = 255u;
22constexpr uint8_t kColorG = 127u;
23constexpr uint8_t kColorB = 63u;
24
25// Common pixel formats that we want to test. This is partially based on
26// VideoCaptureDeviceAVFoundation::FourCCToChromiumPixelFormat but we do not
27// include MJPEG because compressed formats are not supported by the
28// PixelBufferPool. In addition to the formats supported for capturing, we also
29// test I420, which all captured formats are normally converted to in software
30// making it a sensible destination format.
31
32// media::PIXEL_FORMAT_NV12 a.k.a. "420v"
33constexpr OSType kPixelFormatNv12 =
34    kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
35// media::PIXEL_FORMAT_UYVY a.k.a. "2vuy"
36constexpr OSType kPixelFormatUyvy = kCVPixelFormatType_422YpCbCr8;
37// media::PIXEL_FORMAT_YUY2 a.k.a. "yuvs"
38constexpr OSType kPixelFormatYuvs = kCVPixelFormatType_422YpCbCr8_yuvs;
39// media::PIXEL_FORMAT_I420 a.k.a. "y420"
40constexpr OSType kPixelFormatI420 = kCVPixelFormatType_420YpCbCr8Planar;
41
42}  // namespace
43
44TEST(PixelBufferTransfererTest, CanCopyYuvsAndVerifyColor) {
45  constexpr OSType kPixelFormat = kPixelFormatYuvs;
46  constexpr int kWidth = 32;
47  constexpr int kHeight = 32;
48  PixelBufferTransferer transferer;
49  // Source: A single colored buffer.
50  std::unique_ptr<ByteArrayPixelBuffer> source =
51      CreateYuvsPixelBufferFromSingleRgbColor(kWidth, kHeight, kColorR, kColorG,
52                                              kColorB);
53  // Destination buffer: A same-sized YUVS buffer.
54  base::ScopedCFTypeRef<CVPixelBufferRef> destination =
55      PixelBufferPool::Create(kPixelFormat, kWidth, kHeight, 1)->CreateBuffer();
56  EXPECT_TRUE(transferer.TransferImage(source->pixel_buffer, destination));
57  // Verify the result is the same color.
58  EXPECT_TRUE(YuvsIOSurfaceIsSingleColor(CVPixelBufferGetIOSurface(destination),
59                                         kColorR, kColorG, kColorB));
60}
61
62TEST(PixelBufferTransfererTest, CanScaleYuvsAndVerifyColor) {
63  constexpr OSType kPixelFormat = kPixelFormatYuvs;
64  constexpr int kSourceWidth = 32;
65  constexpr int kSourceHeight = 32;
66  constexpr int kDestinationWidth = 16;
67  constexpr int kDestinationHeight = 16;
68  PixelBufferTransferer transferer;
69  // Source: A single colored buffer.
70  std::unique_ptr<ByteArrayPixelBuffer> source =
71      CreateYuvsPixelBufferFromSingleRgbColor(kSourceWidth, kSourceHeight,
72                                              kColorR, kColorG, kColorB);
73  // Destination buffer: A downscaled YUVS buffer.
74  base::ScopedCFTypeRef<CVPixelBufferRef> destination =
75      PixelBufferPool::Create(kPixelFormat, kDestinationWidth,
76                              kDestinationHeight, 1)
77          ->CreateBuffer();
78  EXPECT_TRUE(transferer.TransferImage(source->pixel_buffer, destination));
79  // Verify the result is the same color.
80  EXPECT_TRUE(YuvsIOSurfaceIsSingleColor(CVPixelBufferGetIOSurface(destination),
81                                         kColorR, kColorG, kColorB));
82}
83
84TEST(PixelBufferTransfererTest, CanScaleYuvsAndVerifyCheckerPattern) {
85  // Note: The ARGB -> YUVS -> ARGB conversions results in a small loss of
86  // information, so for the checker pattern to be intact the buffer can't be
87  // tiny (e.g. 4x4).
88  constexpr int kSourceWidth = 64;
89  constexpr int kSourceHeight = 64;
90  constexpr int kSourceNumTilesAcross = 4;
91  constexpr int kDestinationWidth = 32;
92  constexpr int kDestinationHeight = 32;
93  PixelBufferTransferer transferer;
94  // Source: A single colored buffer.
95  std::unique_ptr<ByteArrayPixelBuffer> source =
96      CreateYuvsPixelBufferFromArgbBuffer(
97          kSourceWidth, kSourceHeight,
98          CreateArgbCheckerPatternBuffer(kSourceWidth, kSourceHeight,
99                                         kSourceNumTilesAcross));
100  // Destination buffer: A downscaled YUVS buffer.
101  base::ScopedCFTypeRef<CVPixelBufferRef> destination =
102      PixelBufferPool::Create(kPixelFormatYuvs, kDestinationWidth,
103                              kDestinationHeight, 1)
104          ->CreateBuffer();
105  EXPECT_TRUE(transferer.TransferImage(source->pixel_buffer, destination));
106  // Verify the result has the same number of checker tiles.
107  int num_tiles_across_x;
108  int num_tiles_across_y;
109  std::tie(num_tiles_across_x, num_tiles_across_y) =
110      GetCheckerPatternNumTilesAccross(
111          CreateArgbBufferFromYuvsIOSurface(
112              CVPixelBufferGetIOSurface(destination)),
113          kDestinationWidth, kDestinationHeight);
114  EXPECT_EQ(num_tiles_across_x, kSourceNumTilesAcross);
115  EXPECT_EQ(num_tiles_across_y, kSourceNumTilesAcross);
116}
117
118TEST(PixelBufferTransfererTest, CanStretchYuvsAndVerifyCheckerPattern) {
119  // Note: The ARGB -> YUVS -> ARGB conversions results in a small loss of
120  // information, so for the checker pattern to be intact the buffer can't be
121  // tiny (e.g. 4x4).
122  constexpr int kSourceWidth = 64;
123  constexpr int kSourceHeight = 64;
124  constexpr int kSourceNumTilesAcross = 4;
125  constexpr int kDestinationWidth = 48;
126  constexpr int kDestinationHeight = 32;
127  PixelBufferTransferer transferer;
128  // Source: A single colored buffer.
129  std::unique_ptr<ByteArrayPixelBuffer> source =
130      CreateYuvsPixelBufferFromArgbBuffer(
131          kSourceWidth, kSourceHeight,
132          CreateArgbCheckerPatternBuffer(kSourceWidth, kSourceHeight,
133                                         kSourceNumTilesAcross));
134  // Destination buffer: A downscaled YUVS buffer.
135  base::ScopedCFTypeRef<CVPixelBufferRef> destination =
136      PixelBufferPool::Create(kPixelFormatYuvs, kDestinationWidth,
137                              kDestinationHeight, 1)
138          ->CreateBuffer();
139  EXPECT_TRUE(transferer.TransferImage(source->pixel_buffer, destination));
140  // Verify the result has the same number of checker tiles.
141  int num_tiles_across_x;
142  int num_tiles_across_y;
143  std::tie(num_tiles_across_x, num_tiles_across_y) =
144      GetCheckerPatternNumTilesAccross(
145          CreateArgbBufferFromYuvsIOSurface(
146              CVPixelBufferGetIOSurface(destination)),
147          kDestinationWidth, kDestinationHeight);
148  EXPECT_EQ(num_tiles_across_x, kSourceNumTilesAcross);
149  EXPECT_EQ(num_tiles_across_y, kSourceNumTilesAcross);
150}
151
152TEST(PixelBufferTransfererTest, CanStretchYuvsAndVerifyColor) {
153  constexpr OSType kPixelFormat = kPixelFormatYuvs;
154  constexpr int kSourceWidth = 32;
155  constexpr int kSourceHeight = 32;
156  constexpr int kDestinationWidth = 48;  // Aspect ratio does not match source.
157  constexpr int kDestinationHeight = 16;
158  PixelBufferTransferer transferer;
159  // Source: A single colored buffer.
160  std::unique_ptr<ByteArrayPixelBuffer> source =
161      CreateYuvsPixelBufferFromSingleRgbColor(kSourceWidth, kSourceHeight,
162                                              kColorR, kColorG, kColorB);
163  // Destination buffer: A streched YUVS buffer.
164  base::ScopedCFTypeRef<CVPixelBufferRef> destination =
165      PixelBufferPool::Create(kPixelFormat, kDestinationWidth,
166                              kDestinationHeight, 1)
167          ->CreateBuffer();
168  EXPECT_TRUE(transferer.TransferImage(source->pixel_buffer, destination));
169  // Verify the result is the same color.
170  EXPECT_TRUE(YuvsIOSurfaceIsSingleColor(CVPixelBufferGetIOSurface(destination),
171                                         kColorR, kColorG, kColorB));
172}
173
174TEST(PixelBufferTransfererTest, CanConvertAndStretchSimultaneouslyYuvsToNv12) {
175  // Source pixel format: YUVS
176  constexpr int kSourceWidth = 32;
177  constexpr int kSourceHeight = 32;
178  constexpr OSType kDestinationPixelFormat = kPixelFormatNv12;
179  constexpr int kDestinationWidth = 48;  // Aspect ratio does not match source.
180  constexpr int kDestinationHeight = 16;
181  PixelBufferTransferer transferer;
182  // Source: A single colored buffer.
183  std::unique_ptr<ByteArrayPixelBuffer> source =
184      CreateYuvsPixelBufferFromSingleRgbColor(kSourceWidth, kSourceHeight,
185                                              kColorR, kColorG, kColorB);
186  // Destination buffer: A streched NV12 buffer.
187  base::ScopedCFTypeRef<CVPixelBufferRef> destination =
188      PixelBufferPool::Create(kDestinationPixelFormat, kDestinationWidth,
189                              kDestinationHeight, 1)
190          ->CreateBuffer();
191  EXPECT_TRUE(transferer.TransferImage(source->pixel_buffer, destination));
192}
193
194class PixelBufferTransfererParameterizedTest
195    : public ::testing::Test,
196      public ::testing::WithParamInterface<std::tuple<OSType, OSType>> {};
197
198// We do not have the testing utils necessary to create and verify pixel buffers
199// in other formats than YUVS, so in order to test the full conversion matrix of
200// all supported formats X -> Y, this parameterized test performs:
201// YUVS -> X -> Y -> YUVS
202TEST_P(PixelBufferTransfererParameterizedTest,
203       CanConvertFromXToYAndVerifyColor) {
204  OSType pixel_format_from;
205  OSType pixel_format_to;
206  std::tie(pixel_format_from, pixel_format_to) = GetParam();
207  LOG(INFO) << "Running Test: " << MacFourCCToString(pixel_format_from)
208            << " -> " << MacFourCCToString(pixel_format_to);
209
210  constexpr int kWidth = 32;
211  constexpr int kHeight = 32;
212  PixelBufferTransferer transferer;
213  // We always start with YUVS because this is the format that the testing
214  // utilities can convert to/from RGB.
215  std::unique_ptr<ByteArrayPixelBuffer> original_yuvs_buffer =
216      CreateYuvsPixelBufferFromSingleRgbColor(kWidth, kHeight, kColorR, kColorG,
217                                              kColorB);
218  // YUVS -> pixel_format_from
219  base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer_from;
220  if (pixel_format_from == kPixelFormatYuvs) {
221    pixel_buffer_from = original_yuvs_buffer->pixel_buffer;
222  } else {
223    pixel_buffer_from =
224        PixelBufferPool::Create(pixel_format_from, kWidth, kHeight, 1)
225            ->CreateBuffer();
226    transferer.TransferImage(original_yuvs_buffer->pixel_buffer,
227                             pixel_buffer_from);
228  }
229  ASSERT_TRUE(pixel_buffer_from);
230
231  // pixel_format_from -> pixel_format_to
232  base::ScopedCFTypeRef<CVPixelBufferRef> pixel_buffer_to =
233      PixelBufferPool::Create(pixel_format_to, kWidth, kHeight, 1)
234          ->CreateBuffer();
235  EXPECT_TRUE(transferer.TransferImage(pixel_buffer_from, pixel_buffer_to));
236
237  // We always convert back to YUVS because this is the only format that the
238  // testing utilities can convert to/from RGB.
239  base::ScopedCFTypeRef<CVPixelBufferRef> final_yuvs_buffer;
240  // pixel_format_to -> YUVS
241  if (pixel_format_to == kPixelFormatYuvs) {
242    final_yuvs_buffer = pixel_buffer_to;
243  } else {
244    final_yuvs_buffer =
245        PixelBufferPool::Create(kPixelFormatYuvs, kWidth, kHeight, 1)
246            ->CreateBuffer();
247    transferer.TransferImage(pixel_buffer_to, final_yuvs_buffer);
248  }
249  ASSERT_TRUE(final_yuvs_buffer);
250  // Verify that after our "conversion dance" we end up with the same color that
251  // we started with.
252  EXPECT_TRUE(YuvsIOSurfaceIsSingleColor(
253      CVPixelBufferGetIOSurface(final_yuvs_buffer), kColorR, kColorG, kColorB));
254}
255
256INSTANTIATE_TEST_SUITE_P(
257    PixelBufferTransfererTest,
258    PixelBufferTransfererParameterizedTest,
259    ::testing::Combine(::testing::Values(kPixelFormatNv12,
260                                         kPixelFormatUyvy,
261                                         kPixelFormatYuvs,
262                                         kPixelFormatI420),
263                       ::testing::Values(kPixelFormatNv12,
264                                         kPixelFormatUyvy,
265                                         kPixelFormatYuvs,
266                                         kPixelFormatI420)));
267
268}  // namespace media
269