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